#include #include "cg3.h" #include "cglib02.h" #include "cg3600.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class TGrid_mask; class TQuery_mask : public TAutomask { TGrid_mask* _gm; TString4 _last_tipo; protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); public: void do_query(); TQuery_mask(TGrid_mask* gm); virtual ~TQuery_mask() { } }; class TMastrini_video : public TSkeleton_application { TQuery_mask* _qm; TGrid_mask* _gm; protected: virtual bool create(); virtual void main_loop(); virtual bool destroy(); public: TQuery_mask & query_mask() { return *_qm; } }; HIDDEN inline TMastrini_video& app() { return (TMastrini_video&)main_app();} /////////////////////////////////////////////////////////// // TList /////////////////////////////////////////////////////////// // Simpler and faster 32 bit implemetation class TList : public TContainer { TArray _data; protected: // @cmember Ritorna un puntatore al primo oggetto del contenitore virtual TObject* first_item( ) { return _data.first_item(); } // @cmember Ritorna un puntatore all'ultimo oggetto del contenitore virtual TObject* last_item( ) { return _data.last_item(); } // @cmember Ritorna un puntatore all'oggetto successivo all'oggetto corrente virtual TObject* succ_item( ) { return _data.succ_item(); } // @cmember Ritorna un puntatore all'oggetto che precede l'oggetto corrente virtual TObject* pred_item( ) { return _data.pred_item(); } // @cmember Ritorna il numero di oggetti nel contenitore virtual long objects( ) const { return _data.objects(); } public: long items() const { return _data.items(); } void destroy() { _data.destroy(); } long insert(TObject* obj, long pos) { return _data.insert(obj, pos); } long append(TObject* obj, long pos = -1); bool remove(long pos) { _data.remove(pos, true); return true; } const TObject& obj(long index) const { const TObject* o = _data.objptr(index); CHECK(o, "Null list item"); return *o; } }; long TList::append(TObject* obj, long index) { if (index < 0 || index >= items()) index = items(); else index++; return insert(obj, index); } /////////////////////////////////////////////////////////// // 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 movimento 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 TCursor* _cur; 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; // Date limite TString _da_caus, _a_caus; // Causali limite bool _provvis; // Includi provvisori real _pdare_ini, _pavere_ini; real _pdare_per, _pavere_per; real _pdare_fin, _pavere_fin; TList _riga; // Righe del mastrino protected: TCursor& cur() { return *_cur; } 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, bool provvis); 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; const TBill& conto() const { return _conto; } 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; TCursor* TMastrino::_cur = NULL; 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 _cur; _cur = NULL; 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(); long i; for (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(); long i; for (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, bool provvis) { 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 _provvis = provvis; 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(); } // 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, provvis); _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, provvis); _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; 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 rmov_rec.zero(); conto.put(rmov_rec); TRectype darow(rmov_rec), arow(rmov_rec); darow.put(RMV_DATAREG, inizio_esercizio); arow.put(RMV_DATAREG, max_data_reg); TCursor cur(&rel(), "", 2, &darow, &arow); const TRecnotype totrows = cur.items(); cur.freeze(); TString caption(80); caption.format(FR("Caricamento mastrino %03d.%03d.%06ld"), _conto.gruppo(), _conto.conto(), _conto.sottoconto()); TProgind pi(totrows, caption, false, true); for (cur = 0L; cur.pos() < totrows; ++cur) { pi.addstatus(1); #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 eventualmente i movimenti provvisori if (!_provvis) { const char is_provvis = *(const char*)mov_provvis; if (is_provvis > ' ') continue; } const TDate data_corrente = _esercizio <= 0 ? rmov_datareg : TDate((const char*)mov_datacomp); if (data_corrente > _a_data) continue; 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, rmov_datareg); _riga.append(r); } } // Mi sposto all'inizio per far funzionare bene da subito il metodo riga(0) mov().first(); rmov().first(); } void TMastrino::reread() { read(_conto, _esercizio, _da_data, _a_data, _da_caus, _a_caus, _provvis); } 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 (rec >= 0 && rec < items()) { 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 (rec < 0) { for (long i = last(); i >= 0; i = pred(i)) collapse(i); } else { if (row(rec).tipo() != riga_mastrino) // Se non sono su una riga mastrino ... rec = pred(rec, riga_mastrino); // ... mi sposto sulla precedente riga mastrino 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); } XI_EVENT* event() { return _xiev; } 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; public: TGrid_control& grid() const { return (TGrid_control&)*_ctl; } 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(const char* head) const; public: long selected() const { return _cur_rec; } bool select(long n); int visible_rows() const; XI_OBJ* find_column(short cid) 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 const char* h; for (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_OBJ* itf = get_interface(parent); XI_RCT rct = coord2rct(itf, x, y, dx, dy); 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; long list_attr = XI_ATR_ENABLED | XI_ATR_VISIBLE; // if (_read_only) list_attr |= XI_ATR_NAVIGATE; XI_OBJ_DEF* listdef = xi_add_list_def(NULL, cid, rct.top, rct.left, rct.bottom-rct.top, list_attr, 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(xi_get_system_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'; } XI_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++) { xi_get_rect(column[i], &rd); _default_width[i] = rd.right - rd.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) { if (rec == _cur_rec) // Simulo intercettazione doppio click _grid->on_record_button(rec); if (_read_only) refused = true; } else { if (_read_only && rec == _cur_rec) { const short cid = col2cid(xiev->v.select.column); _grid->on_dbl_cell(rec, cid); refused = true; } } _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); int i; for (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; int i; for (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, (XinRect*)&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], (XinRect*)&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 /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// // Maschera per colori /////////////////////////////////////////////////////////// class TColor_mask : public TSelect_color_mask { public: void get_colors(COLOR& mb, COLOR& mf, COLOR& cb, COLOR& cf); TColor_mask(); }; void TColor_mask::get_colors(COLOR& mb, COLOR& mf, COLOR& cb, COLOR& cf) { get_color("M", mb, mf); get_color("C", cb, cf); } TColor_mask::TColor_mask() : TSelect_color_mask("cg3600b") { add_color_def("M", TR("Riga mastrino"), REQUIRED_BACK_COLOR, FOCUS_COLOR); add_color_def("C", TR("Riga contropartita"), NORMAL_BACK_COLOR, NORMAL_COLOR); } /////////////////////////////////////////////////////////// // TMastrini_grid /////////////////////////////////////////////////////////// class TMastrini_grid : public TGrid_field { TMastrino _mastrino; TDecoder _causali; TEsercizi_contabili _esercizi; TColor_mask _colmsk; COLOR _mas_back, _mas_fore; COLOR _con_back, _con_fore; bool _primanoting, _contsep; protected: // TGrid_field virtual bool on_record(long rec); virtual void on_grid_button(); virtual bool on_resize_column(short id, int new_size); void update_mask() const; public: virtual void cell_request(long rec, short id, TGrid_cell& cell); virtual void on_dbl_cell(long rec, short id); 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, bool provv); void reread(); TMastrino& mastrino() { return _mastrino; } void save_colors(); void load_colors(); void set_colors(); void set_contsep(bool cs) { _contsep = cs; } TMastrini_grid(TMask* m); virtual ~TMastrini_grid() { } }; TMastrini_grid::TMastrini_grid(TMask* m) : TGrid_field(m), _causali(LF_CAUSALI, CAU_DESCR), _primanoting(false), _contsep(false) { load_colors(); } void TMastrini_grid::destroy() { _mastrino.destroy(); grid().select(-1); } HIDDEN const char* real2string(const real& r) { TCurrency cur(r); return cur.string(true); } HIDDEN void set_imp(TMask_field& f, const TImporto& imp) { if (!imp.is_zero()) { TString80 str; str.format("%s %c", real2string(imp.valore()), imp.sezione()); f.set(str); } else f.reset(); } void TMastrini_grid::cell_request(long rec, short id, TGrid_cell& cell) { if (rec < 0) // testate { XI_OBJ* col = grid().find_column(short(1000+id%1000)); if (col != NULL) { TString head; xi_get_text(col, head.get_buffer(), head.size()); cell = head; } return; } 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); TString8 str; if (anno != eser) str = "C"; // Di competenza vecchia if (mov.get(MOV_PROVVIS).not_empty()) { if (str.not_empty()) str << '/'; str << 'P'; } cell = str; } 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: // Descrizione { const TRectype& rmov = _mastrino.riga(rec); const TRectype& mov = _mastrino.testata(rec); TString descr = rmov.get(RMV_DESCR); if (riga.tipo() == riga_mastrino) { if (descr.empty()) { const TRectype& mov = _mastrino.testata(rec); descr = mov.get(MOV_DESCR); if (descr.empty()) { TBill uncle(rmov,true); descr = uncle.descrizione(); } } } else { // Bug 0001748: ignora descrizioni generate da contabilizzazione if (descr.empty() || descr == _mastrino.conto().descrizione()) { const TBill conto(rmov); descr = conto.descrizione(); } } if (_contsep) { const TString16 cs = mov.get(MOV_CONTSEP); if (cs.full()) descr << "\n" << cache().get("&NPENT", cs, "S0"); } cell.set(descr); } break; case 105: // Dare { const TRectype& rmov = _mastrino.riga(rec); const char sez = rmov.get_char(RMV_SEZIONE); if (sez == 'D') cell = real2string(rmov.get_real(RMV_IMPORTO)); } break; case 106: // Avere { const TRectype& rmov = _mastrino.riga(rec); const char sez = rmov.get_char(RMV_SEZIONE); if (sez == 'A') cell = real2string(rmov.get_real(RMV_IMPORTO)); } 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 = real2string(rmov.get_real(RMV_IMPORTO)); 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 = real2string(imp.valore()); 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 (rec >= 0 && rec < items()) { 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, TR("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(FR("%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) { if (!_primanoting) { _primanoting = true; const TRectype& testata = _mastrino.testata(rec); bool refresh = testata.edit(); if (refresh && yesno_box(TR("Si desidera aggiornare il mastrino?"))) reread(); _primanoting = false; } } // 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, bool provv) { destroy(); _mastrino.read(conto, annoes, dd, ad, dc, ac, provv); 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()); const bool can_link = main_app().argc() <= 2; // NON sono stato chiamato dalla prima nota gm.enable(DLG_LINK, can_link && _mastrino.items() > 0); gm.enable(DLG_NEWREC, can_link); } void TMastrini_grid::load_colors() { _colmsk.get_colors(_mas_back, _mas_fore, _con_back, _con_fore); } void TMastrini_grid::set_colors() { if (_colmsk.run() == K_ENTER) load_colors(); } /////////////////////////////////////////////////////////// // TMastrino_set /////////////////////////////////////////////////////////// class TMastrino_set : public TRecordset { TMastrini_grid& _grid; long _curr; TArray _info; protected: void add_field(TFieldtypes t, short id, int width, const char* name = NULL); long cell_request(long rec, short column, TString& str) const; const TString& query_text() const { return EMPTY_STRING; } public: virtual TRecnotype items() const { return _grid.items(); } virtual unsigned int columns() const { return _info.items(); } virtual const TRecordset_column_info& column_info(unsigned int column) const; virtual bool move_to(TRecnotype n); virtual TRecnotype current_row() const { return _curr; } virtual void requery() {} virtual const TVariant& get(unsigned int column) const; TMastrino_set(TMastrini_grid& g); }; const TRecordset_column_info& TMastrino_set::column_info(unsigned int column) const { return (const TRecordset_column_info&)_info[column]; } bool TMastrino_set::move_to(TRecnotype n) { const bool ok = n >= 0 && n < items(); _curr = n; return ok; } long TMastrino_set::cell_request(long rec, short column, TString& str) const { XI_EVENT xiev; memset(&xiev, 0, sizeof(xiev)); xiev.type = XIE_CELL_REQUEST; xiev.v.cell_request.s = str.get_buffer(); xiev.v.cell_request.len = str.size(); TGrid_cell cell(&xiev); _grid.cell_request(rec, column, cell); return xiev.v.cell_request.attrib; } const TVariant& TMastrino_set::get(unsigned int column) const { if (_curr >= 0 && _curr < items() && column >= 0 && column < columns()) { const TRecordset_column_info& info = column_info(column); TToken_string str; cell_request(_curr, abs(info._pos), str); if (str.full()) { //decide se il campo appartiene ad una sottocella (es. numdoc / prot) bool divide = info._pos < 0; //è la prima parte di una sottocella if (!divide && column < columns()-1) divide = column_info(column+1)._pos < 0; //è la seconda parte di una sottocella //se deve splittare cerca lo spazio come carattere di separazione tra le sottocelle if (divide) { const int cr = str.find(' '); if (cr >= 0) { if (info._pos > 0) str.cut(cr); else str.ltrim(cr); str.trim(); } } TVariant& tmp = get_tmp_var(); switch (info._type) { case _realfld: tmp = real(real::ita2eng(str)); break; case _longfld: tmp = atol(str); break; case _datefld: tmp = TDate(str); break; default: if (info._width == 1) tmp = str.right(1); else tmp = str; break; } return tmp; } } return NULL_VARIANT; } void TMastrino_set::add_field(TFieldtypes tipo, short id, int width, const char* name) { TRecordset_column_info* i = new TRecordset_column_info; TString& n = i->_name; if (name && *name) n = name; else { cell_request(-1, abs(id), n); const int cr = n.find('\n'); if (cr > 0) { if (id > 0) n.cut(cr); else n.ltrim(cr+1); n.trim(); } } i->_type = tipo; i->_pos = id; i->_width = width; _info.add(i); } TMastrino_set::TMastrino_set(TMastrini_grid& g) : _grid(g), _curr(-1) { _grid.mastrino().collapse(-1);// Nascondo tutte le righe di contropartita add_field(_datefld, 101, 10); // Data reg add_field(_datefld,-101, 10); // Data comp add_field(_alfafld, 102, 1); // Movimento di competenza? add_field(_alfafld, 103, 25); // Causale add_field(_alfafld, 104, 50); // Descrizione add_field(_realfld, 105, 13); // Dare add_field(_realfld, 106, 13); // Avere add_field(_alfafld, 107, 7); // Num doc add_field(_longfld,-107, 7); // Num prot add_field(_realfld, 108, 13); // Saldo add_field(_alfafld,-108, 1, "Sezione"); // Sezione add_field(_realfld, 109, 13); // Saldo giornaliero add_field(_alfafld,-109, 1, "Sezione"); // Sezione giornaliera } /////////////////////////////////////////////////////////// // 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 long handler(WINDOW win, EVENT* ep); static bool link_handler(TMask_field& f, KEY k); static bool new_handler(TMask_field& f, KEY k); static bool edit_handler(TMask_field& f, KEY k); static bool export_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); set_handler(DLG_NEWREC, new_handler); set_handler(DLG_EDIT, edit_handler); set_handler(DLG_EXPORT, export_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::new_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TExternal_app app("cg2 -0"); const bool refresh = app.run() == 0; if (refresh && yesno_box(TR("Si desidera aggiornare il mastrino?"))) { TGrid_mask& gm = (TGrid_mask&)f.mask(); TMastrini_grid& grid = gm.grid(); grid.reread(); } } return true; } bool TGrid_mask::edit_handler(TMask_field& f, KEY k) { bool ok = true; if (k == K_SPACE) { TGrid_mask& gm = (TGrid_mask&)f.mask(); TMastrini_grid& grid = gm.grid(); TMastrino_set ms(grid); TFilename n; n.tempdir(); n.add("mastrino.xls"); ms.save_as(n); ok = xvt_sys_goto_url(n, "open") != FALSE; if (!ok) ok = export_handler(f, k); // Se non parte Excel salvo altrove } return ok; } bool TGrid_mask::export_handler(TMask_field& f, KEY k) { bool ok = true; if (k == K_SPACE) { TFilename n; n.tempdir(); n.add("mastrino.xls"); FILE_SPEC fs; xvt_fsys_convert_str_to_fspec(n, &fs); if (xvt_dm_post_file_save(&fs, f.prompt()) == FL_OK) { xvt_fsys_convert_fspec_to_str(&fs, n.get_buffer(), n.size()); TGrid_mask& gm = (TGrid_mask&)f.mask(); TMastrini_grid& grid = gm.grid(); TMastrino_set ms(grid); ms.save_as(n); } } return ok; } 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; case K_ENTER: case K_CTRL+'+': case K_CTRL+'-': if (focus_field().dlg() == _grid->dlg()) _grid->on_dbl_cell(rec, DLG_USER); break; default: break; } if (rec != grid().selected()) { grid().select(rec); return true; } return TMask::on_key(k); } long 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; MENU_ITEM* menu = xvt_res_get_menu(BROWSE_BAR); if (menu != NULL) { const PNT& p = ep->v.mouse.where; xvt_menu_popup(menu->child, win, p, XVT_POPUP_CENTER, 0); xvt_res_free_menu_tree(menu); } return 0L; } } if (ep->type == E_COMMAND) { if (_last_grid) { switch (ep->v.cmd.tag) { case BROWSE_BAR+1: _last_grid->save_columns_order(); break; case BROWSE_BAR+2: _last_grid->reset_columns_order(); break; case BROWSE_BAR+3: _last_grid->on_key(K_F11); break; default: break; } return 0L; } } return TMask::handler(win, ep); } /////////////////////////////////////////////////////////// // TQuery_mask /////////////////////////////////////////////////////////// bool TQuery_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_TIPO: if (e == fe_modify) { const TString& tipo = o.get(); if (tipo != _last_tipo) { _last_tipo = tipo; if (tipo.full()) { TWait_cursor hourglass; TString query; query << "USE " << LF_PCON << " SELECT " << PCN_TMCF << "=\"" << tipo << "\""; TISAM_recordset conti(query); if (conti.move_first()) { set(F_GRUPPO, conti.get(PCN_GRUPPO).as_int(), 0x2); set(F_CONTO, conti.get(PCN_CONTO).as_int(), 0x2); } } } } break; case F_CLIENTE: case F_FORNITORE: if (e == fe_modify && !o.empty()) { const TRectype& rec = ((TEdit_field&)o).browse()->cursor()->curr(); const int g = rec.get_int(CLI_GRUPPO); const int c = rec.get_int(CLI_CONTO); if (g > 0 && c > 0) { set(F_GRUPPO, g, 0x2); set(F_CONTO, c, 0x2); } } break; case F_ESERCIZIO: if (e == fe_modify || e == fe_close) { TEsercizi_contabili esc; const int anno = atoi(o.get()); if (esc.exist(anno)) { TDate dd = get(F_DADATA); if (esc.date2esc(dd) != anno) set(F_DADATA, esc[anno].inizio()); dd = get(F_ADATA); if (esc.date2esc(dd) != anno) set(F_ADATA, esc[anno].fine()); } else { if (anno > 0) return error_box(FR("Esercizio inesistente: %d"), anno); } } break; case F_DADATA: case F_ADATA: if (e == fe_close) { const TEsercizi_contabili esercizi; int codice_esercizio = get_int(F_ESERCIZIO); if (codice_esercizio <= 0) { const short id_altra_data = o.dlg() == F_DADATA ? F_ADATA : F_DADATA; const TDate d = get(id_altra_data); if (d.ok()) codice_esercizio = esercizi.date2esc(d); } if (o.empty()) { if (codice_esercizio == 0) return error_box(TR("E' necessario specificare almeno una data.")); return true; } const TDate d = o.get(); const int esercizio = esercizi.date2esc(d); if (get_int(F_ESERCIZIO) != 0) { if (esercizio != codice_esercizio) return error_box(FR("La data deve appartenere all'esercizio %d"), codice_esercizio); } else { if (esercizio == 0) return error_box(TR("La data deve appartenere ad un esercizio contabile")); } } break; case DLG_FINDREC: if (e == fe_button) { short id; switch (get(F_TIPO)[0]) { case 'C': id = F_CLIENTE; break; case 'F': id = F_FORNITORE; break; default : id = F_SOTTOCONTO; break; } field(id).on_key(K_F9); } break; case DLG_CONFIG: if (e == fe_button) _gm->grid().set_colors(); break; default: break; } return true; } TQuery_mask::TQuery_mask(TGrid_mask* gm) : TAutomask("cg3600a"), _gm(gm), _last_tipo("") { } void TQuery_mask::do_query() { 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); const bool provv = get_bool(F_PROVVIS); conto.set(*_gm, F_GRUPPO, F_CONTO, F_SOTTOCONTO, 0, F_DESSOTTOC); TMastrini_grid& gf = _gm->grid(); gf.set_contsep(get_bool(F_ST_CONTSEP)); gf.read(conto, annoes, da_data, a_data, da_caus, a_caus, provv); gf.select(get_bool(F_END) ? gf.items() -1 : 0); _gm->run(); } /////////////////////////////////////////////////////////// // TMastrini_video /////////////////////////////////////////////////////////// bool TMastrini_video::create() { xvtil_statbar_set("", TRUE); open_files(LF_RMOV, LF_MOV, LF_CAUSALI, LF_SALDI, LF_PCON, 0); _gm = new TGrid_mask; _qm = new TQuery_mask(_gm); return TSkeleton_application::create(); } bool TMastrini_video::destroy() { delete _qm; delete _gm; return TSkeleton_application::destroy(); } void TMastrini_video::main_loop() { TQuery_mask& qm = *_qm; if (argc() > 2) { TFilename ininame = argv(2)+2; if (ininame.exist()) { TConfig ini(ininame, "24"); qm.set(F_ESERCIZIO, ini.get(RMV_ANNOES)); qm.set(F_TIPO, ini.get(RMV_TIPOC)); qm.set(F_GRUPPO, ini.get(RMV_GRUPPO)); qm.set(F_CONTO, ini.get(RMV_CONTO)); qm.set(F_SOTTOCONTO, ini.get(RMV_SOTTOCONTO)); qm.set(F_CLIENTE, ini.get(RMV_SOTTOCONTO)); qm.set(F_FORNITORE, ini.get(RMV_SOTTOCONTO)); qm.send_key(K_SPACE, DLG_OK); } } const TEsercizi_contabili esercizi; TDate inies, fines; int codesc = qm.get_int(F_ESERCIZIO); if (!esercizi.exist(codesc)) codesc = esercizi.last(); esercizi.code2range(codesc, inies, fines); qm.set(F_ESERCIZIO, codesc); qm.set(F_DADATA, inies); qm.set(F_ADATA, fines); while (qm.run() == K_ENTER) qm.do_query(); } /////////////////////////////////////////////////////////// // Main /////////////////////////////////////////////////////////// int cg3600(int argc, char* argv[]) { TMastrini_video mv; mv.run(argc, argv, TR("Mastrini")); return 0; }