#include #include #include #include #include #include #include #include "cg3.h" #include "cglib.h" #include "cg3600a.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); void change_step(long step); public: TObject* TList::objptr(long index); void destroy(); long insert(TObject* obj, long pos); long add(TObject* obj, long pos = -1); TObject* detach(long pos); bool remove(long pos); void choose_step(long expected_size); // Sceglie il passo per gli elemnti TList(long expected_size); virtual ~TList(); }; TList::TList(long expected_size) : _items(0), _current(0) { _skip = new TList_object*[MAX_SKIP]; _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) { 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) { 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; } } } void TList::choose_step(long expected_size) { long step = expected_size / MAX_SKIP + 1; change_step(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 _last_skip++; } return index; } long TList::add(TObject* obj, long index) { if (index < 0 || index >= _items) { index = insert(obj, _items); } else { TList_object* lo = lstobjptr(index); CHECK(lo, "NULL list object"); delete lo->_obj; lo->_obj = obj; } return 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; } /////////////////////////////////////////////////////////// // TGrid_control /////////////////////////////////////////////////////////// class TGrid_control; class TGrid_field : public TOperable_field { long _to_select; protected: // TMask_field virtual void create(WINDOW parent); virtual void parse_head(TScanner& scanner); virtual bool parse_item(TScanner& scanner); public: virtual long items() const; virtual bool handler(XI_EVENT* ep); TGrid_control* grid() const { return (TGrid_control*)_ctl; } TGrid_field(TMask* m) : TOperable_field(m) { } virtual ~TGrid_field() { } }; class TGrid_control : public TControl { enum { FIRST_FIELD = 101, MAX_COL = 128 }; long _cur_rec; // @cmember:(INTERNAL) Tipo di ogni colonna byte _type[MAX_COL]; TGrid_field* _grid; protected: // TControl //@cmember Gestisce gli eventi delle celle virtual bool event_handler(XI_OBJ* itf, XI_EVENT* xiev); protected: long items() const { return _grid->items(); } int rec2row(long rec) const; long row2rec(int row) const; XI_OBJ* find_column(int col) const; public: long selected() const { return _cur_rec; } void select(long n); void set_focus_rec(long rec); 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); 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(0) { const int NUMBER_WIDTH = strchr(flags, 'A') ? 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 const bool multiline = strchr(flags, 'M') != NULL; const int lines_in_cell = multiline ? (int)xi_get_pref(XI_PREF_DEFAULT_MAX_LINES_IN_CELL) : 1; // 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->scroll_bar = TRUE; l->scroll_bar_button = TRUE; l->white_space_color = MASK_DARK_COLOR; l->rule_color = MASK_DARK_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; cd->size_rows = multiline; 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 | XI_ATR_READONLY; if (_type[i] == 'C') attr |= 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 = strchr(h, '\n') == NULL; if (multiline) { cd->size_rows = TRUE; cd->wrap_text = TRUE; } } 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(); } // Converts a row number in the correspondig record number int TGrid_control::rec2row(long record) const { int rows; const long* rec = xi_get_list_info(_obj, &rows); int r = int(record - rec[0]); 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 { int rows; const long* handle = xi_get_list_info(_obj, &rows); if (row < 0) row = 0; else { if (row >= rows) row = rows-1; } const long r = handle[row]; CHECKD(r >= 0 && r < items(), "Sheet line out of range: ", row); return r; } int TGrid_control::visible_rows() const { return xi_get_visible_rows(_obj, NULL, NULL); } 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); } } void TGrid_control::select(long rec) { if (rec >= 0 && rec < items()) { int rows; const long* handle = xi_get_list_info(_obj, &rows); int first = 0, last = 0; xi_get_visible_rows(_obj, &first, &last); if (rec < handle[first] || rec > handle[last]) xi_scroll_rec(_obj, rec, NORMAL_COLOR, XI_ATR_ENABLED, 0); } } void TGrid_control::set_focus_rec(long rec) { if (rec < 0) rec = selected(); int r = rec2row(rec); if (r < 0) { select(rec); r = rec2row(rec); } const int c = _type[0] == 'C' ? 2 : 1; XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, r, c); xi_set_focus(&cell); } XI_OBJ* TGrid_control::find_column( int col) const // @parm Indice o identificatore colonna { CHECKD(col >= 0, "Bad column ", col); if (col < FIRST_FIELD) // Se e' un indice trasformalo in identificatore col += FIRST_FIELD + 1000; int num; XI_OBJ** columns = xi_get_member_list(_obj, &num); for (int c = num-1; c >= 0; c--) { if (columns[c]->cid == col) break; } return c >= 0 ? columns[c] : NULL; } // 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; } else refused = TRUE; break; case XIE_GET_LAST: xiev->v.rec_request.data_rec = items()-1; 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()) refused = TRUE; else xiev->v.rec_request.data_rec = n; } 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 = int(rec * 100L / n); } break; case XIE_COL_MOVE: if (xiev->v.column.in_fixed || xiev->v.column.col_nbr < xi_get_fixed_columns(xiev->v.column.list)) refused = TRUE; break; case XIE_COL_SIZE: break; case XIE_SELECT: xiev->refused = TRUE; break; case XIE_CELL_REQUEST: if (xiev->v.cell_request.col_nbr == 0) { if (xiev->v.cell_request.len > 1) { sprintf(xiev->v.cell_request.s, "%ld", xiev->v.cell_request.rec); xiev->v.cell_request.color = NORMAL_COLOR; } } break; default: break; } return !refused; } /////////////////////////////////////////////////////////// // TGrid_field /////////////////////////////////////////////////////////// void TGrid_field::create(WINDOW parent) { _ctl = new TGrid_control(parent, dlg(), _ctl_data._x, _ctl_data._y, _ctl_data._width, _ctl_data._size, _ctl_data._flags, _ctl_data._park, this); } 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; } 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) { if (xiev->type == XIE_CELL_REQUEST) { const long& rec = xiev->v.cell_request.rec; const short& col = xiev->v.cell_request.col_nbr; if (col == 3) { strcpy(xiev->v.cell_request.s, "Questa e' una descrizioncina sgarsolina sgarsolina," " che non doverebbe stare in una sola righina"); xiev->v.cell_request.back_color = rec % 7 ? COLOR_YELLOW : COLOR_CYAN; return TRUE; } } return FALSE; } long TGrid_field::items() const { return 30000L; } /////////////////////////////////////////////////////////// // TGrid_mask /////////////////////////////////////////////////////////// class TGrid_mask : public TMask { TEsercizi_contabili _esercizi; protected: // TMask virtual TMask_field* parse_field(TScanner& sc); static bool data_handler(TMask_field& f, KEY k); public: const TEsercizi_contabili& esercizi() const { return _esercizi; } TEsercizi_contabili& esercizi() { return _esercizi; } TGrid_mask(const char* name); virtual ~TGrid_mask() { } }; TGrid_mask::TGrid_mask(const char* name) { read_mask(name, 0, 0); set_handler(F_DADATA, data_handler); set_handler(F_ADATA, data_handler); } TMask_field* TGrid_mask::parse_field(TScanner& sc) { TMask_field* f; if (sc.key() == "SP") f = new TGrid_field(this); else f = TMask::parse_field(sc); return f; } bool TGrid_mask::data_handler(TMask_field& f, KEY k) { bool ok = TRUE; if (f.to_check(k) && !f.empty()) { const TGrid_mask& gm = (const TGrid_mask&)f.mask(); int codice_esercizio = gm.get_int(F_ESERCIZIO); if (codice_esercizio == 0) { const short id_altra_data = f.dlg() == F_DADATA ? F_ADATA : F_DADATA; const TDate d = gm.get(id_altra_data); if (d.ok()) codice_esercizio = gm.esercizi().date2esc(d); } const TDate d = f.get(); const int esercizio = gm.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; } /////////////////////////////////////////////////////////// // TMastrini_video /////////////////////////////////////////////////////////// class TMastrini_video : public TApplication { TGrid_mask* _gm; protected: virtual bool create(); virtual bool menu(MENU_TAG); virtual void on_firm_change(); }; bool TMastrini_video::create() { _gm = NULL; dispatch_e_menu(BAR_ITEM(1)); return TRUE; } void TMastrini_video::on_firm_change() { if (_gm) _gm->esercizi().update(); } bool TMastrini_video::menu(MENU_TAG) { _gm = new TGrid_mask("cg3600a"); KEY key = 0; while (key != K_QUIT) { if (key == 0) { const TEsercizi_contabili& esercizi = _gm->esercizi(); const int codice_ultimo_esercizio = esercizi.last(); if (codice_ultimo_esercizio > 0) { const TEsercizio& esercizio = esercizi.esercizio(codice_ultimo_esercizio); _gm->set(F_ESERCIZIO, codice_ultimo_esercizio); _gm->set(F_DADATA, esercizio.inizio()); _gm->set(F_ADATA, esercizio.fine()); } } else { _gm->reset(F_SOTTOCONTO); // Azzera sottoconto _gm->reset(F_CLIENTE); // Azzera cliente _gm->reset(F_FORNITORE); // Azzera fornitore _gm->reset(F_DACAUSALE); // Azzera causali _gm->reset(F_ACAUSALE); } key = _gm->run(); } delete _gm; return FALSE; } int cg3600(int argc, char* argv[]) { TMastrini_video mv; mv.run(argc, argv, "Mastrini"); return 0; }