#include #include #include #include #include #include #include #include #include #include #include #include "cg3.h" #include "cglib.h" #include "cg3600.h" #include "causali.h" #include "mov.h" #include "pconti.h" #include "rmov.h" #include "saldi.h" /////////////////////////////////////////////////////////// // TList /////////////////////////////////////////////////////////// class TList : public TContainer { enum { MAX_SKIP = 8192 }; struct TList_object : public TObject { TObject* _obj; TList_object* _next; TList_object* _prev; }; TList_object** _skip; long _items, _current; long _step, _last_skip; protected: // @cmember Ritorna un puntatore al primo oggetto del contenitore virtual TObject* first_item( ); // @cmember Ritorna un puntatore all'ultimo oggetto del contenitore virtual TObject* last_item( ); // @cmember Ritorna un puntatore all'oggetto successivo all'oggetto corrente virtual TObject* succ_item( ); // @cmember Ritorna un puntatore all'oggetto che precede l'oggetto corrente virtual TObject* pred_item( ); // @cmember Ritorna il numero di oggetti nel contenitore virtual long objects( ); protected: TList_object* detach_lstobj(long index); TList_object* lstobjptr(long index) const; public: long items() const { return _items; } TObject* objptr(long index) const; TObject& obj(long index) { TObject* o = objptr(index); CHECK(o, "Null list item"); return *o; } const TObject& obj(long index) const { const TObject* o = objptr(index); CHECK(o, "Null list item"); return *o; } void destroy(); long insert(TObject* obj, long pos); long append(TObject* obj, long pos = -1); TObject* detach(long pos); bool remove(long pos); void change_step(long step); // Forza il passo per gli elementi long choose_step(long expected_size); // Sceglie il passo per gli elemnti TList(long expected_size = 128L); virtual ~TList(); }; TList::TList(long expected_size) : _items(0), _current(0) { _skip = new TList_object*[MAX_SKIP+1]; _skip[0] = NULL; choose_step(expected_size); } TList::~TList() { destroy(); delete [] _skip; } long TList::objects( ) { return _items; } TObject* TList::first_item() { return objptr(_current = 0); } TObject* TList::succ_item() { return objptr(++_current); } TObject* TList::pred_item() { return objptr(_current++); } TObject* TList::last_item() { return objptr(_current = _items-1); } void TList::destroy() { TList_object* head = _skip[0]; while (head) { TList_object* next = head->_next; if (head->_obj) delete head->_obj; delete head; head = next; } _items = _current = _last_skip = 0; _skip[0] = NULL; } TList::TList_object* TList::lstobjptr(long index) const { TList_object* lstobj = NULL; const ldiv_t p = ldiv(index, _step); if (p.quot >= 0 && p.quot < _last_skip) { TList_object* lo = _skip[p.quot]; for (long s = p.rem; lo && s > 0; s--) lo = lo->_next; lstobj = lo; } return lstobj; } TObject* TList::objptr(long index) const { const TList_object* lo = lstobjptr(index); return lo ? lo->_obj : NULL; } void TList::change_step(long step) { CHECKD(step > 0 && step <= 16384, "Bad list step ", step); _step = step; _last_skip = 0; step = 0; for (TList_object* lo = _skip[0]; lo; lo = lo->_next, step--) { if (step == 0) { CHECK(_last_skip < MAX_SKIP, "Too many items"); _skip[_last_skip++] = lo; step = _step; } } } long TList::choose_step(long expected_size) { long step = expected_size / MAX_SKIP + 1; if (step < 8) step = 8; change_step(step); return step; } long TList::insert(TObject* obj, long index) { if (index < 0) index = 0; else { if (index > _items) index = _items; } const long pred = index-1; TList_object* newobj = new TList_object; newobj->_obj = obj; if (pred < 0) { newobj->_prev = NULL; newobj->_next = _skip[0]; if (_skip[0]) _skip[0]->_prev = newobj; } else { TList_object* lo = lstobjptr(pred); CHECK(lo, "NULL insertion point"); newobj->_next = lo->_next; newobj->_prev = lo; if (lo->_next) lo->_next->_prev = newobj; lo->_next = newobj; } _items++; ldiv_t p = ldiv(index, _step); if (p.rem == 0 && p.quot < MAX_SKIP) // Cambia il capolista _skip[p.quot] = newobj; for (long i = p.quot + 1; i < _last_skip; i++) _skip[i] = _skip[i]->_prev; // Aggiorna capilista successivi // Siamo andati oltre l'ultimo skip const long s = (_items - 1) / _step + 1; if (s > _last_skip) { if (s > MAX_SKIP) change_step(_step+1); else { if (_last_skip > 0) { TList_object* lo = _skip[_last_skip-1]; while (lo->_next != NULL) lo = lo->_next; _skip[_last_skip++] = lo; _skip[_last_skip] = NULL; } else _last_skip = 1; } } return index; } long TList::append(TObject* obj, long index) { if (index < 0 || index >= _items) index = _items; else index++; return insert(obj, index); } TList::TList_object* TList::detach_lstobj(long index) { TList_object* lo = lstobjptr(index); if (lo) { if (lo->_prev) lo->_prev->_next = lo->_next; if (lo->_next) lo->_next->_prev = lo->_prev; ldiv_t res = ldiv(index, _step); if (res.rem == 0) _skip[res.quot] = lo->_next; long p; for (p = res.quot + 1; p < _last_skip; p++) _skip[p] = _skip[p]->_next; _items--; p = (_items - 1) / _step + 1; if (p < _last_skip) { _last_skip--; _skip[_last_skip] = NULL; } } return lo; } TObject* TList::detach(long index) { TList_object* lo = detach_lstobj(index); TObject* obj; if (lo) { obj = lo->_obj; delete lo; } else obj = NULL; return obj; } bool TList::remove(long index) { TObject* o = detach(index); if (o) delete o; return o != NULL; } /////////////////////////////////////////////////////////// // TBalance /////////////////////////////////////////////////////////// class TBalance : public TObject { TImporto _saldo_ini, _progr_dare, _progr_avere, _saldo_fin; protected: bool find(const TBill& b, int esercizio, TImporto& si, TImporto& da, TImporto& av, TImporto& sf) const; public: void read(int g, int c, long s, int esercizio, bool ignora_movap = FALSE); void read(const TBill& b, int esercizio, bool ignora_movap = FALSE); void reread(); const TImporto& saldo_iniziale() const; const real& progressivo_dare_iniziale() const; const real& progressivo_avere_iniziale() const; real progressivo_dare_finale() const; real progressivo_avere_finale() const; TImporto saldo_finale(bool chiusura = FALSE) const; TImporto saldo_finale_chiusura() const; TBalance(); TBalance(int g, int c, long s, int esercizio, bool ignora_movap = FALSE); TBalance(const TBill& b, int esercizio, bool ignora_movap = FALSE); virtual ~TBalance() { } }; TBalance::TBalance() { } TBalance::TBalance(int g, int c, long s, int esercizio, bool ignora_movap) { read(g, c, s, esercizio, ignora_movap); } TBalance::TBalance(const TBill& b, int esercizio, bool ignora_movap) { read(b, esercizio, ignora_movap); } bool TBalance::find(const TBill& b, int esercizio, TImporto& si, TImporto& da, TImporto& av, TImporto& sf) const { CHECK(b.sottoconto() > 0L, "Sottoconto mancante"); TLocalisamfile saldi(LF_SALDI); TRectype& curr = saldi.curr(); curr.put(SLD_GRUPPO, b.gruppo()); curr.put(SLD_CONTO, b.conto()); curr.put(SLD_SOTTOCONTO, b.sottoconto()); curr.put(SLD_ANNOES, esercizio); curr.put(SLD_FLSCA, FALSE); const bool ok = saldi.read() == NOERR; if (ok) { si.set(curr.get_char(SLD_FLAGSALINI), curr.get_real(SLD_SALDO)); da.set('D', curr.get_real(SLD_PDARE)); av.set('A', curr.get_real(SLD_PAVERE)); sf.set(curr.get_char(SLD_FLAGSALFIN), curr.get_real(SLD_SALDOFIN)); } else { si.set('D', ZERO); da = av = sf = si; } return ok; } void TBalance::read(int gruppo, int conto, long sottoconto, int esercizio, bool ignora_movap) { const TBill zio(gruppo, conto, sottoconto); read(zio, esercizio, ignora_movap); } void TBalance::read(const TBill& b, int esercizio, bool ignora_movap) { TImporto si, sf, pd, pa; find(b, esercizio, si, pd, pa, sf); _saldo_ini = si; _progr_dare = pd; _progr_avere = pa; _saldo_fin = sf; if (_saldo_ini.is_zero()) { TLocalisamfile pcon(LF_PCON); TRectype& curr = pcon.curr(); curr.put(PCN_GRUPPO, b.gruppo()); curr.put(PCN_CONTO, b.conto()); const int err = pcon.read(); CHECK(err == NOERR, "Impossibile stabilire indicatore di bilancio"); const int indbil = curr.get_int(PCN_INDBIL); if (indbil == 1 || indbil == 2 || indbil == 5) { TEsercizi_contabili esercizi; const int precedente = esercizi.pred(esercizio); if (precedente > 0 && find(b, precedente, si, pd, pa, sf)) { _saldo_ini = si; _saldo_ini += pd; _saldo_ini += pa; } } } else { if (ignora_movap) _saldo_ini.set('D', ZERO); } } const TImporto& TBalance::saldo_iniziale() const { return _saldo_ini; } const real& TBalance::progressivo_dare_iniziale() const { return _saldo_ini.sezione() == 'D' ? _saldo_ini.valore() : ZERO; } const real& TBalance::progressivo_avere_iniziale() const { return _saldo_ini.sezione() == 'A' ? _saldo_ini.valore() : ZERO; } real TBalance::progressivo_dare_finale() const { real pd = progressivo_dare_iniziale(); pd += _progr_dare.valore(); if (_saldo_fin.sezione() == 'D') pd += _saldo_fin.valore(); return pd; } real TBalance::progressivo_avere_finale() const { real pa = progressivo_avere_iniziale(); pa += _progr_avere.valore(); if (_saldo_fin.sezione() == 'A') pa += _saldo_fin.valore(); return pa; } TImporto TBalance::saldo_finale(bool chiusura) const { TImporto sf(_saldo_ini); sf += _progr_dare; sf += _progr_avere; if (chiusura) sf += _saldo_fin; return sf; } TImporto TBalance::saldo_finale_chiusura() const { return saldo_finale(TRUE); } /////////////////////////////////////////////////////////// // TMastrino /////////////////////////////////////////////////////////// enum tipo_riga_mastrino { riga_mastrino, riga_contropartita }; class TRiga_mastrino : public TObject { tipo_riga_mastrino _type; // Tipo della riga TRecnotype _mov, _rmov; // Numero fisico di record movivento e riga movimento real _dare, _avere; // Progressivi dare ed avere TDate _data; // Data di registrazione (Ottimizzazione) public: tipo_riga_mastrino tipo() const { return _type; } TRecnotype rmov() const { return _rmov; } TRecnotype mov() const { return _mov; } const TDate& data() const { return _data; } const real& dare() const { return _dare; } const real& avere() const { return _avere; } TImporto saldo() const; // Dare-Avere normalizzato TRiga_mastrino(tipo_riga_mastrino trig, TRecnotype rmov, TRecnotype mov, const real& d, const real& a, const TDate& datareg); virtual ~TRiga_mastrino() { } }; TRiga_mastrino::TRiga_mastrino(tipo_riga_mastrino trig, TRecnotype rmov, TRecnotype mov, const real& d, const real& a, const TDate& datareg) : _type(trig), _rmov(rmov), _mov(mov), _dare(d), _avere(a), _data(datareg) { } TImporto TRiga_mastrino::saldo() const { TImporto imp('D', _dare - _avere); imp.normalize(); return imp; } class TMastrino : public TObject { static long _instances; static TRelation* _rel; static TLocalisamfile *_rmov; // File principale della relazione static TLocalisamfile *_mov; // File secondario della relazione TBill _conto; // Conto del mastrino int _esercizio; // Esercizio di riferimento (eventualmente 0) TDate _da_data, _a_data; TString _da_caus, _a_caus; real _pdare_ini, _pavere_ini; real _pdare_per, _pavere_per; real _pdare_fin, _pavere_fin; TList _riga; // Righe del mastrino protected: TRelation& rel() { return *_rel; } TLocalisamfile& rmov() { return *_rmov; } TLocalisamfile& mov() { return *_mov; } void position_rel(long n); TRiga_mastrino& row(long n) const { return (TRiga_mastrino&)_riga.obj(n); } public: long items() const { return _riga.items(); } void read(const TBill& conto, int annoes, const TDate& dd, const TDate& ad, const TString& dc, const TString& ac); void reread(); TRiga_mastrino& operator[](long n) const { return row(n); } const TRectype& riga(long n); const TRectype& testata(long n); long first(tipo_riga_mastrino tipo = riga_mastrino) const; long pred(long rec, tipo_riga_mastrino tipo = riga_mastrino) const; long succ(long rec, tipo_riga_mastrino tipo = riga_mastrino) const; long last(tipo_riga_mastrino tipo = riga_mastrino) const; void destroy() { _riga.destroy(); } const real& progressivo_dare_iniziale() const { return _pdare_ini; } const real& progressivo_avere_iniziale() const { return _pavere_ini; } TImporto saldo_iniziale() const; const real& progressivo_dare_finale() const { return _pdare_fin; } const real& progressivo_avere_finale() const { return _pavere_fin; } TImporto saldo_finale() const; real progressivo_dare_periodo() const { return _pdare_ini + _pdare_per; } real progressivo_avere_periodo() const { return _pavere_ini + _pavere_per; } TImporto saldo_periodo() const; int esercizio() const { return _esercizio; } const TDate& inizio_periodo() const { return _da_data; } const TDate& fine_periodo() const { return _a_data; } void periodo(TDate& dd, TDate& ad) const { dd = _da_data; ad = _a_data; } bool expandable(long rec) const; bool expand(long rec); bool collapse(long rec); TMastrino(); virtual ~TMastrino(); }; long TMastrino::_instances = 0L; TRelation* TMastrino::_rel = NULL; TLocalisamfile* TMastrino::_rmov = NULL; // File principale della relazione TLocalisamfile* TMastrino::_mov = NULL; // File secondario della relazione TMastrino::TMastrino() : _esercizio(0) { if (_instances == 0L) { _rel = new TRelation(LF_RMOV); _rel->add(LF_MOV, "NUMREG=NUMREG"); _rmov = &_rel->lfile(); _mov = &_rel->lfile(LF_MOV); } _instances++; } TMastrino::~TMastrino() { _instances--; if (_instances == 0L) { delete _rel; _rel = NULL; _rmov = _mov = NULL; } } long TMastrino::succ(long rec, tipo_riga_mastrino tipo) const { if (rec < 0) rec = -1; const long ul = items(); for (long i = rec+1; i < ul; i++) { if (row(i).tipo() == tipo) break; } return i; } long TMastrino::pred(long rec, tipo_riga_mastrino tipo) const { if (rec > items()) rec = items(); for (long i = rec-1; i >= 0; i--) { if (row(i).tipo() == tipo) break; } return i; } long TMastrino::first(tipo_riga_mastrino tipo) const { return succ(-1, tipo); } long TMastrino::last(tipo_riga_mastrino tipo) const { return pred(items(), tipo); } void TMastrino::read(const TBill& conto, int ae, const TDate& dd, const TDate& ad, const TString& dc, const TString& ac) { TEsercizi_contabili esercizi; _conto = conto; _esercizio = ae; _riga.destroy(); rmov().setkey(2); TRectype& rmov_rec = rmov().curr(); TRectype& mov_rec = mov().curr(); if (ae <= 0) { if (dd.ok()) ae = esercizi.date2esc(dd); else ae = esercizi.date2esc(ad); } CHECKD(esercizi.exist(ae), "Anno di esercizio fantasioso: ", ae); const TDate& inizio_esercizio = esercizi[ae].inizio(); _da_data = dd.ok() ? dd : inizio_esercizio; _a_data = ad.ok() ? ad : esercizi[ae].fine(); const bool test_caus = !(dc.blank() && ac.blank()); _da_caus = dc; _a_caus = ac.blank() ? "zzz" : ac; // Se vuota sceglie la massima causale TDate max_data_reg = _a_data; long num_giorni = _a_data - inizio_esercizio + 1; if (_esercizio > 0) { const int succ = esercizi.next(ae); if (succ > 0) { max_data_reg = esercizi[succ].fine(); num_giorni += 30; } else max_data_reg = esercizi[ae].fine(); } TString caption(80); caption.format("Caricamento mastrino %03d.%03d.%06ld", _conto.gruppo(), _conto.conto(), _conto.sottoconto()); TProgind pi(num_giorni, caption, FALSE, TRUE, 48); // Stima dimensione mastrino _riga.choose_step(num_giorni); // Valori dei saldi fino alla data di inizio stampa: // Vengono inizializzati con i saldi iniziali dell'esercizio, // poi verranno sommati gli importi dei movimenti che // vanno dall'inizio dell'esercizio al giorno precedente // la data di inizio stampa TBalance saldo(_conto, ae, TRUE); _pdare_ini = saldo.progressivo_dare_iniziale(); _pavere_ini = saldo.progressivo_avere_iniziale(); // Valori dei saldi finali: // Comprendono i movimenti di apertura, chiusura ed i progressivi attuali saldo.read(_conto, ae, FALSE); _pdare_fin = saldo.progressivo_dare_finale(); _pavere_fin = saldo.progressivo_avere_finale(); // Valori dei saldi del perido in esame: // Vengono inizializzati a zero e poi si incrementa man mano // coi valori degli importi dei movimenti compresi nei // limiti della stampa _pdare_per = _pavere_per = ZERO; rmov_rec.zero(); conto.put(rmov_rec); rmov_rec.put(RMV_DATAREG, inizio_esercizio); const TRecfield rmov_datareg (rmov_rec, RMV_DATAREG); const TRecfield rmov_numreg (rmov_rec, RMV_NUMREG); const TRecfield rmov_gruppo (rmov_rec, RMV_GRUPPO); const TRecfield rmov_conto (rmov_rec, RMV_CONTO); const TRecfield rmov_sottoconto(rmov_rec, RMV_SOTTOCONTO); const TRecfield rmov_sezione (rmov_rec, RMV_SEZIONE); const TRecfield rmov_importo (rmov_rec, RMV_IMPORTO); const TRecfield mov_datacomp (mov_rec, MOV_DATACOMP); const TRecfield mov_provvis (mov_rec, MOV_PROVVIS); const TRecfield mov_codcaus (mov_rec, MOV_CODCAUS); #ifdef DBG long num_rec = 0; const clock_t clock_start = clock(); #endif TDate ultimo_giorno; // memorizza ultima data registrazione utilizzata for (int err = rel().read(_isgteq); err == NOERR; err = rel().next()) { // Controlla di non aver superato la data limite const TDate data_reg((TDate)rmov_datareg); if (data_reg > max_data_reg) break; // Controlla che il conto sia ancora quello selezionato if (_conto.sottoconto() != long(rmov_sottoconto) || _conto.conto() != int(rmov_conto) || _conto.gruppo() != int(rmov_gruppo)) break; #ifdef DBG num_rec++; if ((num_rec & 0x7F) == 0) { const double sec = (clock() - clock_start) / CLOCKS_PER_SEC; if (sec > 0.0) { TString80 msg; msg.format("%ld records at %ld rec/sec", num_rec, long(num_rec/sec)); pi.set_text(msg); } } #endif // Ignora i movimenti provvisori const char provvis = *(const char*)mov_provvis; if (provvis > ' ') continue; const TDate data_corrente = _esercizio <= 0 ? data_reg : (TDate)mov_datacomp; if (data_corrente > _a_data) continue; // Aggiorna la barra solo una volta al giorno if (data_reg > ultimo_giorno) { const long giorno = data_reg - inizio_esercizio + 1; pi.setstatus(giorno); ultimo_giorno = data_reg; } const char sezione = *((const char*)rmov_sezione); const real importo((const char*)rmov_importo); if (data_corrente < _da_data) { if (data_corrente >= inizio_esercizio) { if (sezione == 'D') _pdare_ini += importo; else _pavere_ini += importo; } } else { if (sezione == 'D') _pdare_per += importo; else _pavere_per += importo; // Controlla che la causale sia nei limiti if (test_caus) { const bool ok = _da_caus <= mov_codcaus && _a_caus >= mov_codcaus; if (!ok) continue; } TRiga_mastrino* r = new TRiga_mastrino(riga_mastrino, rmov().recno(), mov().recno(), _pdare_per, _pavere_per, data_reg); _riga.append(r); } } pi.setstatus(num_giorni); // Raramente arriva in fondo! } void TMastrino::reread() { read(_conto, _esercizio, _da_data, _a_data, _da_caus, _a_caus); } void TMastrino::position_rel(long n) { const TRiga_mastrino& r = row(n); if (rmov().recno() != r.rmov()) rmov().readat(r.rmov()); if (mov().recno() != r.mov()) mov().readat(r.mov()); } const TRectype& TMastrino::riga(long n) { position_rel(n); return rmov().curr(); } const TRectype& TMastrino::testata(long n) { position_rel(n); return mov().curr(); } TImporto TMastrino::saldo_iniziale() const { TImporto s('D', _pdare_ini - _pavere_ini); return s.normalize(); } TImporto TMastrino::saldo_finale() const { TImporto s('D', _pdare_fin - _pavere_fin); return s.normalize(); } TImporto TMastrino::saldo_periodo() const { TImporto s('D', progressivo_dare_periodo() - progressivo_avere_periodo()); return s.normalize(); } bool TMastrino::expandable(long rec) const { bool e = FALSE; if (row(rec).tipo() == riga_mastrino) { if (rec < items()-1) e = row(rec+1).tipo() != riga_contropartita; else e = TRUE; } return e; } // Genera le righe di contropartita di una riga del mastrino bool TMastrino::expand(long rec) { bool ok = expandable(rec); if (ok) { const TRectype& head = testata(rec); // Testata movimento const long numreg = head.get_long(RMV_NUMREG); // Numero di registrazione const int numrig = riga(rec).get_int(RMV_NUMRIG); // Numero riga contabile const TDate datareg = head.get(MOV_DATAREG); // Data di registrazione rmov().setkey(1); // Usa chiave NUMREG+NUMRIG TRectype& curr = rmov().curr(); // Record corrente const TRecfield rnumreg (curr, RMV_NUMREG); // Numero di registrazione corrente const TRecfield rnumrig (curr, RMV_NUMRIG); // Numero di riga corrente const TRecfield rsezione(curr, RMV_SEZIONE); // Sezione Dare/Avere const TRecfield rimporto(curr, RMV_IMPORTO); // Importo della riga int err = NOERR; if (numrig != 1) // Se non e' gia' posizionato grazie a riga(rec) { curr.zero(); // Azzera record corrente curr.put(RMV_NUMREG, numreg); // Inizializza la chiave parziale err = rmov().read(_isgteq); // Cerca la prima riga del movimento } for (; err == NOERR; err = rmov().next()) // Scandisce righe movimento { if (numreg != (long)rnumreg) // Controlla validita' numero break; if (numrig != (int)rnumrig) // Ignora la riga gia' presente { real dare, avere; // Costruisce importo della riga if (*(const char*)rsezione == 'D') dare = rimporto; else avere = rimporto; // Aggiunge una riga di contropartita al mastrino TRiga_mastrino* r = new TRiga_mastrino(riga_contropartita, rmov().recno(), mov().recno(), dare, avere, datareg); _riga.append(r, rec++); } } } return ok; } // Elimina le righe di contropartita di una riga del mastrino bool TMastrino::collapse(long rec) { bool ok = TRUE; // Posso eliminare? if (row(rec).tipo() != riga_mastrino) // Se non sono su una riga mastrino ... rec = pred(rec, riga_mastrino); // ... mi sposto sulla precedente else ok = !expandable(rec); // Controlla che sia possibile if (ok) // Posso effetivamente procedere { rec++; // Elimino ogni riga contropartita successiva while (rec < items() && row(rec).tipo() != riga_mastrino) _riga.remove(rec); } return ok; } /////////////////////////////////////////////////////////// // TGrid_control /////////////////////////////////////////////////////////// class TGrid_control; class TGrid_cell : public TFixed_string { XI_EVENT* _xiev; public: TString& set(const char* txt); TString& set(long num); void set_icon(int id); void show_button(bool on = TRUE); void hide_button() { show_button(FALSE); } void set_back_color(COLOR col); void set_fore_color(COLOR col); void set_colors(COLOR back, COLOR fore); short get_column() const { return _xiev->v.cell_request.col_nbr; } TString& operator = (const char* str) { return set(str); } TString& operator = (const TString& str) { return set(str); } TGrid_cell(XI_EVENT* xiev); virtual ~TGrid_cell() { } }; class TGrid_field : public TOperable_field { protected: // TMask_field virtual void create(WINDOW parent); virtual void parse_head(TScanner& scanner); virtual bool parse_item(TScanner& scanner); virtual word class_id() const; TGrid_control& grid() const { return (TGrid_control&)*_ctl; } public: virtual bool handler(XI_EVENT* xiev); virtual long items() const; virtual void cell_request(long rec, short id, TGrid_cell& cell); virtual bool on_record(long rec) { return TRUE; } virtual bool off_record(long rec) { return TRUE; } virtual bool on_resize_column(short cid, int new_size) { return TRUE; } virtual void on_dbl_cell(long rec, short id) { } virtual void on_grid_button() { } virtual void on_record_button(long rec) { } virtual void on_cell_button(long rec, short cid) { } long selected() const; void update(long n = -1); int visible_rows() const; bool select(long rec); void reset_columns_order(); void save_columns_order() const; TGrid_field(TMask* m); virtual ~TGrid_field() { } }; class TGrid_control : public TControl { enum grid_control_constants { MAX_COL = 128 }; long _cur_rec; bool _read_only; // @cmember:(INTERNAL) Tipo di ogni colonna byte _type[MAX_COL]; TGrid_field* _grid; int _default_width[MAX_COL]; int _columns_order; protected: // TControl //@cmember Gestisce gli eventi delle celle virtual bool event_handler(XI_OBJ* itf, XI_EVENT* xiev); //@cmember Chiama gli handlers opportuni per verificare il cambio record bool try_to_select(long rec) const; protected: //@cmember Ritorna il numero totale di righe long items() const { return _grid->items(); } //@cmember Converte un record nella eventuale riga corrispondente a video int rec2row(long rec) const; //@cmember Converte una riga a video nell'eventuale record corrispondente long row2rec(int row) const; //@cmember Converte un indice di colonna nel corrispondente id short int col2cid(int pos) const; void update_selection(XI_EVENT* xiev); void set_columns_order(TToken_string* order); XI_OBJ* find_column(short cid) const; XI_OBJ* find_column(const char* head) const; public: long selected() const { return _cur_rec; } bool select(long n); int visible_rows() const; byte& column_type(int c) { CHECKD(c >= 0 && c < MAX_COL, "Bad column ", c); return _type[c]; } void update(long n = -1); bool is_visible(long rec) const; void load_columns_order(); void save_columns_order() const; void reset_columns_order() { set_columns_order(NULL); } TGrid_control(WINDOW parent, short cid, short x, short y, short dx, short dy, const char* flags, const char* head, TGrid_field* owner); virtual ~TGrid_control() {} }; TGrid_control::TGrid_control( WINDOW parent, // @parm Finestra alla quale appartiene lo spreadsheet short cid, // @parm Identificatore short x, // @parm Coordinata x (in caratteri) nel quale posizionare lo spreadsheet short y, // @parm Coordinata y (in caratteri) nel quale posizionare lo spreadsheet short dx, // @parm Larghezza (in caratteri) dello spreasheet short dy, // @parm Lunghezza (in caratteri) dello spreasheet const char* flags, // @parm Flags di abilitazione const char* head, // @parm Titolo delle colonne TGrid_field* owner) : _grid(owner), _cur_rec(-1), _columns_order(0) { _read_only = FALSE; bool auto_num = FALSE; bool multi_line = FALSE; int lines_in_cell = 1; for (const char* f = flags; *f; f++) { switch(*f) { case 'A': auto_num = TRUE; break; case 'D': _read_only = TRUE; break; case 'M': multi_line = TRUE; lines_in_cell = (int)xi_get_pref(XI_PREF_DEFAULT_MAX_LINES_IN_CELL); break; case '2': case '3': case '4': case '5': if (multi_line) lines_in_cell = *f - '0'; break; default: break; } } const int NUMBER_WIDTH = auto_num ? 7 : 1; short v_width[MAX_COL]; short m_width[MAX_COL]; int fixed_columns = 1; // Number of fixed columns int lines_in_header = 1; // Number of header lines // Calcolo larghezza massima tabella TToken_string header(head); TToken_string new_header(256); int i = 0; int f_width = NUMBER_WIDTH; // Stima larghezza colonne fisse int max_width = f_width; // Stima larghezza della colonna piu' grande for (const char* h = header.get(); h; h = header.get(), i++) { CHECKD(i < MAX_COL, "Tu meni calumns in scit: ", i); _type[i] = ' '; TFixed_string testa(esc(h)); const bool multiple = testa.find('\n') > 0; if (multiple) lines_in_header = 2; const int at = testa.find('@'); int v = testa.len(); // Video width if (at >= 0) { const TString& wi = testa.mid(at+1); const int video = atoi(wi); if (video > 0) v = video; if (wi.find('F') >= 0) { fixed_columns = i+2; f_width += v+1; } if (wi.find('R') >= 0) _type[i] = 'R'; testa.cut(at); } v++; // memory width of column m_width[i] = v * lines_in_cell; if (v > 64) v = 64; v_width[i] = v; if (v_width[i] > max_width) max_width = v_width[i]; new_header.add(testa); } // Calcola rettangolo massimo per lo sheet XI_RCT rct; coord2rct(parent, x, y, dx, dy, rct); rct.right -= 2*XI_FU_MULTIPLE; // toglie scroll-bar // Controlla se ci sono troppe colonne fisse if ((f_width+max_width)*XI_FU_MULTIPLE > rct.right) fixed_columns = 1; XI_OBJ* itf = get_interface(parent); XI_OBJ_DEF* listdef = xi_add_list_def(NULL, cid, rct.top, rct.left, rct.bottom-rct.top, XI_ATR_ENABLED | XI_ATR_VISIBLE, NORMAL_COLOR, NORMAL_BACK_COLOR, // normal DISABLED_COLOR, DISABLED_BACK_COLOR, // disabled FOCUS_COLOR, // active 0); listdef->app_data = (long)this; XI_LIST_DEF* l = listdef->v.list; l->min_heading_height = xi_button_calc_height_font_id(xvt_default_font()) * lines_in_header; l->sizable_columns = TRUE; l->movable_columns = TRUE; l->fixed_columns = fixed_columns; l->max_lines_in_cell = lines_in_cell; l->scroll_bar = TRUE; l->scroll_bar_button = TRUE; l->white_space_color = MASK_DARK_COLOR; l->rule_color = MASK_DARK_COLOR; if (_read_only) l->single_select = TRUE; else l->active_back_color = FOCUS_BACK_COLOR; // Definizione della prima colonna (numero di riga) const long attr = XI_ATR_VISIBLE | XI_ATR_RJUST | XI_ATR_SELECTABLE; XI_OBJ_DEF* coldef = xi_add_column_def(listdef, FIRST_FIELD+1000-1, attr, 0, NUMBER_WIDTH * XI_FU_MULTIPLE, NUMBER_WIDTH , ""); coldef->app_data = (long)this; XI_COLUMN_DEF* cd = coldef->v.column; cd->heading_platform = TRUE; cd->column_platform = TRUE; for (h = new_header.get(0), i = 0; h; h = new_header.get(), i++) { long attr = XI_ATR_VISIBLE | XI_ATR_ENABLED | XI_ATR_AUTOSCROLL; if (_read_only) attr |= XI_ATR_READONLY | XI_ATR_SELECTABLE; if (_type[i] == 'R') attr |= XI_ATR_RJUST; coldef = xi_add_column_def(listdef, FIRST_FIELD+i+1000, attr, i+1, v_width[i] * XI_FU_MULTIPLE, m_width[i], (char*)h); coldef->app_data = (long)this; cd = coldef->v.column; cd->heading_platform = TRUE; cd->center_heading = TRUE; if (multi_line) cd->wrap_text = _type[i] != 'R'; } RCT rd; xi_get_def_rect(listdef, &rd); if ((rd.right - rd.left) > (rct.right - rct.left)) l->width = rct.right - rct.left; _obj = xi_create(itf, listdef); // Create the whole thing! xi_dequeue(); // Flush events in XOL xi_tree_free(listdef); // Free definitions CHECKD(_obj, "Can't create list control ", cid); update_tab_cid(); int num; XI_OBJ** column = xi_get_member_list(_obj, &num); for (i = 0; i < num; i++) { RCT rct; xi_get_rect(column[i], &rct); _default_width[i] = rct.right - rct.left; } } // Converts a record number in the correspondig row number int TGrid_control::rec2row(long record) const { int rows; const long* rec = xi_get_list_info(_obj, &rows); int r = rows > 0 ? int(record - rec[0]) : -1; if (r < 0 || r >= rows) r = -1; return r; } // Converts a row number in the correspondig record number long TGrid_control::row2rec(int row) const { CHECK(row >= 0, "Negative grid row?"); int rows; const long* handle = xi_get_list_info(_obj, &rows); long rec; if (rows > 0) { if (row >= rows) rec = handle[rows-1] + row - rows + 1; else rec = handle[row]; } else rec = -1; if (rec < 0 || rec >= items()) rec = -1; return rec; } int TGrid_control::visible_rows() const { return xi_get_visible_rows(_obj, NULL, NULL); } bool TGrid_control::is_visible(long rec) const { int first, last; xi_get_visible_rows(_obj, &first, &last); int rows; const long* handle = xi_get_list_info(_obj, &rows); bool yes = rec >= handle[first] && rec <= handle[last]; return yes; } void TGrid_control::update(long n) { if (n >= 0) { const int riga = rec2row(n); if (riga >= 0) { XI_OBJ row; XI_MAKE_ROW(&row, _obj, riga); xi_cell_request(&row); } } else { int num = 0; const long* handle = xi_get_list_info(_obj, &num); bool scroll_first = items() == 0; if (!scroll_first) { int first = 0, last = 0; xi_get_visible_rows(_obj, &first, &last); n = handle[first]; scroll_first = n > items(); } if (scroll_first) xi_scroll(_obj, XI_SCROLL_FIRST); else xi_scroll_rec(_obj, n, NORMAL_COLOR, XI_ATR_ENABLED, 0); } } bool TGrid_control::select(long rec) { bool ok, sel; if (rec >= 0) { ok = try_to_select(rec); sel = ok; } else { ok = _cur_rec >= 0 && _cur_rec < items() && _grid->off_record(_cur_rec); sel = FALSE; } if (ok) { if (sel) { int first, last; xi_get_visible_rows(_obj, &first, &last); // Controllo che la nuova riga sia completamente visibile const int next_row = rec2row(rec); if (next_row >= first && next_row <= last) { if (_read_only) { XI_OBJ riga; XI_MAKE_ROW(&riga, _obj, next_row); long attr = xi_get_attrib(&riga); attr |= XI_ATR_SELECTED; xi_set_attrib(&riga, attr); } } else { long attr = XI_ATR_ENABLED; if (_read_only) attr |= XI_ATR_SELECTED; xi_scroll_rec(_obj, rec, NORMAL_COLOR, attr, 0); } if (!_read_only) { const int next_row = rec2row(rec); XI_OBJ cella; XI_MAKE_CELL(&cella, _obj, next_row, 1); xi_set_focus(&cella); } } // end if (sel) // Deseleziona record precedente se ancora visibile if (_read_only) { const int cur_row = rec2row(_cur_rec); if (cur_row >= 0) { XI_OBJ riga; XI_MAKE_ROW(&riga, _obj, cur_row); long attr = xi_get_attrib(&riga); attr &= ~XI_ATR_SELECTED; xi_set_attrib(&riga, attr); } xi_dequeue(); } if (rec < 0 || rec >= items()) rec = -1; _cur_rec = rec; } // end if (ok) return ok; } short TGrid_control::col2cid(int pos) const { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); CHECKD(pos >= 0 && pos < num, "Bad column ", pos); const short cid = column[pos]->cid - 1000; return cid; } bool TGrid_control::try_to_select(long rec) const { bool ok = rec >= 0 && rec < items(); if (ok && rec != _cur_rec) { if (_cur_rec >= 0 && _cur_rec < items()) ok = _grid->off_record(_cur_rec); if (ok) ok = _grid->on_record(rec); } return ok; } void TGrid_control::update_selection(XI_EVENT* xiev) { const bool is_curr = xiev->v.rec_request.data_rec == _cur_rec; if (_read_only) { if (is_curr) xiev->v.rec_request.attrib |= XI_ATR_SELECTED; else xiev->v.rec_request.attrib &= ~XI_ATR_SELECTED; } else xiev->v.rec_request.has_focus = is_curr; } // Certified 75% bool TGrid_control::event_handler(XI_OBJ* itf, XI_EVENT *xiev) { BOOLEAN& refused = xiev->refused; const bool handled = _grid->handler(xiev); if (handled) return !refused; switch (xiev->type) { case XIE_GET_FIRST: if (items() > 0L) { long n = items() * (long)xiev->v.rec_request.percent / 100L; if (n < 0L) n = 0L; xiev->v.rec_request.data_rec = n; update_selection(xiev); } else refused = TRUE; break; case XIE_GET_LAST: xiev->v.rec_request.data_rec = items()-1; update_selection(xiev); break; case XIE_GET_PREV: case XIE_GET_NEXT: { const long n = xiev->v.rec_request.spec_rec + (xiev->type == XIE_GET_NEXT ? +1 : -1) ; if (n >= 0 && n < items()) { xiev->v.rec_request.data_rec = n; update_selection(xiev); } else refused = TRUE; } break; case XIE_GET_PERCENT: { const long rec = xiev->v.get_percent.record; long n = items(); if (n <= 0) n = 1; xiev->v.get_percent.percent = short(rec * 100L / n); } break; case XIE_COL_MOVE: // Rifiuta di spostare una colonna nelle o dalle colonne fisse if (xiev->v.column.in_fixed || xiev->v.column.col_nbr < xi_get_fixed_columns(xiev->v.column.list)) refused = TRUE; else _columns_order = 1; break; case XIE_COL_SIZE: { const short cid = col2cid(xiev->v.column.col_nbr); if (_grid->on_resize_column(cid, xiev->v.column.new_col_width)) _columns_order = 1; else refused = TRUE; } break; case XIE_SELECT: if (xiev->v.select.xi_obj->type == XIT_ROW) // Considero solo le righe { if (xiev->v.select.selected) // Sto selezionando { const long rec = row2rec(xiev->v.select.xi_obj->v.row_data.row); if (try_to_select(rec)) { if (xiev->v.select.column == 0) { _cur_rec = rec; // Assegno subito il record corrente _grid->on_record_button(rec); } else { if (_read_only && rec == _cur_rec) { const short cid = col2cid(xiev->v.select.column); _grid->on_dbl_cell(rec, cid); } _cur_rec = rec; // Assegno solo ora il record corrente } } else refused = TRUE; } } break; case XIE_CELL_REQUEST: { const long& rec = xiev->v.cell_request.rec; if (rec >= 0 && rec < items()) { TGrid_cell cell(xiev); const short cid = col2cid(cell.get_column()); if (cid >= FIRST_FIELD) { _grid->cell_request(rec, cid, cell); } else { if (cell.size() > 2) { cell.set(rec+1); // Setto il colore del testo altrimenti verrebbe grigio: // non uso la set_color perche' ignora NORMAL_COLOR xiev->v.cell_request.color = NORMAL_COLOR; } } } else refused = TRUE; // Ogni tanto succede } break; case XIE_ON_ROW: { // Qui ci passa solo se non e' _read_only const long rec = row2rec(xiev->v.xi_obj->v.row); if (rec >= 0) { if (_grid->on_record(rec)) _cur_rec = rec; else refused = TRUE; } else { NFCHECK("You are entering an invalid row: %d", xiev->v.xi_obj->v.row); refused = TRUE; } } break; case XIE_OFF_ROW: // Qui ci passa solo se non e' _read_only if (_cur_rec >= 0 && _cur_rec < items()) refused = !_grid->off_record(_cur_rec); break; case XIE_ON_CELL: break; case XIE_DBL_CELL: { const long rec = row2rec(xiev->v.xi_obj->v.cell.row); if (try_to_select(rec)) { const short cid = col2cid(xiev->v.xi_obj->v.cell.column); _grid->on_dbl_cell(rec, cid); } } break; case XIE_BUTTON: if (xiev->v.xi_obj->type == XIT_LIST) { _grid->on_grid_button(); } else { const XI_CELL_DATA& cell = xiev->v.xi_obj->v.cell; const long rec = row2rec(cell.row); if (try_to_select(rec)) { const short cid = col2cid(cell.column); _grid->on_cell_button(rec, cid); } else NFCHECK("You are clicking an invalid cell: %d", cell.row); } break; default: break; } return !refused; } XI_OBJ* TGrid_control::find_column(short cid) const { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); for (int i = num-1; i >= 0; i--) { if (column[i]->cid == cid) break; } return i >= 0 ? column[i] : NULL; } XI_OBJ* TGrid_control::find_column(const char* head) const { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); TString80 text; for (int i = num-1; i >= 0; i--) { xi_get_text(column[i], text.get_buffer(), text.size()); if (text == head) break; } return i >= 0 ? column[i] : NULL; } void TGrid_control::set_columns_order(TToken_string* order) { XI_OBJ* itf = get_interface(); XI_OBJ* focus = xi_get_focus(itf); xi_set_focus(itf); int num_cols; XI_OBJ** column = xi_get_member_list(_obj, &num_cols); // Costante da sottrarre nella xi_column_set_pixel_width altrimenti la somma due volte! const int offset = 2 * (int)xi_get_pref(XI_PREF_COLUMN_OFFSET); const int fixed = xi_get_fixed_columns(_obj); if (fixed > 1) xi_set_fixed_columns(_obj, 1); if (order == NULL) { for (int index = 1; index < num_cols; index++) { const short cid = FIRST_FIELD + 1000 + index - 1; XI_OBJ* col = find_column(cid); if (col) { xi_move_column(col, index); RCT rct; xi_get_rect(col, &rct); if (_default_width[index] != rct.right - rct.left) xi_column_set_pixel_width(col, _default_width[index]-offset); } } _columns_order = 0x3; } else { TToken_string col(8, ','); int pos = 0; for (col = order->get(0); !col.blank(); col = order->get(), pos++) { const char* head = esc(col.get(0)); const int width = col.get_int(); XI_OBJ* column = find_column(head); if (column) // Controlla che esista ancora { if (pos > 0 && pos < num_cols) xi_move_column(column, pos); // Sposta la colonna se possibile if (width > XI_FU_MULTIPLE) // Se ha una larghezza valida xi_column_set_pixel_width(column, width - offset); } } } if (fixed > 1) xi_set_fixed_columns(_obj, fixed); if (focus) xi_set_focus(focus); } HIDDEN TFilename& field2parag(const TMask_field& f, TFilename& name) { const TMask& m = f.mask(); name = m.source_file(); name.ext(""); // Nome della maschera senza estensione const int index = m.number(); CHECKD(index >= 0 && index <= 8, "Bad mask index:", index); if (index > 0) // Aggiunge l'eventuale numero di sotto-maschera name << '(' << index << ')'; return name; } void TGrid_control::load_columns_order() { TFilename parag; field2parag(*_grid, parag); TConfig config(CONFIG_USER, parag); TToken_string order = config.get("Browse", NULL, id()); if (order.empty_items()) config.remove("Browse", id()); else set_columns_order(&order); _columns_order = 0; } void TGrid_control::save_columns_order() const { if (_columns_order) { TFilename parag; field2parag(*_grid, parag); TConfig config(CONFIG_USER, parag); // Apre il file di configurazione TToken_string order(127); // Nuovo ordine delle colonne if (_columns_order == 1) // Se vale 3 devo solo resettare { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); TString80 head; for (int i = 0; i < num; i++) // Scorre tutte le colonne { xi_get_text(column[i], head.get_buffer(), head.size()); const int acapo = head.find('\n'); if (acapo > 0) { head[acapo] = '\\'; head.insert("n", acapo+1); } order.add(head); RCT rct; xi_get_rect(column[i], &rct); order << ',' << rct.right - rct.left; } config.set("Browse", order, NULL, TRUE, id()); } else config.remove("Browse", id()); } } /////////////////////////////////////////////////////////// // TGrid_cell /////////////////////////////////////////////////////////// TGrid_cell::TGrid_cell(XI_EVENT* xiev) : TFixed_string(xiev->v.cell_request.s, xiev->v.cell_request.len), _xiev(xiev) { } // Setta il testo di una cella (Mai piu' testo troppo lungo!) // Se c'e' gia' un'icona la elimina TString& TGrid_cell::set(const char* txt) { strncpy(txt, size()); if (not_empty()) { int& icon = _xiev->v.cell_request.icon_rid; if (icon) icon = 0; } return *this; } TString& TGrid_cell::set(long num) { char buff[16]; sprintf(buff, "%ld", num); return set(buff); } // Setta l'icona di una cella // Se c'e' gia' un testo lo elimina void TGrid_cell::set_icon(int id) { _xiev->v.cell_request.icon_rid = id; if (id) _xiev->v.cell_request.s[0] = '\0'; } void TGrid_cell::show_button(bool on) { _xiev->v.cell_request.button = on; _xiev->v.cell_request.button_on_focus = on; } void TGrid_cell::set_back_color(COLOR col) { if (col != NORMAL_BACK_COLOR) _xiev->v.cell_request.back_color = col; } void TGrid_cell::set_fore_color(COLOR col) { if (col != NORMAL_COLOR) _xiev->v.cell_request.color = col; } void TGrid_cell::set_colors(COLOR back, COLOR fore) { if (back != NORMAL_BACK_COLOR) _xiev->v.cell_request.back_color = back; if (fore != NORMAL_COLOR) _xiev->v.cell_request.color = fore; } /////////////////////////////////////////////////////////// // TGrid_field /////////////////////////////////////////////////////////// TGrid_field::TGrid_field(TMask* m) : TOperable_field(m) { } word TGrid_field::class_id() const { return CLASS_GRID_FIELD; } void TGrid_field::update(long n) { grid().update(n); } void TGrid_field::parse_head(TScanner& scanner) { _ctl_data._width = scanner.integer(); _ctl_data._height = scanner.integer(); if (_ctl_data._height == 0) _ctl_data._height = -1; } void TGrid_field::create(WINDOW parent) { _ctl = new TGrid_control(parent, dlg(), _ctl_data._x, _ctl_data._y, _ctl_data._width, _ctl_data._height, _ctl_data._flags, _ctl_data._park, this); grid().load_columns_order(); } bool TGrid_field::parse_item(TScanner& scanner) { if (scanner.key() == "IT") { _ctl_data._park.add(scanner.string()); return TRUE; } return TMask_field::parse_item(scanner); } bool TGrid_field::handler(XI_EVENT* xiev) { return FALSE; } long TGrid_field::items() const { return 100000L; } int TGrid_field::visible_rows() const { return grid().visible_rows(); } void TGrid_field::cell_request(long rec, short id, TGrid_cell& cell) { cell.set("Cell"); } long TGrid_field::selected() const { return grid().selected(); } bool TGrid_field::select(long rec) { return grid().select(rec); } void TGrid_field::reset_columns_order() { grid().reset_columns_order(); } void TGrid_field::save_columns_order() const { grid().save_columns_order(); } /////////////////////////////////////////////////////////// // Da qui in poi e' tutta roba specializzata del programma /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// // Maschere per colori /////////////////////////////////////////////////////////// class TRow_mask : public TMask { public: virtual void update(); TRow_mask(); virtual ~TRow_mask() { } }; class TColor_mask : public TVariable_mask { static TRow_mask* _sheet_mask; static TMask* get_mask(int, TMask&) { return _sheet_mask; } COLOR _mas_back, _mas_fore; COLOR _con_back, _con_fore; protected: static bool color_handler(TMask_field& f, KEY k); void type2colors(char tipo, COLOR& back, COLOR& fore) const; public: void get_cur_colors(COLOR& back, COLOR& fore) const; void set_cur_colors(COLOR back, COLOR fore); void get_colors(COLOR& mb, COLOR& mf, COLOR& cb, COLOR& cf) const; TColor_mask(COLOR mb, COLOR mf, COLOR cb, COLOR cf); virtual ~TColor_mask(); }; /////////////////////////////////////////////////////////// // TRow_mask /////////////////////////////////////////////////////////// TRow_mask* TColor_mask::_sheet_mask = NULL; TRow_mask::TRow_mask() : TMask("cg2100k", 1) { } void TRow_mask::update() { TSheet_field* s = get_sheet(); TColor_mask& m = (TColor_mask&)s->mask(); COLOR back, fore; m.get_cur_colors(back, fore); set_pen(COLOR_BLACK); set_brush(back); frame(2, 4, 16, 6, 0); set_brush(fore); frame(20, 4, 34, 6, 0); } /////////////////////////////////////////////////////////// // TColor_mask /////////////////////////////////////////////////////////// void TColor_mask::type2colors(char tipo, COLOR& back, COLOR& fore) const { if (toupper(tipo) == 'M') { back = _mas_back; fore = _mas_fore; } else { back = _con_back; fore = _con_fore; } } void TColor_mask::get_cur_colors(COLOR& back, COLOR& fore) const { TSheet_field& s = (TSheet_field&)fld(0); TToken_string& row = s.row(s.selected()); const char tipo = row[5]; type2colors(tipo, back, fore); } void TColor_mask::set_cur_colors(COLOR back, COLOR fore) { TSheet_field& s = (TSheet_field&)fld(0); const int cur = s.selected(); TToken_string& row = s.row(cur); const char tipo = toupper(row[5]); if (tipo == 'M') { _mas_back = back; _mas_fore = fore; } else { _con_back = back; _con_fore = fore; } s.set_back_and_fore_color(back, fore, cur); s.force_update(cur); } bool TColor_mask::color_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TMask& m = f.mask(); TColor_mask& cm = (TColor_mask&)m.get_sheet()->mask(); COLOR back, fore; cm.get_cur_colors(back, fore); const bool use_back = f.dlg() == 100; const COLOR col = choose_color(use_back ? back : fore, m.win()); if (col != COLOR_INVALID) { if (use_back) back = col; else fore = col; cm.set_cur_colors(back, fore); cm._sheet_mask->update(); } } return TRUE; } void TColor_mask::get_colors(COLOR& mb, COLOR& mf, COLOR& cb, COLOR& cf) const { mb = _mas_back; mf = _mas_fore; cb = _con_back; cf = _con_fore; } TColor_mask::TColor_mask(COLOR mb, COLOR mf, COLOR cb, COLOR cf) : TVariable_mask("cg2100k"), _mas_back(mb), _mas_fore(mf), _con_back(cb), _con_fore(cf) { CHECK(_sheet_mask == NULL, "One color at time, please"); _sheet_mask = new TRow_mask; _sheet_mask->set_handler(99, color_handler); _sheet_mask->set_handler(100, color_handler); TVariable_sheet_field& s = (TVariable_sheet_field&)fld(0); s.set_getmask(get_mask); const char* const tipi = "MC"; int row = 0; for (const char* c = tipi; *c; c++) { COLOR back, fore; type2colors(*c, back, fore); TToken_string& riga = s.row(-1); riga << "Riga " << (*c == 'M' ? "mastrino" : "contropartita"); s.set_back_and_fore_color(back, fore, row++); } } TColor_mask::~TColor_mask() { delete _sheet_mask; _sheet_mask = NULL; } /////////////////////////////////////////////////////////// // TMastrini_grid /////////////////////////////////////////////////////////// class TMastrini_grid : public TGrid_field { TMastrino _mastrino; TDecoder _causali; TEsercizi_contabili _esercizi; COLOR _mas_back, _mas_fore; COLOR _con_back, _con_fore; protected: // TGrid_field virtual bool on_record(long rec); virtual void cell_request(long rec, short id, TGrid_cell& cell); virtual void on_grid_button(); virtual bool on_resize_column(short id, int new_size); virtual void on_dbl_cell(long rec, short id); void update_mask() const; public: virtual void on_record_button(long rec); virtual long items() const { return _mastrino.items(); } void destroy(); void read(const TBill& conto, int annoes, const TDate& dd, const TDate& ad, const TString& dc, const TString& ac); void reread(); TMastrino& mastrino() { return _mastrino; } void save_colors(); void load_colors(); void set_colors(); TMastrini_grid(TMask* m); virtual ~TMastrini_grid() { } }; TMastrini_grid::TMastrini_grid(TMask* m) : TGrid_field(m), _causali(LF_CAUSALI, CAU_DESCR) { load_colors(); } void TMastrini_grid::destroy() { _mastrino.destroy(); grid().select(-1); } HIDDEN void set_imp(TMask_field& f, const TImporto& imp) { if (!imp.is_zero()) { TString80 str; str = imp.valore().string("."); str << ' ' << imp.sezione(); f.set(str); } else f.reset(); } void TMastrini_grid::cell_request(long rec, short id, TGrid_cell& cell) { const TRiga_mastrino& riga = _mastrino[rec]; switch (id) { case 101: if (riga.tipo() == riga_mastrino) { const TRectype& mov = _mastrino.testata(rec); cell = riga.data().string(); cell << ' ' << mov.get(MOV_DATADOC); } break; case 102: if (riga.tipo() == riga_mastrino) { const TRectype& mov = _mastrino.testata(rec); const int anno = _esercizi.date2esc(riga.data()); const int eser = mov.get_int(MOV_ANNOES); const comp = anno != eser; cell.set_icon(comp ? ICO_CHECK_ON : ICO_CHECK_OFF); } break; case 103: if (riga.tipo() == riga_mastrino) { const TRectype& mov = _mastrino.testata(rec); cell.set(_causali.decode(mov.get(MOV_CODCAUS))); } else { const TRectype& rmov = _mastrino.riga(rec); cell.format("%03d.%03d.%06ld", rmov.get_int(RMV_GRUPPO), rmov.get_int(RMV_CONTO), rmov.get_long(RMV_SOTTOCONTO)); } break; case 104: if (riga.tipo() == riga_mastrino) { const TRectype& mov = _mastrino.testata(rec); cell.set(mov.get(MOV_DESCR)); if (cell.empty()) { const TRectype& rmov = _mastrino.riga(rec); cell.set(rmov.get(RMV_DESCR)); if (cell.empty()) { TBill uncle(rmov,TRUE); cell.set(uncle.descrizione()); } } } else { const TRectype& rmov = _mastrino.riga(rec); cell.set(rmov.get(RMV_DESCR)); if (cell.empty()) { const TBill conto(rmov); cell.set(conto.descrizione()); } } break; case 105: { const TRectype& rmov = _mastrino.riga(rec); if (rmov.get_char(RMV_SEZIONE) == 'D') cell = rmov.get_real(RMV_IMPORTO).string("."); } break; case 106: { const TRectype& rmov = _mastrino.riga(rec); if (rmov.get_char(RMV_SEZIONE) == 'A') cell = rmov.get_real(RMV_IMPORTO).string("."); } break; case 107: if (riga.tipo() == riga_mastrino) { const TRectype& mov = _mastrino.testata(rec); cell = mov.get(MOV_NUMDOC); cell.left_just(7); cell << ' ' << mov.get(MOV_PROTIVA); } break; case 108: { const TRectype& rmov = _mastrino.riga(rec); cell = rmov.get_real(RMV_IMPORTO).string("."); cell << ' ' << rmov.get(RMV_SEZIONE); } break; case 109: if (riga.tipo() == riga_mastrino) { const long next_row = _mastrino.succ(rec, riga_mastrino); bool stampa = next_row >= _mastrino.items(); if (!stampa) { const TDate& data = _mastrino[next_row].data(); stampa = riga.data() != data; } if (stampa) { TImporto imp = riga.saldo(); imp += _mastrino.saldo_iniziale(); imp.normalize(); cell = imp.valore().string("."); cell << ' ' << imp.sezione(); } } break; default: break; } if (riga.tipo() == riga_mastrino) cell.set_colors(_mas_back, _mas_fore); else cell.set_colors(_con_back, _con_fore); } bool TMastrini_grid::on_record(long rec) { if (_mastrino[rec].tipo() != riga_mastrino) rec = _mastrino.pred(rec, riga_mastrino); TRiga_mastrino& riga = _mastrino[rec]; TMask& gm = mask(); set_imp(gm.field(F_TOTRIG_SAL), riga.saldo()); gm.set(F_TOTRIG_DAR, riga.dare()); gm.set(F_TOTRIG_AVE, riga.avere()); return TRUE; } void TMastrini_grid::on_dbl_cell(long rec, short id) { if (_mastrino.expandable(rec)) _mastrino.expand(rec); else _mastrino.collapse(rec); update(); } void TMastrini_grid::on_grid_button() { const long total = _mastrino.items(); if (total > 0) { TProgind* pi = NULL; if (total > 50) pi = new TProgind(total, "Aggiornamento contropartite ...", FALSE, TRUE, 48); else begin_wait(); // Cerca l'ultima contropartita const long last_con = _mastrino.last(riga_contropartita); // Se non esistono contropartite devo espandere le righe const bool expand = last_con < 0; #ifdef DBG const clock_t clock_start = clock(); #endif if (expand) { long step = 0; for (long n = _mastrino.first(riga_mastrino); n < _mastrino.items(); n = _mastrino.succ(n, riga_mastrino)) { if (_mastrino.expandable(n)) _mastrino.expand(n); if (pi) { pi->setstatus(++step); #ifdef DBG if ((step & 0x7F) == 0) { const double sec = (clock() - clock_start) / CLOCKS_PER_SEC; if (sec > 0.0) { TString80 msg; msg.format("%ld records at %ld rec/sec", step, long(step/sec)); pi->set_text(msg); } } #endif } } } else { for (long n = last_con; n > 0; n = _mastrino.pred(n, riga_contropartita)) { _mastrino.collapse(n); if (pi) pi->setstatus(total - n + 1); } } if (pi) delete pi; else end_wait(); update(); } } void TMastrini_grid::on_record_button(long rec) { begin_wait(); const TRectype& testata = _mastrino.testata(rec); TString text; text << "1|" << testata.get(MOV_NUMREG); const char* const appname = "cg2 -0"; TMessage pn(appname, MSG_LN, text); pn.send(); TExternal_app a(appname); a.run(); TMailbox mail; if (mail.next_s(MSG_LN) != NULL) { if (yesno_box("Si desidera aggiornare il mastrino?")) reread(); } end_wait(); } // Posso ridimensionare solo le descrizioni, le altre devono rimanere fisse per // non perdere la formattazione su due righe bool TMastrini_grid::on_resize_column(short cid, int new_size) { return cid == 103 || cid == 104; } void TMastrini_grid::read(const TBill& conto, int annoes, const TDate& dd, const TDate& ad, const TString& dc, const TString& ac) { destroy(); _mastrino.read(conto, annoes, dd, ad, dc, ac); update(); update_mask(); } void TMastrini_grid::reread() { destroy(); _mastrino.reread(); update(); update_mask(); } void TMastrini_grid::update_mask() const { TMask& gm = mask(); gm.set(F_ESERCIZIO, _mastrino.esercizio()); gm.set(F_DADATA, _mastrino.inizio_periodo()); gm.set(F_ADATA, _mastrino.fine_periodo()); set_imp(gm.field(F_TOTPRO_SAL), _mastrino.saldo_iniziale()); gm.set(F_TOTPRO_DAR, _mastrino.progressivo_dare_iniziale()); gm.set(F_TOTPRO_AVE, _mastrino.progressivo_avere_iniziale()); gm.reset(F_TOTRIG_SAL); gm.reset(F_TOTRIG_DAR); gm.reset(F_TOTRIG_AVE); set_imp(gm.field(F_TOTPER_SAL), _mastrino.saldo_periodo()); gm.set(F_TOTPER_DAR, _mastrino.progressivo_dare_periodo()); gm.set(F_TOTPER_AVE, _mastrino.progressivo_avere_periodo()); set_imp(gm.field(F_TOTATT_SAL), _mastrino.saldo_finale()); gm.set(F_TOTATT_DAR, _mastrino.progressivo_dare_finale()); gm.set(F_TOTATT_AVE, _mastrino.progressivo_avere_finale()); gm.enable(DLG_LINK, _mastrino.items() > 0); } void TMastrini_grid::load_colors() { TConfig conf(CONFIG_USER, "cg3600b"); _mas_back = conf.get_color("ColorMB", NULL, -1, FOCUS_BACK_COLOR); _mas_fore = conf.get_color("ColorMF", NULL, -1, FOCUS_COLOR); _con_back = conf.get_color("ColorCB", NULL, -1, NORMAL_BACK_COLOR); _con_fore = conf.get_color("ColorCF", NULL, -1, NORMAL_COLOR); } void TMastrini_grid::save_colors() { TConfig conf(CONFIG_USER, "cg3600b"); conf.set_color("ColorMB", _mas_back); conf.set_color("ColorMF", _mas_fore); conf.set_color("ColorCB", _con_back); conf.set_color("ColorCF", _con_fore); } void TMastrini_grid::set_colors() { TColor_mask m(_mas_back, _mas_fore, _con_back, _con_fore); if (m.run() == K_ENTER) { m.get_colors(_mas_back, _mas_fore, _con_back, _con_fore); save_colors(); } } /////////////////////////////////////////////////////////// // TGrid_mask /////////////////////////////////////////////////////////// class TGrid_mask : public TMask { TMastrini_grid* _grid; protected: // TMask virtual TMask_field* parse_field(TScanner& sc); virtual bool on_key(KEY k); virtual void handler(WINDOW win, EVENT* ep); static bool link_handler(TMask_field& f, KEY k); public: TMastrini_grid& grid() { CHECK(_grid, "What's grid?"); return *_grid; } TGrid_mask(); virtual ~TGrid_mask() { } }; TGrid_mask::TGrid_mask() : _grid(NULL) { read_mask("cg3600b", 0, 0); set_handler(DLG_LINK, link_handler); } TMask_field* TGrid_mask::parse_field(TScanner& sc) { TMask_field* f; if (sc.key() == "SP") f = _grid = new TMastrini_grid(this); else f = TMask::parse_field(sc); return f; } bool TGrid_mask::link_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TGrid_mask& gm = (TGrid_mask&)f.mask(); TMastrini_grid& grid = gm.grid(); const long rec = grid.selected(); if (rec >= 0 && rec < grid.items()) grid.on_record_button(rec); } return TRUE; } bool TGrid_mask::on_key(KEY k) { long rec = grid().selected(); switch (k) { case K_LHOME: rec = 0; break; case K_PREV: rec -= _grid->visible_rows(); if (rec < 0) rec = 0; break; case K_UP: if (rec > 0) rec--; break; case K_DOWN: if (rec < _grid->items()-1) rec++; break; case K_NEXT: rec += _grid->visible_rows(); if (rec >= _grid->items()) rec = _grid->items()-1; break; case K_LEND: rec = _grid->items()-1; break; default: break; } if (rec != grid().selected()) { grid().select(rec); return TRUE; } return TMask::on_key(k); } void TGrid_mask::handler(WINDOW win, EVENT* ep) { static TGrid_field* _last_grid = NULL; if (ep->type == E_MOUSE_DOWN && ep->v.mouse.button == 1) { _last_grid = NULL; RCT rct; _grid->get_rect(rct); if (xvt_rect_has_point(&rct, ep->v.mouse.where)) _last_grid = _grid; if (_last_grid) { TGrid_field& sht = (TGrid_field&)*_last_grid; #if (XVT_PTK_VERSION_MAJOR > 4) || (XVT_PTK_VERSION_MAJOR == 4 && XVT_PTK_VERSION_MINOR >= 50) MENU_ITEM* menu = xvt_res_get_menu(BROWSE_BAR); if (menu) { const PNT& p = ep->v.mouse.where; RCT cr; xvt_vobj_get_client_rect(win, &cr); XVT_POPUP_ALIGNMENT pa = XVT_POPUP_CENTER; if (p.h < cr.right / 3) pa = XVT_POPUP_LEFT_ALIGN; else if (p.h > 2 * cr.right / 3) pa = XVT_POPUP_RIGHT_ALIGN; xvt_menu_popup(menu->child, win, p, pa, NULL); xvt_res_free_menu_tree(menu); } #else ASK_RESPONSE r = xvt_dm_post_ask("Annulla", "Ripristina", "Salva", "Ordinamento delle colonne"); if (r == RESP_2) sht.reset_columns_order(); if (r == RESP_2 || r == RESP_3) sht.save_columns_order(); #endif return; } } if (ep->type == E_COMMAND) { if (_last_grid) { switch (ep->v.cmd.tag) { case M_EDIT_UNDO: _last_grid->reset_columns_order(); case M_EDIT_COPY: _last_grid->save_columns_order(); break; case M_EDIT_SEARCH: _last_grid->on_key(K_F11); break; default: break; } return; } } TMask::handler(win, ep); } /////////////////////////////////////////////////////////// // TQuery_mask /////////////////////////////////////////////////////////// class TQuery_mask : public TMask { protected: static bool esercizio_handler(TMask_field& f, KEY k); static bool data_handler(TMask_field& f, KEY k); static bool find_handler(TMask_field& f, KEY k); public: void do_query(TGrid_mask& gm); TQuery_mask(); virtual ~TQuery_mask() { } }; TQuery_mask::TQuery_mask() : TMask("cg3600a") { set_handler(F_ESERCIZIO, esercizio_handler); set_handler(F_DADATA, data_handler); set_handler(F_ADATA, data_handler); set_handler(DLG_FINDREC, find_handler); } bool TQuery_mask::esercizio_handler(TMask_field& f, KEY k) { if (k == K_TAB && f.focusdirty()) { TEsercizi_contabili esc; const int anno = atoi(f.get()); if (esc.exist(anno)) { TMask& m = f.mask(); TDate dd = m.get(F_DADATA); if (esc.date2esc(dd) != anno) m.set(F_DADATA, esc[anno].inizio()); dd = m.get(F_ADATA); if (esc.date2esc(dd) != anno) m.set(F_ADATA, esc[anno].fine()); } else { if (anno > 0) return error_box("Esercizio inesistente: %d", anno); } } return TRUE; } bool TQuery_mask::data_handler(TMask_field& f, KEY k) { bool ok = TRUE; if (k == K_ENTER) { const TQuery_mask& qm = (const TQuery_mask&)f.mask(); const TEsercizi_contabili esercizi; int codice_esercizio = qm.get_int(F_ESERCIZIO); if (codice_esercizio == 0) { const short id_altra_data = f.dlg() == F_DADATA ? F_ADATA : F_DADATA; const TDate d = qm.get(id_altra_data); if (d.ok()) codice_esercizio = esercizi.date2esc(d); } if (f.empty()) { if (codice_esercizio == 0) return error_box("E' necessario specificare almeno una data."); return TRUE; } const TDate d = f.get(); const int esercizio = esercizi.date2esc(d); if (codice_esercizio != 0) { if (esercizio != codice_esercizio) ok = error_box("La data deve appartenere all'esercizio %d", codice_esercizio); } else { if (esercizio == 0) ok = error_box("La data deve appartenere ad un esercizio contabile"); } } return ok; } bool TQuery_mask::find_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { const TQuery_mask& qm = (const TQuery_mask&)f.mask(); short id; switch (qm.get(F_TIPO)[0]) { case 'C': id = F_CLIENTE; break; case 'F': id = F_FORNITORE; break; default : id = F_SOTTOCONTO; break; } qm.field(id).on_key(K_F9); } return TRUE; } void TQuery_mask::do_query(TGrid_mask& gm) { const char t = get(F_TIPO)[0]; const int g = get_int(F_GRUPPO); const int c = get_int(F_CONTO); const long s = get_long((t <= ' ') ? F_SOTTOCONTO : ((t == 'C') ? F_CLIENTE : F_FORNITORE)); const TBill conto(g, c, s, t); const int annoes = get_int(F_ESERCIZIO); const TDate da_data(get(F_DADATA)); const TDate a_data(get(F_ADATA)); const TString& da_caus = get(F_DACAUSALE); const TString& a_caus = get(F_ACAUSALE); conto.set(gm, F_GRUPPO, F_CONTO, F_SOTTOCONTO, 0, F_DESSOTTOC); gm.grid().read(conto, annoes, da_data, a_data, da_caus, a_caus); gm.run(); } /////////////////////////////////////////////////////////// // TMastrini_video /////////////////////////////////////////////////////////// class TMastrini_video : public TApplication { TQuery_mask* _qm; TGrid_mask* _gm; TArray _file; protected: virtual bool create(); virtual bool destroy(); virtual bool menu(MENU_TAG); virtual void on_firm_change(); public: void open_files(int logicnum, ...); }; void TMastrini_video::open_files(int logicnum, ...) { va_list marker; va_start(marker, logicnum); while (logicnum > 0) { if (_file.objptr(logicnum)) NFCHECK("File gia' aperto: %d", logicnum); else _file.add(new TLocalisamfile(logicnum), logicnum); logicnum = va_arg(marker, int); } va_end(marker); } bool TMastrini_video::create() { xvt_statbar_set("", TRUE); open_files(LF_RMOV, LF_MOV, LF_CAUSALI, 0); open_files(LF_SALDI, LF_PCON, 0); _qm = new TQuery_mask; _gm = new TGrid_mask; dispatch_e_menu(BAR_ITEM(1)); return TRUE; } bool TMastrini_video::destroy() { delete _gm; delete _qm; _file.destroy(); return TRUE; } void TMastrini_video::on_firm_change() { TEsercizi_contabili::update(); } bool TMastrini_video::menu(MENU_TAG mt) { if (mt == BAR_ITEM(1)) { TQuery_mask& qm = *_qm; const TEsercizi_contabili esercizi; const int codice_ultimo_esercizio = esercizi.last(); if (codice_ultimo_esercizio > 0) { const TEsercizio& esercizio = esercizi.esercizio(codice_ultimo_esercizio); qm.set(F_ESERCIZIO, codice_ultimo_esercizio); qm.set(F_DADATA, esercizio.inizio()); qm.set(F_ADATA, esercizio.fine()); } KEY key = 0; while (key != K_QUIT) { qm.reset(F_SOTTOCONTO); // Azzera sottoconto qm.reset(F_CLIENTE); // Azzera cliente qm.reset(F_FORNITORE); // Azzera fornitore qm.reset(-5); // Azzera causali key = qm.run(); switch (key) { case K_ENTER: disable_menu_item(M_FONT); qm.do_query(*_gm); enable_menu_item(M_FONT); break; default: break; } } return FALSE; } else { _gm->grid().set_colors(); } return TRUE; } /////////////////////////////////////////////////////////// // Main /////////////////////////////////////////////////////////// int cg3600(int argc, char* argv[]) { TMastrini_video mv; mv.run(argc, argv, "Mastrini"); return 0; }