#define XI_INTERNAL #include extern "C" { #include } #include #include #include #include #include #include #include #include #include #include #include class TCell_property : public TObject { COLOR _back, _fore; public: void set(COLOR back, COLOR fore) { _back = back; _fore = fore; } bool get(COLOR& back, COLOR& fore) const; TCell_property(); }; bool TCell_property::get(COLOR& back, COLOR& fore) const { if (_back != COLOR_INVALID && _fore != COLOR_INVALID) { back = _back; fore = _fore; return true; } return false; } TCell_property::TCell_property() : _back(COLOR_INVALID), _fore(COLOR_INVALID) { } class TRow_property : public TObject { TBit_array _disabled; COLOR _back, _fore; int _height; TArray * _cell_prop; public: void set(int col, COLOR back, COLOR fore); bool get(int col, COLOR & back, COLOR & fore) const; void set_height(int h) { _height = h; } int height() const { return _height; } TBit_array & disabled() { return _disabled;} const TBit_array & disabled() const { return _disabled;} TRow_property(); virtual ~TRow_property() { } }; TRow_property::TRow_property() : _back(COLOR_INVALID), _fore(COLOR_INVALID), _height(-1), _cell_prop(NULL) { } void TRow_property::set(int col, COLOR back, COLOR fore) { if (col < 0) { _back = back; _fore = fore; if (_cell_prop != NULL) { delete _cell_prop; _cell_prop = NULL; } } else { if (_cell_prop == NULL) _cell_prop = new TArray; TCell_property* p = (TCell_property*)_cell_prop->objptr(col); if (back == COLOR_INVALID || fore == COLOR_INVALID) { if (p != NULL) _cell_prop->destroy(col); } else { if (p == NULL) { p = new TCell_property; _cell_prop->add(p, col); } p->set(back, fore); } } } bool TRow_property::get(int col, COLOR & back, COLOR & fore) const { if (col >= 0) // Lascia stare la colonna del numero riga { if (_cell_prop != NULL) { const TCell_property* cp = (const TCell_property*)_cell_prop->objptr(col); if (cp != NULL && cp->get(back, fore)) return true; } if (_back != COLOR_INVALID && _fore != COLOR_INVALID) { back = _back; fore = _fore; return true; } } return false; } /////////////////////////////////////////////////////////// // TSpreadsheet /////////////////////////////////////////////////////////// // @doc INTERNAL // @class TSpreadsheet | Classe per la creazione di uno spreadsheet all'interno // di una maschera // // @base public | TWindow class TSpreadsheet : public TControl { // @author:(INTERNAL) Guido // @cfriend TSheet_field friend class TSheet_field; // @access:(INTERNAL) Private Member // @cmember:(INTERNAL) costanti enum { MAX_COL=96 }; static int ROW_NUMBER_WIDTH; // @cmember:(INTERNAL) Array di TToken_strings contenenti le righe TString_array _str; // @cmember:(INTERNAL) Array delle colonne disattivate (solo visualizzazione) TBit_array _column_disabled; // @cmember:(INTERNAL) Array delle proprieta' delle righe TArray _property; // @cmember:(INTERNAL) Maschera in cui e' contenuto lo spreadsheet TMask _mask; // @cmember:(INTERNAL) Numero di colonne presenti nello spreadsheet int _columns; // @cmember:(INTERNAL) Indica se e' attivo lo spreadsheet bool _active; // @cmember:(INTERNAL) Indica se e' attivo l'auto append bool _auto_append; // @cmember:(INTERNAL) Identificatore della prima colonna di navigazione short _first_nav_column_id; // @cmember:(INTERNAL) Identificatore dell' ultima colonna di navigazione short _last_nav_column_id; // @cmember:(INTERNAL) Funzione per la gestione di una riga dello sheet (vedi ) SPREADSHEET_NOTIFY _notify; // @cmember:(INTERNAL) Campo corrente che si sta editando TOperable_field* _edit_field; // @cmember:(INTERNAL) Coordinata della riga della cella corrente int _cur_row; // @cmember:(INTERNAL) Numero del record sul file al quale fa riferimento la cella corrente int _cur_rec; // @cmember:(INTERNAL) Coordinata della colonna della cella corrente int _cur_col; // @cmember:(INTERNAL) Indica se la riga corrente e' stata modificata bool _row_dirty; // @cmember:(INTERNAL) Indica se la cella corrente e' stata modificata bool _cell_dirty; // @cmember:(INTERNAL) Permette di gestire i check OFF_ROW e OFF_CELL bool _check_enabled; // @cmember:(INTERNAL) Numero della riga che necessita aggiornamento (vengono aggiornate // nella ) int _needs_update; // @cmember:(INTERNAL) Numero della riga a cui saltare appena possibile int _selection_posted; clock_t _ignore_button; // @cmember:(INTERNAL) Dimensioni delle colonne int _default_width[MAX_COL]; // @cmember:(INTERNAL) Bisogna salvare l'ordine delle colonne byte _save_columns_order; // @cmember:(INTERNAL) Campo corrente che si sta editando TOperable_field* _f9_target; // @cmember:(INTERNAL) Inizializza lo spreadsheet void init(); // @cmember:(INTERNAL) Funzione che intercetta gli eventi dello spreadsheet friend void XVT_CALLCONV1 xiev_handler(XI_OBJ *itf, XI_EVENT *xiev); // @access Protected Member protected: //@cmember Gestisce gli eventi delle celle (chiamata dal ) virtual bool event_handler(XI_OBJ* itf, XI_EVENT* xiev); virtual void set_read_only(bool ro) { activate(!ro); } virtual bool read_only() const { return !_active; } KEY barcode_newline() const; //@cmember Copia una cella nel corrispondente campo della maschera e ne ritorna il contenuto const char* copy_cell2field(XI_OBJ* cell = NULL); //@cmember Gestisce l'uscita delle celle (chiamata dal ) bool off_cell_handler(XI_OBJ* cell = NULL); // @cmember Ritorna il campo della maschera corrispondente alla cella dello // spreadsheet indicata da

TOperable_field* col2field(int pos) const; // @cmember Controlla se esiste il campo della maschera corrispondente alla cella dello // spreadsheet indicata da

TOperable_field* test_field(int pos) const; // @cmember Ritorna il campo della maschera corrispondente alla cella dello // spreadsheet indicata da

(chiama ) TOperable_field* cell2field(const XI_OBJ* cell) const; // @cmember Ritorna la posizione della colonna con identificatore

int cid2col(short cid) const; // @cmember Ritorna la colonna logica con identificatore

int cid2index(short cid) const; // @cmember Ritorna la colonna corrispondente al campo

della maschera int field2col(const TOperable_field* f) const; // @cmember Aggiorna il record sullo spreadsheet void update_rec(int rec); TOperable_field* field(short id) const; // @cmember Converte il numero del record nel corrispondente numero della riga int rec2row(int rec); // @cmember Converte il numero della riga nel corrispondente // numero del record int row2rec(int& row); // @cmember Setta la posizione (riga e colonna) del focus sullo spreadsheet. // Ritorna il numero del record corrente int set_pos(int row, int col) { _cur_col = col; _cur_row = row; return _cur_rec = row2rec(_cur_row); } // @cmember Chiama la funzione specificata con la bool notify(int row, KEY k); // @cmember Chiama la funzione specificata con la // ogni volta che c'e' una modifica nello spreadsheet void notify_change(); // @cmember Permette di fare tutti gli aggiornamenti necessari (indicati in //

) void on_idle(); // @cmember Cerca le proprieta' della riga r e volendo le crea pure TRow_property* get_property(int r, bool create = false); TSheet_field& owner() const { return (TSheet_field&)*_fld; } // @cmember Cerca la colonna col XI_OBJ* find_column(int col) const; // @access Public Member public: // @cmember Restituisce il numero di colonna corrente const int cur_column() const { return _cur_col; } // @cmember Modifica a video la riga void update(int row); // @cmember Ritorna la disabilitazione della colonna

bool column_disabled(int col) const { return _column_disabled[col]; } // @cmember Ritorna l' abilitazione della colonna

bool column_enabled(int col) const { return !column_disabled(col); } // @cmember Ritorna il contenuto della riga

-esima TToken_string& row(int n) { return _str.row(n); } // @cmember Aggiunge una riga allo spreadsheet passata come puntatore int add(const TToken_string& s) { return _str.add(s); } // @cmember Aggiunge una riga allo spreadsheet (vedi ) int add(TToken_string* s) { return _str.add(s); } // @cmember Inserisce un record in una posizione stabilita int insert(int rec, bool update_sheet, bool call_notify); // @cmember Elimina il record

bool destroy(int rec = -1, bool update_sheet = true); // @cmember Ritorna l'array di tutte le stringhe delle righe TString_array& rows_array() { return _str; } // @cmember Trova una colonna abilitata a partire da colonna int find_enabled_column(int rec, int colonna, int direction) const; // @cmember Trova un record abilitato a partire da rec int find_enabled_record(int rec, int direction) const; // @cmember Permette di mettere il focus su una cella bool set_focus_cell(int riga, int colonna); // @cmember Abilita/disabilita tutto lo spreadsheet (vedi ) void activate(bool on); // @cmember Permette di abilitare/disabilitare una colonna void enable_column(int col, bool on = true); // @cmember Permette di visualizzare/nascondere una colonna void show_column(int col, bool on = true); // @cmember Permette di eliminare una colonna dallo spreadsheet void delete_column(const int col) const; // @cmember Permette di attivare/ disattivare l'auto append void set_auto_append(bool on) { _auto_append = on;} // @cmember Permette di attivare la navigazione tra due colonne con invio void set_nav_column(short firstcol, short lastcol) { _first_nav_column_id = firstcol; _last_nav_column_id = lastcol; } // @cmember Permette di testare l'auto append bool auto_append() const { return _auto_append;} bool exist_column(const int col) const { return find_column(col) != NULL; } // @cmember Sposta la colonna dalla posizione

alla posizione //

void move_column(const int fromindex, const int toindex) const; // @cmember Permette di invertire la posizione di due colonne void swap_columns(const int fromid, const int toid) const; // @cmember Permette di invertire la posizione di due righe void swap_rows(const int fromindex, const int toindex); // @cmember Sposta la riga fromindex alla posizione toindex void move_row(const int fromindex, const int toindex); // @cmember L'utente ha salvato la disposizione delle colonne? bool user_saved_columns_order() const; // @cmember Salva la disposizione delle colonne void save_columns_order() const; // @cmember Salva la disposizione delle colonne void load_columns_order(); // @cmember Setta la disposizione delle colonne void set_columns_order(); // @cmember Setta la disposizione delle colonne void set_columns_order(TToken_string* order); static int set_line_number_width(int digits); // @cmember Setta la larghezza della colonna void set_column_width(const int col, const int width) const; // @cmember Aggiorna la larghezza di default della colonna void update_column_default_width(const int col) const; // @cmember Setta il titolo della colonna void set_column_header(const int col, const TString& header) const; // @cmember Getta il titolo della colonna const char* get_column_header(const int col) const; // @cmember Setta l'allineamento di una colonna void set_column_justify(int col, bool right); // @cmember Setta l'altezza della riga void set_row_height(const int row, const int height); // @cmember Permette di abilitare/disabilitare una singola cella void enable_cell(int row, int column, bool on = true); // @cmember Controlla se una cella e' disabilitata bool cell_disabled(int row, int column) const; // @cmember Setta i colori di una riga void set_back_and_fore_color(COLOR back, COLOR fore, int row, int col); // @cmember Legge i colori di una riga bool get_back_and_fore_color(COLOR& back, COLOR& fore, int row, int col); // @cmember Ritorna la maschera che appartiene allo spreadsheet TMask& sheet_mask() const; // @cmember Ritorna la maschera cui appartiene lo spreadsheet TMask& mask() const; // @cmember Ricopia i campi della maschera nel record

-esimo ed // aggiorna il display void mask2str(int n); // @cmember Ricopia i campi del record

-esimo nella maschera ed // aggiorna il display void str2mask(int n); // @cmember Apre la maschera della riga

-esima editando la riga KEY edit(int n); // @cmember Ritorna il numero di righe dello sheet int items() const { return _str.items(); } // @cmember Ritorna il record corrente int selected() const { return _cur_rec; } // @cmember Seleziona una riga dandogli il focus void select(int row, bool scrollto) { select(row, -1, scrollto); } // @cmember Seleziona una riga e una colonna dandogli il focus void select(int row, int col, bool scrollto); // @cmember Ritorna il numero di colonne presenti nello spreadsheet int columns() const { return _columns; } // @cmember Seleziona una riga appena possibile void post_select(int r); // @cmember Controlla se e' stato modificato una cella dello spreadsheet byte dirty() const { return owner().dirty(); } // @cmember Permette di indicare se e' stata modificata una cella dello spreadsheet void set_dirty(byte spork = true) { owner().set_dirty(spork); _row_dirty = _cell_dirty = spork!=0; } // @cmember Ritorna il valore della variabile active bool active() const { return _active; } // @cmember Ritorna se e' possibile lasciare il focus dallo spreadsheet (true se e' possibile) bool test_focus_change(); // @cmember Setta il membro

al valore

void set_notify(SPREADSHEET_NOTIFY n) { _notify = n; } // @cmember aggiunge una riga bool add_row_auto(); bool error_box(const char* msg); bool point2cell(const PNT& pnt, short& id, long& row) const; // @cmember Costruttore TSpreadsheet(WINDOW parent, short dlg, short x, short y, short dx, short dy, const char* maskname, int maskno, const char* head, TSheet_field* owner); // @cmember Distruttore virtual ~TSpreadsheet(); }; int TSpreadsheet::ROW_NUMBER_WIDTH = 3; KEY TSpreadsheet::barcode_newline() const { static KEY _barcode_newline = ini_get_int(CONFIG_INSTALL, "Main", "BarcodeNewline"); return _barcode_newline; } // @doc INTERNAL // @mfunc Costruttore TSpreadsheet::TSpreadsheet( WINDOW parent, // @parm Finestra alla quale appartiene lo spreadsheet short dlg, // @parm Identificatore del campo 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* maskname, // @parm Nome del file della maschera int maskno, // @parm Numero identificativo della maschera nel file const char* head, // @parm Titolo delle colonne TSheet_field* o) // @parm Indica il campo della maschera che contiene lo spreadsheet : _mask(), _active(true), _notify(NULL), _edit_field(NULL), _cur_row(0), _cur_rec(0), _cur_col(1), _row_dirty(false), _cell_dirty(false), _check_enabled(true), _needs_update(-1), _selection_posted(-1), _ignore_button(0), _save_columns_order(false), _f9_target(NULL), _auto_append(false), _first_nav_column_id(-1), _last_nav_column_id(-1) { int m_width[MAX_COL], v_width[MAX_COL]; int fixed_cols = 0; // Number of fixed columns int lines_in_header = 1; // Number of header lines TControl::_fld = o; _mask.set_sheet(o); // Collega la maschera allo sheet _mask.read_mask(maskname, maskno, 0); // Legge la maschera // Calcolo larghezza massima tabella TToken_string header(head); TToken_string new_header(256); int i = 0, tot_width = ROW_NUMBER_WIDTH+1; int max_width = tot_width<<1; // Stima larghezza della colonna piu' grande unsigned char f_width[MAX_COL]; // Larghezza colonne fisse const char * h; for (h = header.get(); h; h = header.get(), i++) { CHECKD(i < MAX_COL, "Tu meni calumns in scit: ", i); const int cid = FIRST_FIELD+i; // Column & Field ID const TOperable_field & f = (TOperable_field &) _mask.field(cid); // Field on mask TString testa(h); char* t = testa.get_buffer(); for (int c = 0; t[c]; c++) { if (t[c] == '\\' && (t[c+1] == 'n' || t[c+1] == 'r')) { t[c] = '\n'; strcpy(t+c+1, t+c+2); lines_in_header = 2; } } const int at = testa.find('@'); const int m = f.size(); // Memory width int v = m; // Video width if (at >= 0) { const TString& wi = testa.mid(at+1); v = atoi(wi) ; if (v == 0) v = m; if (f.has_query_button()) v++; if (wi.find('F') >= 0) { f_width[fixed_cols] = v+1; fixed_cols++; } testa.cut(at); } else { v = max(testa.len(), m+(f.has_query_button() ? 1 : 0)); } if (v > 69) v = 69; m_width[i] = m+1; // m = number of allowed chars v_width[i] = v+1; // v = width of column if (v >= max_width) max_width = v+1; tot_width += v_width[i]; new_header.add(testa); } _columns = i; 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 posso bloccare anche questa colonna int f_totwidth=4+3; for (int fc = 0; fc < fixed_cols; fc ++) { f_totwidth += f_width[fc]; if ((f_totwidth+max_width)*XI_FU_MULTIPLE >= rct.right) { fixed_cols = fc; break; } } XI_OBJ_DEF* listdef = xi_add_list_def(NULL, dlg, rct.top, rct.left, rct.bottom-rct.top, XI_ATR_ENABLED | XI_ATR_VISIBLE, NORMAL_COLOR, NORMAL_BACK_COLOR, // normal NORMAL_COLOR, DISABLED_BACK_COLOR, // disabled FOCUS_COLOR, // active 0); CHECK(listdef, "Can't create spreadsheet definition"); listdef->app_data = (long)this; XI_LIST_DEF* l = listdef->v.list; l->width = rct.right - rct.left; 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->scroll_bar = true; l->scroll_bar_button = true; l->fixed_columns = fixed_cols+1; l->active_back_color = FOCUS_BACK_COLOR; l->white_space_color = MASK_DARK_COLOR; l->rule_color = MASK_DARK_COLOR; #ifdef LINUX l->scroll_on_thumb_track = true; #endif // Definizione della prima colonna (numero di riga) word attr = XI_ATR_RJUST; if (sheet_mask().id2pos(FIRST_FIELD-1) >= 0) attr |= XI_ATR_SELECTABLE; XI_OBJ_DEF* coldef = xi_add_column_def(listdef, 0, attr, 0, ROW_NUMBER_WIDTH * XI_FU_MULTIPLE, ROW_NUMBER_WIDTH+1, (char *)((attr & XI_ATR_SELECTABLE) ? "X" : "")); coldef->app_data = (long)this; coldef->v.column->heading_platform = true; coldef->v.column->column_platform = true; if (attr & XI_ATR_SELECTABLE) { coldef->v.column->icon_rid = ICO_SEARCH; coldef->v.column->icon_x = -4; // l'icona e' 32x32 ma e' disegnata solo al centro coldef->v.column->icon_y = -8; if (listdef->v.list->min_heading_height < 20) listdef->v.list->min_heading_height = 20; } else coldef->v.column->center_heading = true; for (h = new_header.get(0), i = 0; h; h = new_header.get(), i++) { const TString testo(h); const int cid = FIRST_FIELD+i; // Column & Field ID const TOperable_field & f = (const TOperable_field &)_mask.field(cid); // Field on mask const int acqua = f.class_id(); long flags = XI_ATR_EDITMENU | XI_ATR_AUTOSCROLL | XI_ATR_FOCUSBORDER; if (AUTOSELECT) flags |= XI_ATR_AUTOSELECT; switch (acqua) { case CLASS_EDIT_FIELD: if (f.right_justified()) flags |= XI_ATR_RJUST; break; case CLASS_REAL_FIELD: case CLASS_CURRENCY_FIELD: flags |= XI_ATR_RJUST; break; case CLASS_BOOLEAN_FIELD: flags |= XI_ATR_SELECTABLE; break; default: break; } if (f.active()) flags |= XI_ATR_ENABLED; else _column_disabled.set(i); coldef = xi_add_column_def(listdef, cid, flags, cid, v_width[i] * XI_FU_MULTIPLE, m_width[i], (char*)(const char*)testo); coldef->app_data = (long)this; coldef->v.column->heading_platform = true; coldef->v.column->center_heading = true; if (flags & XI_ATR_SELECTABLE) coldef->v.column->icon_mode = XIM_ICON_HAS_PRIORITY; } // Create the whole thing! _obj = xi_create(itf, listdef); xi_dequeue(); // Flush events in XOL xi_tree_free(listdef); // Free definitions CHECKD(_obj, "Can't create spreadsheet ", owner().dlg()); update_tab_cid(); int num; XI_OBJ** column = xi_get_member_list(_obj, &num); memset(_default_width, 0, sizeof(_default_width)); for (i = 0; i < num; i++) { XI_RCT rct; xi_get_rect(column[i], &rct); _default_width[i] = rct.right - rct.left; } } TSpreadsheet::~TSpreadsheet() { } TMask& TSpreadsheet::sheet_mask() const { return ((TSpreadsheet*)this)->_mask; } // Converts a row number in the correspondig record number int TSpreadsheet::row2rec(int& row) { int rows; const long* handle = xi_get_list_info(_obj, &rows); if (rows <= 0) return -1; // Empty sheet if (row < 0) { row = 0; } else { if (row >= rows) row = rows-1; } const int r = (int)handle[row]; CHECKD(r >= 0 && r < items(), "Sheet line out of range: ", row); return r; } // Converts a row number in the correspondig record number int TSpreadsheet::rec2row(int record) { 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; } // Retrieves the corresponding field of the mask from a spredsheet cell TOperable_field* TSpreadsheet::test_field(int pos) const { TOperable_field* good = NULL; if (pos > 0) { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); CHECKD(pos >= 0 && pos < num, "Bad column number", pos); for (short id = column[pos]->cid; ; id += 100) { TOperable_field* f = field(id); if (f == NULL) break; // Search failed good = f; // We've found a field with the proper ID ... if (f->active()) break; // ... and it's active: end of search } } else good = field(DLG_USER); return good; } // Retrieves the corresponding field of the mask from a spredsheet cell TOperable_field* TSpreadsheet::col2field(int pos) const { TOperable_field* good = test_field(pos); CHECKD(good, "Can't find field corresponding to column ", pos); return good; } // Retrieves the corresponding field of the mask from a spredsheet cell TOperable_field* TSpreadsheet::cell2field(const XI_OBJ* cell) const { XI_OBJ cella; if (cell == NULL) { XI_MAKE_CELL(&cella, _obj, _cur_row, _cur_col); cell = &cella; } return col2field(cell->v.cell.column); } int TSpreadsheet::cid2col(short cid) const { CHECKD(cid >= FIRST_FIELD, "Bad column id ", cid); int num; XI_OBJ** column = xi_get_member_list(_obj, &num); for (int c = num-1; c > 0; c--) { if (column[c]->cid == cid) return c; } return 0; } int TSpreadsheet::cid2index(short cid) const { CHECKD(cid >= FIRST_FIELD, "Bad column id ", cid); return (cid % 100) - 1; } int TSpreadsheet::field2col(const TOperable_field* f) const { const short cid = FIRST_FIELD + cid2index(f->dlg()); return cid2col(cid); } void TSpreadsheet::update_rec(int rec) { const int riga = rec2row(rec); if (riga >= 0) { const bool has_focus = rec == selected() && mask().focus_field().dlg() == owner().dlg(); // if (has_focus) xi_set_focus(get_interface()); XI_OBJ row; XI_MAKE_ROW(&row, _obj, riga); xi_cell_request(&row); // Update internal values if (has_focus) { str2mask(_cur_rec); set_focus_cell(riga, _cur_col); } } if (_needs_update == rec) _needs_update = -1; } // Cerca una colonna abilitata a partire da colonna // La prima cella utilizzabile ha indice 1 // rec e' un numero di record assoluto // colonna e' un numero di colonna a video: puo' succedere che la 3 corrisponda al campo 107 int TSpreadsheet::find_enabled_column(int rec, int colonna, int direction) const { CHECKD(direction == +1 || direction == -1, "Bad column search direction", direction); const TRow_property* prop = ((TSpreadsheet*)this)->get_property(rec); if (prop != NULL && prop->height() == 0) return 0; int num; XI_OBJ** column = xi_get_member_list(_obj, &num); if (colonna <= 0 || colonna >= num) colonna = 1; int c = colonna; do { const short n = column[c]->cid - FIRST_FIELD; if (!cell_disabled(rec, n)) return c; c += direction; if (c >= num) c = 1; else if (c <= 0) c = num-1; } while (c != colonna); return 0; } int TSpreadsheet::find_enabled_record(int rec, int direction) const { for (int r = rec+direction; r >= 0 && r < items(); r += direction) { if (find_enabled_column(r, 1, +1) > 0) return r; } return -1; } // riga (da 0), colonna (0 = numero, 1 = prima cella, ...) bool TSpreadsheet::set_focus_cell(int riga, int colonna) { // xi_set_focus(get_interface()); const int rec = row2rec(riga); if (rec < 0 || rec >= items()) return false; colonna = find_enabled_column(rec, colonna, +1); if (colonna > 0) { WINDOW win = xvt_scr_get_focus_vobj(); // Puo' essere NULL per cui poi non funziona ... WINDOW par = parent(); // ... la xi_set_focus(&cell) if (win != par) { xvt_scr_set_focus_vobj(par); xvt_vobj_raise(par); } XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, riga, colonna); xi_set_focus(&cell); if (rec != _cur_rec) { _cur_rec = rec; _cur_row = riga; _row_dirty = false; } _edit_field = col2field(_cur_col = colonna); // qui } return colonna > 0; } // @doc INTERNAL // @mfunc Inserisce un record in una posizione stabilita // // @rdesc Ritorna la posizione nella quale e' stato inserito il record. Se non riesce ad inserirlo // ritorna -1. int TSpreadsheet::insert( int rec, // @parm Numero del record da inserire nello spreadsheet bool update_sheet, // @parm Chiama cell request bool call_notify) // @parm Chiama funzione di notify // @comm Non e' possibile inserire un nuovo record nel caso nello spreadsheet vi siano // almeno 9999 righe oppure se lo spreadsheet non e' attivo. { static bool ininsert = false; // TMask & m = owner().mask(); verificare if (ininsert || items() >= 9999) return -1; ininsert = true; int r = rec < 0 ? items() : rec; bool ok = call_notify ? notify(r, K_INS) : true; if (ok) { TToken_string* toktok = new TToken_string(80, owner().separator()); r = _str.insert(toktok, rec); _property.insert(NULL, r, true); // Crea lo spazio necessario per le proprieta' // Notifica che l'inserimento e' terminato owner().post_insert(r); xi_insert_row(_obj, INT_MAX); if (call_notify) notify(r, K_CTRL + K_INS); if (update_sheet) { xi_cell_request(_obj); if (_selection_posted < 0) post_select(r); } } else r = -1; ininsert = false; return r; } // @doc INTERNAL // @mfunc Elimina una riga // // @rdesc Ritorna il risultato dell'operazione: // // @flag true | Se la riga esisteve e quindi e' stata eliminata // @flag false | Se la riga non esisteve bool TSpreadsheet::destroy( int rec, // @parm Numero della riga da eliminare bool update_sheet) // @parm Aggiornamento visuale dell sheet // @comm Se il parametro

assume valore -1 vengono eliminate tutte le righe presenti // nello spreadsheet { static bool indestroy = false; if ( indestroy ) return false; indestroy = true; bool ok = true; if (rec < 0) { _str.destroy(); _property.destroy(); set_dirty(_row_dirty = false); } else { _property.destroy(rec, true); // Destroy line info ok = _str.destroy(rec, true); // Destroy line } if (ok && mask().is_running() && update_sheet) update(-1); indestroy = false; return ok; } // @doc INTERNAL // @mfunc Modifica a video la riga void TSpreadsheet::update( int rec) // @parm Numero della riga da modificare // @comm Se il valore di

e' minore di 0 viene aggiornato l'intero spreadsheet { if (rec < 0) { int num = 0; const long* handle = xi_get_list_info(_obj, &num); int first = 0, last = 0; bool scroll = items() == 0; // || !owner().mask().is_running(); if (!scroll) { xi_get_visible_rows(_obj, &first, &last); scroll = items() <= handle[first]; } if (scroll) xi_scroll(_obj, XI_SCROLL_FIRST); else { const long as = AUTOSELECT ? XI_ATR_AUTOSELECT : 0; xi_scroll_rec(_obj, handle[first], NORMAL_COLOR, XI_ATR_ENABLED | as, 0); } _needs_update = -1; // Clear pending row update } else update_rec(rec); } void TSpreadsheet::notify_change() { if (_cur_rec >= 0) { if (!_row_dirty) { str2mask(_cur_rec); _edit_field = cell2field(NULL); // Ricalcola correttamente il campo corrente notify(_cur_rec, K_SPACE); xvtil_statbar_refresh(); set_dirty(); } _row_dirty = _cell_dirty = true; // Era tra le graffe } } const char* TSpreadsheet::copy_cell2field(XI_OBJ* cell) { const char* txt; if (cell == NULL) { XI_OBJ cella; XI_MAKE_CELL(&cella, _obj, _cur_row, _cur_col); txt = xi_get_text(&cella, NULL, -1); } else txt = xi_get_text(cell, NULL, -1); if (_edit_field->is_editable()) { if (_edit_field->class_id() == CLASS_ZOOM_FIELD) _edit_field->set(row(_cur_rec).get(cid2index(_edit_field->dlg()))); const char* val = (const char*)((TEditable_field*)_edit_field)->win2raw(txt); _edit_field->set(val); _edit_field->set_dirty(); // Get it dirty! } return _edit_field->get(); } bool TSpreadsheet::off_cell_handler(XI_OBJ *cell) { bool ok = true; if (_edit_field != NULL) { const char* nuo = copy_cell2field(cell); if (_edit_field->on_key(_edit_field->is_edit() ? K_TAB : K_SPACE) == false) // Test it ok = *nuo == '\0'; // Se e' vuoto lascia stare else _cell_dirty = false; if (_row_dirty) { owner().mask().notify_focus_field(id()); // A volte si diverte a perdere il focus mask2str(_cur_rec); // Update sheet row } } return ok; } bool TSpreadsheet::test_focus_change() { bool ok = true; if (_cell_dirty) ok = off_cell_handler(); if (ok) ok = xi_move_focus(get_interface()) ? true : false; return ok; } // Certified 75% bool TSpreadsheet::event_handler(XI_OBJ* itf, XI_EVENT *xiev) { // static KEY _lastab = K_TAB; verificare static clock_t digit_timer = 0; BOOLEAN& refused = xiev->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; const TRow_property* prop = get_property(0); if (prop != NULL && prop->height() >= 0) xiev->v.rec_request.row_height = prop->height(); } else refused = true; break; case XIE_GET_LAST: { xiev->v.rec_request.data_rec = items()-1; const TRow_property* prop = get_property(xiev->v.rec_request.data_rec); if (prop != NULL && prop->height() >= 0) xiev->v.rec_request.row_height = prop->height(); } 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; const TRow_property* prop = get_property(n); if (prop != NULL && prop->height() >= 0) xiev->v.rec_request.row_height = prop->height(); } } break; case XIE_CELL_REQUEST: { const long& rec = xiev->v.cell_request.rec; const short& maxlen = xiev->v.cell_request.len; if (rec < 0 || rec >= items() || maxlen <= 0) // Puo' succedere: strano ma vero! { refused = true; return false; } const char* src = NULL; int nm; XI_OBJ** obj = xi_get_member_list(xiev->v.cell_request.list, &nm); const short& num = xiev->v.cell_request.col_nbr; const int cid = obj[num]->cid; if (cid >= FIRST_FIELD) { if (rec < items()) { const int col = cid - FIRST_FIELD; const int curr = _cur_rec; _cur_rec = rec; // Memorizzo il record corrente per calcolare il campo correttamente const TOperable_field* f = field(cid); _cur_rec = curr; // Lo ripristino dopo il calcolo if (f != NULL && f->is_editable()) { const TEditable_field* e = (const TEditable_field*)f; TToken_string& rowrec = row(rec); src = rowrec.get(col); // Set value for cell if (src && *src) { switch (e->class_id()) { case CLASS_LIST_FIELD: { const TList_field& lst = *(TList_field*)f; TToken_string codes = lst.get_codes(); TToken_string values = lst.get_values(); src = xvtil_get_cell_selection(obj[num], codes.get_pos(src), codes, values); } break; case CLASS_BOOLEAN_FIELD: if (*src <= ' ') xiev->v.cell_request.icon_rid = ICO_CHECK_OFF; else xiev->v.cell_request.icon_rid = ICO_CHECK_ON; break; case CLASS_CURRENCY_FIELD: if (e->driver(0)) { TOperable_field* driver = e->driver(0); if (driver->parent() == e->parent()) { const real r(src); // Memorizzo valore numerico const int pos = driver->dlg() - FIRST_FIELD; const TString4 codval = rowrec.get(pos); // Codice valuta const TCurrency c(r, codval, ZERO, e->uppercase()); src = c.string(true); break; } } //Fall down as usual default: src = e->raw2win(src); // Get formatted string break; } } else { if (e->class_id() == CLASS_BOOLEAN_FIELD) xiev->v.cell_request.icon_rid = ICO_CHECK_OFF; } if (cell_disabled(rec, col)) { xiev->v.cell_request.back_color = DISABLED_BACK_COLOR; xiev->v.cell_request.color = DISABLED_COLOR; xiev->v.cell_request.attrib &= ~XI_ATR_ENABLED; } else { // Non impostare il colore per la cella correntemente col focus if (rec != _cur_rec || num != _cur_col) { if (f->required()) xiev->v.cell_request.back_color = REQUIRED_BACK_COLOR; else get_back_and_fore_color(xiev->v.cell_request.back_color, xiev->v.cell_request.color, rec, col); } if (e->has_query_button()) // Metto il bottone sulle celle attive { xiev->v.cell_request.button = true; xiev->v.cell_request.button_on_focus = true; // Imposto l'icona della lente per ricerche normali/customizzate sui campi testo if (e->is_edit()) xiev->v.cell_request.button_icon_rid = ICO_SEARCH; } } } else { xiev->v.cell_request.back_color = DISABLED_BACK_COLOR; xiev->v.cell_request.color = DISABLED_COLOR; xiev->v.cell_request.attrib &= ~XI_ATR_ENABLED; } } } else src = format("%d", rec+1); // Numero riga char* dst = xiev->v.cell_request.s; if (src && *src) { strncpy(dst, src, maxlen); dst[maxlen-1] = '\0'; } else *dst = '\0'; } break; case XIE_CHG_CELL: if (_edit_field && !_cell_dirty) { notify_change(); _cell_dirty = true; _edit_field->set_dirty(); } break; case XIE_BUTTON: if (_check_enabled) { if (xiev->v.xi_obj->type == XIT_CELL) // Bottone della cella { on_idle(); // Termina tutti gli eventuali update in corso const XI_CELL_DATA& cell = xiev->v.xi_obj->v.cell; int num; XI_OBJ** column = xi_get_member_list(_obj, &num); CHECK(cell.column < num, "Bad column"); int row = cell.row; const int rec = row2rec(row); const int col = column[cell.column]->cid - FIRST_FIELD; if (!cell_disabled(rec, col)) { if (xi_move_focus(xiev->v.xi_obj)) { if (clock() > _ignore_button) dispatch_e_char(parent(), K_F9); _ignore_button = 0; } } } else add_row_auto(); } break; case XIE_SELECT: if (xiev->v.xi_obj->type == XIT_ROW) { _check_enabled = false; refused = true; if (!test_focus_change()) break; TOperable_field* f = test_field(xiev->v.select.column); if (f == NULL) break; //se clicco su una cella non associata ad un campo esce const int oldrec = _cur_rec; const int record = set_pos(xiev->v.select.xi_obj->v.row, _cur_col); if (oldrec != _cur_rec) { _row_dirty = _cell_dirty = false; on_idle(); // Forces update delayed by str2mask _cur_rec = record; // Forces correct record (may be changed by on_idle!) notify(_cur_rec, K_TAB); const int riga = xiev->v.select.xi_obj->v.row; const int colonna = find_enabled_column(_cur_rec, 1, +1); //set_pos(riga, colonna); set_focus_cell(riga, colonna); } if (xiev->v.select.column > 0) { if (!cell_disabled(record, cid2index(f->dlg()))) { notify_change(); if (f->get().blank()) f->set("X"); else f->set(""); f->on_key(K_SPACE); mask2str(_cur_rec); on_idle(); _cell_dirty = false; // Non e' necessario lasciare dirty la cella in quanto mask2str e' gia' fatta } } else { TMask& sm = owner().sheet_mask(); const int button_pos = sm.id2pos(FIRST_FIELD-1); if (button_pos >= 0) { TMask_field& button = sm.fld(button_pos); if (button.active()) { button.disable(); // Impedisce che un doppio click indesiderato abbia effetto if (!_row_dirty) { notify_change(); _row_dirty = _cell_dirty = false; FOR_EACH_MASK_FIELD(sm, i, fld) fld->set_dirty(false); } button.on_hit(); if (sm.dirty()) { _row_dirty = true; mask2str(_cur_rec); } button.enable(); } owner().highlight(); } } _check_enabled = true; } break; case XIE_DBL_CELL: if (!_cell_dirty || off_cell_handler()) { _check_enabled = false; const int oldrec = _cur_rec; if ( xiev->v.xi_obj != NULL ) { // set_pos(xiev->v.xi_obj->v.cell.row, xiev->v.xi_obj->v.cell.column); const XI_CELL_DATA& cell = xiev->v.xi_obj->v.cell; const bool ok = set_focus_cell(cell.row, cell.column); if (!ok) { _check_enabled = true; refused = true; return false; } } if (oldrec != _cur_rec || !_row_dirty) { _row_dirty = false; notify_change(); _cell_dirty = false; } const KEY k = edit(_cur_rec); if (k == K_ENTER) _row_dirty = true; else if (k == K_DEL) { _row_dirty = _cell_dirty = false; if (_cur_rec >= items()) { _row_dirty = _cell_dirty = false; _cur_rec = items()-1; _cur_row = 0; _cur_col = 1; } if (_cur_rec >= 0 && _cur_rec < items()) set_focus_cell(_cur_row, _cur_col); } else if (k == K_ESC) { XI_OBJ row; XI_MAKE_ROW(&row, _obj, _cur_row); xi_cell_request(&row); } _check_enabled = true; } break; case XIE_ON_LIST: owner().mask().notify_focus_field(id()); break; case XIE_OFF_LIST: on_idle(); break; case XIE_ON_ROW: if (_check_enabled) { const int row = xiev->v.xi_obj->v.row; // Riga in cui si sta entrando int rows; // Numero totale di righe attive const long* handle = xi_get_list_info(_obj, &rows); // Calcola il numero del record corrispondente alla riga const bool exist = row >= 0 && row < rows; int next_rec = exist ? (int)handle[row] : items()-1; // Se la riga non esiste o non ha nessuna cella valida abilitata ... // ... cerca la prossima riga valida e rifiuta l'ingresso in questa if (!exist || find_enabled_column(next_rec, 1, +1) <= 0) { next_rec = find_enabled_record(next_rec, next_rec >= _cur_rec ? +1 : -1); post_select(next_rec); refused = true; break; } if (ENTER_AS_TAB && _first_nav_column_id > 0) { const int fcol = cid2col(_first_nav_column_id); const int lcol = cid2col(_last_nav_column_id); const int mcol = min(fcol, lcol); if (_cur_col != mcol) { _cur_col = mcol; set_focus_cell(xiev->v.xi_obj->v.row, _cur_col); } } // Setta _cur_rec in base a alla riga e cella correnti set_pos(xiev->v.xi_obj->v.row, _cur_col); if (_cur_rec < items() && notify(_cur_rec, K_TAB)) { /* Guy! str2mask(_cur_rec); */ _row_dirty = _cell_dirty = false; _ignore_button = clock()+250; // Ignora i click sui bottoni (invisibili) per un quarto di secondo } else { _cur_row = _cur_rec = 0; refused = true; } } break; case XIE_OFF_ROW: if (_check_enabled) { _check_enabled = false; // Avoid recursion! if (_row_dirty && active()) { bool ok = owner().sheet_mask().check_fields(); if (ok) { mask2str(_cur_rec); // Update sheet with mask contents ok = notify(_cur_rec, K_ENTER); // Notify edit _row_dirty = false; // Avoid double notifications! } if (!ok) refused = true; } if (!refused) // Notifica l'abbandono della riga refused = !notify(_cur_rec, K_CTRL+K_TAB); _check_enabled = true; } break; case XIE_ON_CELL: if (_check_enabled) { const int physical_column = xiev->v.xi_obj->v.cell.column; TOperable_field* f = test_field(physical_column); bool disabled = true; if (f) { const int logical_column = (f->dlg()-FIRST_FIELD) % 100; disabled = cell_disabled(_cur_rec, logical_column); // If the cell is disabled ... } if (disabled) { const int dir = physical_column >= _cur_col ? +1 : -1; const int nex = find_enabled_column(_cur_rec, physical_column, dir); if (nex > 0) // If at least one enabled cell exists set_focus_cell(_cur_row, nex); refused = true; } else { _edit_field = f; _cur_col = physical_column; _edit_field->set_focusdirty(_cell_dirty = false); // Azzera il flag di update_pending //#ifdef XI_R4 XinEvent e; e.type = XinEventPaint; xi_eh(_obj->itf->v.itf->xin_win, &e); /* #else EVENT e; e.type = E_UPDATE; xi_eh(_obj->itf->v.itf->xvt_win, &e); #endif */ owner().mask().notify_focus_field(id()); // Non si ripete mai abbastanza! } XI_OBJ row; XI_MAKE_ROW(&row, _obj, _cur_row); xi_cell_request(&row); // Update internal values } break; case XIE_OFF_CELL: if (_edit_field && _check_enabled && _cell_dirty) { _check_enabled = false; XI_OBJ* cell = xiev->v.xi_obj; refused = !off_cell_handler(cell); _check_enabled = true; } if (! refused) { const int col = _cur_col; _cur_col = -1; XI_OBJ row; XI_MAKE_ROW(&row, _obj, _cur_row); xi_cell_request(&row); // Update internal values _cur_col = col; } 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; else _save_columns_order = true; break; case XIE_COL_SIZE: _save_columns_order = 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 = int(rec * 100L / n); } break; case XIE_CLEANUP: break; case XIE_CHAR_CELL: if (_edit_field) { KEY k = xiev_to_key(xiev); if (xvt_chr_is_alnum(k)) digit_timer = clock(); else { if (k == barcode_newline()) { const clock_t delay = clock() - digit_timer; if (delay <= 60) k = K_CTRL + '+'; // impedisce ulteriori elaborazioni } } switch(k) { case K_F2: _cell_dirty = true; case K_F8: case K_F9: if (_f9_target != NULL) { _edit_field = _f9_target; _f9_target = NULL; } if (_edit_field != NULL) { const bool spork = _edit_field->dirty() != 0; // TBT notify_change(); //gestione campi che non appaiono nello sheet ma che possono essere usati nelle ricerche //es: per evitare di avere il valore della prima colonna in una descrizione che si puo' usere.. //..per le ricerche, ma che non appare nello sheet (es. ca2100a.uml) if (cid2index(_edit_field->dlg()) == 0 || _cur_col > 0) copy_cell2field(); _edit_field->set_dirty(_cell_dirty = spork); // TBT } case K_F11: if (_check_enabled && active()) { _check_enabled = false; // Disable checks notify_change(); bool ok = true; if (k == K_F9 && _edit_field->is_kind_of(CLASS_LIST_FIELD)) // list or radio { XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, _cur_row, _cur_col); const TList_field& lst = (const TList_field&)(*_edit_field); const int sel = xvtil_drop_down_list(&cell, lst.get_codes(), lst.get_values()); if (sel >= 0) copy_cell2field(&cell); _ignore_button = clock() + 250; } else // edit_field { // Notifica il corretto campo col focus sulla maschera sheet_mask().notify_focus_field(_edit_field->dlg()); ok = _edit_field->on_key(k); _cell_dirty = _edit_field->dirty() != 0; // TBT _f9_target = NULL; if (!ok && k == K_F9) // Ricerca non completata? { TOperable_field* target = &owner().sheet_mask().focus_field(); if (_edit_field != target) // Ricerca alternativa { _f9_target = target; _cur_col = field2col(_f9_target); dispatch_e_char(parent(), K_F9); } } } if (ok) { mask2str(_cur_rec); on_idle(); // Update immediately! } else set_focus_cell(_cur_row, _cur_col); _check_enabled = true; // Re-enable checks } break; case K_CTRL + '-': // ********* delete line if (_check_enabled) { _check_enabled = false; // Disable checks notify_change(); if (sheet_mask().id2pos(DLG_DELREC)>=0 && sheet_mask().field(DLG_DELREC).enabled() && notify(_cur_rec, K_DEL)) { int rec = _cur_rec; _row_dirty = _cell_dirty = false; destroy(rec); if (rec < items()) str2mask(rec); notify(rec, K_CTRL+K_DEL); // Notifica l'avvenuta cancellazione set_dirty(); // Setta il flag sheet modificato _row_dirty = _cell_dirty = false; // Resetta i flag di modifica appena settati di riflesso if (rec >= items()) rec = items() - 1; if (rec >= 0) select(rec, false); } refused = true; _check_enabled = true; // Re-enable checks } break; case K_CTRL + '+': // ********* insert line add_row_auto(); refused = true; break; case K_CTRL + 'A': { _check_enabled = false; if (_cell_dirty) off_cell_handler(); notify_change(); // CM500244 const KEY k = edit(_cur_rec); if (k == K_ENTER) _row_dirty = true; else if (k == K_DEL) { _row_dirty = _cell_dirty = false; if (_cur_rec >= items()) { _row_dirty = _cell_dirty = false; _cur_rec = items()-1; _cur_row = 0; _cur_col = 1; } if (_cur_rec >= 0 && _cur_rec < items()) set_focus_cell(_cur_row, _cur_col); } else if (k == K_ESC) { XI_OBJ row; XI_MAKE_ROW(&row, _obj, _cur_row); xi_cell_request(&row); } _check_enabled = true; refused = true; } break; case K_CTRL + 'B': { _check_enabled = false; if (_cell_dirty) off_cell_handler(); const int button_pos = sheet_mask().id2pos(FIRST_FIELD-1); if (button_pos >= 0) { TMask & sm = owner().sheet_mask(); TMask_field& button = sm.fld(button_pos); if (button.active()) { notify_change(); button.on_hit(); if (sm.dirty()) mask2str(_cur_rec); } } _check_enabled = true; owner().highlight(); refused = true; } break; case K_CTRL+K_PREV: xi_scroll(_obj, XI_SCROLL_PGUP); break; case K_CTRL+K_NEXT: xi_scroll(_obj, XI_SCROLL_PGDOWN); break; case K_CTRL+K_HOME: xi_scroll(_obj, XI_SCROLL_FIRST); break; case K_CTRL+K_END: xi_scroll(_obj, XI_SCROLL_LAST); break; default: if (k > K_CTRL) { refused = true; } else if (_edit_field->is_kind_of(CLASS_LIST_FIELD)) { notify_change(); // Aggiorna valore corrente listbox TList_field& lst = *(TList_field*)_edit_field; bool changed = false; switch (k) { case K_RIGHT: changed = lst.select_next(); break; case K_LEFT : changed = lst.select_prev(); break; default : changed = (k >= ' ' && k <= 'z') && lst.select_by_initial(char(k)); break; } if (changed) { XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, _cur_row, _cur_col); xi_set_text(&cell, (char*)lst.raw2win(lst.get())); _cell_dirty = true; } refused = true; } else if (is_edit_key(k)) { if (AUTOZOOM) { if ((_edit_field->class_id() == CLASS_ZOOM_FIELD)) copy_cell2field(); } if (!_edit_field->on_key(k)) { refused = true; beep(); } } break; } } break; case XIE_XVT_EVENT: if (xiev->v.xvte.type == E_CHAR) { const KEY k = e_char_to_key(&xiev->v.xvte); switch (k) { case K_DOWN: if (_auto_append && _cur_rec == items() - 1) add_row_auto(); else { // Azzero il flag di pending paint che impedisce la navigazione! XinEvent e; e.type = XinEventPaint; xi_eh(_obj->itf->v.itf->xin_win, &e); } break; case K_ROWEDIT: xiev->type = XIE_DBL_CELL; xiev->v.xi_obj = NULL; event_handler(itf, xiev); break; case K_ENTER: case K_SHIFT+K_ENTER: { const int dir = k == K_ENTER ? +1 : -1; if (ENTER_AS_TAB) { int next_col = find_enabled_column(_cur_rec, _cur_col+dir, dir); while (next_col > 0 && col2field(next_col)->is_kind_of(CLASS_BOOLEAN_FIELD)) next_col = find_enabled_column(_cur_rec, next_col+dir, dir); if (_first_nav_column_id > 0) { const int fcol = cid2col(_first_nav_column_id); const int lcol = cid2col(_last_nav_column_id); if (k == K_ENTER) { const int mcol = max(fcol, lcol); if (next_col > mcol) next_col = -1; } else { const int mcol = min(fcol, lcol); if (next_col < mcol) next_col = _cur_col + 1; } } if (next_col != 0 && next_col != _cur_col && ((next_col>_cur_col)^(dir < 0))) { dispatch_e_char(parent(), k == K_ENTER ? K_TAB : K_BTAB); refused = true; } } if (!refused) { if (ENTER_AS_TAB && _first_nav_column_id > 0) { const int fcol = cid2col(_first_nav_column_id); const int lcol = cid2col(_last_nav_column_id); _cur_col = min(fcol, lcol); } if (_auto_append && k == K_ENTER && _cur_rec == items() - 1) { dispatch_e_char(parent(), K_DOWN); refused = true; } else { const int next_rec = find_enabled_record(_cur_rec, dir); if (next_rec >= 0) { dispatch_e_char(parent(), K_TAB); dispatch_e_char(parent(), k == K_ENTER ? K_DOWN : K_UP); refused = true; } else { dispatch_e_char(parent(), k == K_ENTER ? K_F3 : K_F4); refused = true; } } } } break; case K_ESC: if (xi_move_focus(get_interface())) { owner().mask().on_key(k); refused = true; } break; case K_CTRL + 'A': case K_CTRL + 'B': case K_CTRL + '-': case K_CTRL + '+': case K_CTRL+K_PREV: case K_CTRL+K_NEXT: case K_CTRL+K_HOME: case K_CTRL+K_END: break; default: if (k > K_CTRL) { if (xi_move_focus(get_interface())) owner().mask().on_key(k); refused = true; } break; } } break; default: break; } return !refused; } void TSpreadsheet::activate(bool on) { _active = on; const dword old = xi_get_attrib(_obj); dword att = on ? (old & ~XI_ATR_NAVIGATE) : (old | XI_ATR_NAVIGATE); if (old != att) { int num; XI_OBJ** columns = xi_get_member_list(_obj, &num); xi_move_focus(get_interface()); // Set focus to interface if (on) att |= XI_ATR_TABWRAP; else att &= ~XI_ATR_TABWRAP; xi_set_attrib(_obj, att); for (int col = 1; col < num; col++) { XI_OBJ* column = columns[col]; att = xi_get_attrib(column); if (on) { att &= ~XI_ATR_READONLY; if (AUTOSELECT) att |= XI_ATR_AUTOSELECT; } else { att |= XI_ATR_READONLY; att &= ~XI_ATR_AUTOSELECT; } xi_set_attrib(column, att); // Set new attributes } } } void TSpreadsheet::select(int rec, int col, bool scrollto) { if (col <= 0) { if (col < -1) //ciapino per far funzionare ve0 _cur_col = find_enabled_column(rec, 1, +1); col = _cur_col; } if (!scrollto) { int rows; const long* handle = xi_get_list_info(_obj, &rows); int first = 0, last = 0; xi_get_visible_rows(_obj, &first, &last); scrollto = rec < handle[first] || rec > handle[last]; } if (scrollto) { const long as = AUTOSELECT ? XI_ATR_AUTOSELECT : 0; xi_scroll_rec(_obj, rec, NORMAL_COLOR, XI_ATR_ENABLED | as, 0); } const int row = rec2row(rec); const bool has_focus = mask().focus_field().dlg() == owner().dlg(); if (has_focus) { _cur_rec = -1; set_focus_cell(row, col); } else { _cur_col = find_enabled_column(rec, 1, +1); _cur_rec = rec; _cur_row = row; _edit_field = _cur_col > 0 ? test_field(_cur_col) : NULL; str2mask(_cur_rec); _row_dirty = false; } notify(rec, K_TAB); } void TSpreadsheet::post_select(int rec) { _selection_posted = rec; } void TSpreadsheet::on_idle() { if (_needs_update >= 0) { if (_needs_update < items()) update_rec(_needs_update); _needs_update = -1; } if (_selection_posted >= 0) { const int next_row = _selection_posted; _selection_posted = -1; // if (next_row == _cur_rec) // return ; if (next_row < items()) select(next_row, 1, false); } } // @doc INTERNAL // @mfunc Cerca la colonna col XI_OBJ* TSpreadsheet::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; else if (col >= FIRST_FIELD+100) // Riportalo nel range 101 - 199 col = FIRST_FIELD + cid2index(col); int num; XI_OBJ** columns = xi_get_member_list(_obj, &num); int c; for (c = num-1; c >= 0; c--) { if (columns[c]->cid == col) break; } return c >= 0 ? columns[c] : NULL; } TMask& TSpreadsheet::mask() const { TMask* m = (TMask*)xvt_vobj_get_data(parent()); return *m; } // Ritorna il campo con l'identificatore dato della maschera dello sheet TOperable_field* TSpreadsheet::field(short id) const { const TMask & sm = owner().sheet_mask(); const int pos = sm.id2pos(id); return pos < 0 ? NULL : (TOperable_field*)&sm.fld(pos); } // Ricopia i campi della maschera nel record dato ed aggiorna il display void TSpreadsheet::mask2str(int rec) { if (rec >= 0 && rec < items()) { TToken_string& r = row(rec); owner().mask2row(rec, r); } if (_needs_update != rec) { if (_needs_update >= 0) update_rec(_needs_update); // Double update! _needs_update = rec; } } // Certified 50% // @doc INTERNAL // @mfunc Permette di abilitare/disabilitare una singola cella void TSpreadsheet::enable_cell( int row, // @parm Riga della cella da abilitare/disabilitare int column, // @parm Colonna della cella da abilitare/disabilitare (-1=tutta la riga) bool on) // @parm Indica l'operazione da effettuare sulla cella: // // @flag true | La cella viene abilitata (default) // @flag false| La cella viene disabilitata { if (column >= FIRST_FIELD) column = cid2index(column); TRow_property* prop = get_property(row); if (prop == NULL) { if (on) return; // Don't waste time and memory prop = get_property(row, true); } TBit_array& ba = prop->disabled(); if (column >= 0) { ba.set(column, !on); } else { if (on) ba.reset(); else { ba.set(_columns); // Force right array size ba.set(); // Set all bits } } } void TSpreadsheet::set_back_and_fore_color(COLOR back, COLOR fore, int row, int col) { if (col >= FIRST_FIELD) col = cid2index(col); int first, last; if (row < 0) { first = 0; last = items()-1; } else first = last = row; if (col < 0 && same_color(back, NORMAL_BACK_COLOR) && same_color(fore, NORMAL_COLOR)) back = fore = COLOR_INVALID; const bool crea = back != COLOR_INVALID && fore != COLOR_INVALID; CHECK(!((back==COLOR_INVALID)^(fore== COLOR_INVALID)), "Coppia di colori mal assortita"); for (int r = first; r <= last; r++) { TRow_property* prop = get_property(r, crea); if (prop) prop->set(col, back, fore); } } bool TSpreadsheet::get_back_and_fore_color(COLOR& back, COLOR& fore, int row, int col) { TRow_property* prop = get_property(row, false); if (prop != NULL) return prop->get(col, back, fore); return false; } // @doc INTERNAL // @mfunc Permette di abilitare/disabiltare una colonna void TSpreadsheet::enable_column( int col, // @parm Numero della colonna da abilitare/disabilitare bool on) // @parm Indica l'operazione da effettuare sulla colonna: // // @flag true | Abilita la colonna (default) // @flag false| Disabilita la colonna { if (col >= FIRST_FIELD) col = cid2index(col); const bool change = _column_disabled[col] == on; _column_disabled.set(col, !on); if (change) { XI_OBJ* column = find_column(col); if (column) { dword attr = xi_get_attrib(column); if (on) attr |= XI_ATR_ENABLED; else attr &= ~XI_ATR_ENABLED; xi_set_attrib(column, attr); // Set new attributes } } } void TSpreadsheet::show_column(int col, bool on) { CHECK(0, "xi_set_attrib(column, XI_ATR_VISIBLE) doesn't work!"); XI_OBJ* column = find_column(col); if (column) { dword attr = xi_get_attrib(column); if (on) attr |= XI_ATR_VISIBLE; else attr &= ~XI_ATR_VISIBLE; xi_set_attrib(column, attr); // Set new attributes } } void TSpreadsheet::delete_column( const int col ) const { XI_OBJ* column = find_column(col); if (column) xi_delete(column); } void TSpreadsheet::move_column( const int fromindex, const int toindex) const { int num; XI_OBJ** columns = xi_get_member_list(_obj, &num); CHECKD(fromindex+1 < num, "Can't move column ", fromindex); XI_OBJ* column = columns[fromindex+1]; xi_move_column( column, toindex ); } void TSpreadsheet::swap_columns(const int fromid, const int toid) const { int num; XI_OBJ** columns = xi_get_member_list(_obj, &num); XI_OBJ* from_column = XI_NULL_OBJ; XI_OBJ* to_column = XI_NULL_OBJ; int from_pos = 0; int to_pos = 0; for (int c = num-1; c > 0; c--) { XI_OBJ* column = columns[c]; if (column->cid == fromid) { from_column = column; from_pos = c; } if (column->cid == toid) { to_column = column; to_pos = c; }; } CHECKD(from_pos, "Can't swap column ", fromid); CHECKD(to_pos, "Can't swap column ", toid); xi_move_column(from_column, to_pos); xi_move_column(to_column, from_pos); } void TSpreadsheet::swap_rows( const int fromindex, const int toindex) { if (fromindex != toindex) { _str.swap(fromindex, toindex); _property.swap(fromindex, toindex); } } void TSpreadsheet::move_row(const int fromindex, const int toindex) { if (fromindex != toindex) { TObject* r = _str.remove(fromindex); _str.insert(r, toindex, true); _str.pack(); TObject* p = _property.remove(fromindex); _property.insert(p, toindex, true); _property.pack(); } } int TSpreadsheet::set_line_number_width(int digits) { const int old = ROW_NUMBER_WIDTH; ROW_NUMBER_WIDTH = digits; return old; } void TSpreadsheet::set_column_width(const int col, const int width) const { XI_OBJ* column = find_column(col); if (column) xi_set_column_width(column, width); // Force redraw } void TSpreadsheet::update_column_default_width(const int col) const { XI_OBJ* column = find_column(col); if (column) { XI_RCT rct; xi_get_rect(column, &rct); int cid = col; if (cid >= FIRST_FIELD+100) // Riportalo nel range 101 - 199 cid = FIRST_FIELD + cid2index(cid); int num; XI_OBJ** column = xi_get_member_list(_obj, &num); for (int c = num-1; c > 0; c--) { if (column[c]->cid == cid) ((TSpreadsheet *)this)->_default_width[c] = rct.right - rct.left; } } } void TSpreadsheet::set_column_header(const int col, const TString& header) const { XI_OBJ* column = find_column(col); if (column) xi_set_text(column, (char *)(const char *)header ); } const char* TSpreadsheet::get_column_header(const int col) const { const char* txt = ""; XI_OBJ* column = find_column(col); if (column) txt = xi_get_text(column, NULL, -1); return txt; } void TSpreadsheet::set_column_justify(int col, bool right) { XI_OBJ* column = find_column(col); if (column) { dword attr = xi_get_attrib(column); if (right) attr |= XI_ATR_RJUST; else attr &= ~XI_ATR_RJUST; xi_set_attrib(column, attr); // Set new attribute update(-1); } } void TSpreadsheet::set_row_height(const int row, const int height) { TRow_property* prop = get_property(row, true); prop->set_height(height); } TRow_property* TSpreadsheet::get_property(int row, bool create) { TRow_property* p = (TRow_property*)_property.objptr(row); if (p == NULL && create) { p = new TRow_property; _property.add(p, row); } return p; } // Costruisce l'identificatore del paragrafo contenente la disposizione // delle colonne del campo f 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 if (m.number() > 0) // Aggiunge l'eventuale numero di sotto-maschera name << '(' << m.number() << ')'; return name; } bool TSpreadsheet::user_saved_columns_order() const { TFilename parag; field2parag(owner(), parag); TConfig config(CONFIG_USER, parag); // Apre il file di configurazione const int index = owner().dlg(); TToken_string order(config.get("Browse", NULL, index)); return !order.empty_items(); } void TSpreadsheet::save_columns_order() const { if (_save_columns_order) { TFilename parag; field2parag(owner(), parag); TConfig config(CONFIG_USER, parag); // Apre il file di configurazione if (_save_columns_order == 1) // Se vale 3 devo solo resettare { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); TToken_string order(127); // Nuovo ordine delle colonne for (int i = 0; i < num; i++) // Scorre le colonne { order.add(column[i]->cid); RCT rct; xi_get_rect(column[i], (XinRect *) &rct); order << ',' << rct.right - rct.left; } config.set("Browse", order, NULL, true, owner().dlg()); } else config.remove("Browse", owner().dlg()); } } void TSpreadsheet::load_columns_order() { TFilename parag; field2parag(owner(), parag); TConfig config(CONFIG_USER, parag); const int index = owner().dlg(); TToken_string order(config.get("Browse", NULL, index)); if (order.empty_items()) config.remove("Browse", index); else set_columns_order(&order); } void TSpreadsheet::set_columns_order() { TFilename parag; field2parag(owner(), parag); TConfig config(CONFIG_USER, parag); const int index = owner().dlg(); TToken_string order(config.get("Browse", NULL, index)); if (!order.empty_items()) set_columns_order(&order); } void TSpreadsheet::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) { int next_pos = 0; for (int index = 0; _default_width[index] > 0; index++) { const short cid = index ? FIRST_FIELD + index - 1 : 0; XI_OBJ* col = cid ? find_column(cid) : column[0]; if (col) { if (index >= fixed) { const int cur_pos = xi_obj_to_idx(col); if (cur_pos != next_pos) xi_move_column(col, next_pos); } next_pos++; 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); } } _save_columns_order = byte(0x3); } else { TToken_string col(8, ','); int pos = 0; for (col = order->get(0); !col.blank(); col = order->get(), pos++) { const int cid = col.get_int(0); const int width = col.get_int(); XI_OBJ* col = cid ? find_column(cid) : column[0]; if (col) // Controlla che esista ancora { //if (pos >= fixed && pos < num_cols) // Da' problemi in presenza di colonne nascoste (livelli giacenza) if (pos >= 1 && pos < num_cols) { const int cur_pos = xi_obj_to_idx(col); if (cur_pos != pos) xi_move_column(col, pos); // Sposta la colonna } if (width > XI_FU_MULTIPLE) // Se ha una larghezza valida xi_column_set_pixel_width(col, width - offset); } } _save_columns_order = false; } if (fixed > 1) xi_set_fixed_columns(_obj, fixed); if (focus) xi_set_focus(focus); } // Certified 99% // @doc INTERNAL // @mfunc Controlla se una cella o un'intera riga e' disabilitata // // @rdesc Se column e' minore di zero si considera l'intera riga // @rdesc Ritorna lo stato della cella indicata: // // @flag true | Se la cella e' disabilitata // @flag false| Se la cella e' abilitata bool TSpreadsheet::cell_disabled(int row, int column) const { const TRow_property* prop = ((TSpreadsheet*)this)->get_property(row); bool d = false; if (column < 0) d = (prop == NULL) ? false : (prop->disabled().ones() >= columns()-1); else { d = _column_disabled[column]; // Controlla la colonna if (d == false && prop != NULL) // Se la colonna e' disabilitata e' inutile proseguire d = prop->disabled()[column]; // Controlla la cella } return d; } // Certified 75% void TSpreadsheet::str2mask(int riga) { if (riga == items()) { const int curr = _cur_rec; _cur_rec = riga; owner().sheet_mask().reset(); _cur_rec = curr; mask2str(riga); return; } TToken_string& r = row(riga); owner().row2mask(riga, r); } // Certified 100% bool TSpreadsheet::notify(int rec, KEY k) { const bool ok = _notify ? _notify(owner(), rec, k) : true; if (k == K_ENTER) { const bool cell_dirty = _cell_dirty; // preservato lo stato di cell_dirty set_dirty(ok ? 1 : 3); _cell_dirty = cell_dirty; } return ok; } // Certified 99% KEY TSpreadsheet::edit(int n) { str2mask(n); if (_needs_update == n) // Altrimenti grossi problemi chiamando edit() durante editing cella _needs_update = -1; KEY k = owner().run_editmask(n); if (active()) { if (k == K_ENTER) { mask2str(n); } else { if (k == K_DEL) { const bool ok = notify(n, K_DEL); // Notifica intenzione di cancellare if (ok) { destroy(n); if (n < items()) str2mask(n); notify(n, K_CTRL+K_DEL); // Notifica l'avvenuta cancellazione set_dirty(); } } else if (k == K_ESC) { str2mask(n); // Ripristina valori precedenti } } } else k = K_ESC; return k; } bool TSpreadsheet::add_row_auto() { bool ok = false; if (_check_enabled) { on_idle(); // Termina tutti gli eventuali update in corso if (active() && xi_move_focus(get_interface())) { int rec = -1; TMask& om = owner().mask(); const bool omrun = om.is_running(); if (items() > 0 && !owner().append()) { if (om.focus_field().dlg() == id()) rec = _cur_rec + 1; else rec = 0; } ok = insert(rec, omrun, true) >= 0; if (ok) { _cell_dirty = _row_dirty = false; if (omrun) om.notify_focus_field(id()); } } } return ok; } bool TSpreadsheet::error_box(const char* msg) { _check_enabled = false; const int r = _cur_row; const int c = _cur_col; if (ADVANCED_GRAPHICS && ANIMATED_BOXES) xvt_dm_popup_error(msg); else xvt_dm_post_error(msg); TMask& m = owner().mask(); m.set_focus_field(owner().dlg()); set_focus_cell(min(r, items()-1), c); xvt_win_set_caret_visible(parent(), true); _check_enabled = true; return false; } bool TSpreadsheet::point2cell(const PNT& pnt, short& id, long& row) const { RCT rct; xi_get_rect(_obj, (XI_RCT*)&rct); bool inside = xvt_rect_has_point(&rct, pnt) != 0; id = DLG_NULL; row = -1; if (inside) { int num_cols; XI_OBJ** column = xi_get_member_list(_obj, &num_cols); for (int c = 0; c < num_cols; c++) { xi_get_rect(column[c], (XI_RCT*)&rct); if (xvt_rect_has_point(&rct, pnt)) { id = column[c]->cid; break; } } int first_vis, last_vis; int rows = xi_get_visible_rows(_obj, &first_vis, &last_vis); if (rows > 0) { XI_OBJ row1; XI_MAKE_ROW(&row1, _obj, first_vis); XI_OBJ row2; XI_MAKE_ROW(&row2, _obj, last_vis); XI_RCT r1; xi_get_rect(&row1, &r1); XI_RCT r2; xi_get_rect(&row2, &r2); const int r = first_vis + (pnt.v - r1.top) / (r1.bottom-r1.top); if (r >= first_vis && r <= last_vis) { const long* handle = xi_get_list_info(_obj, &rows); row = handle[r - first_vis]; } } } return inside; } /////////////////////////////////////////////////////////// // TSheet_field /////////////////////////////////////////////////////////// // Certified 100% TSheet_field::TSheet_field(TMask* m) : TLoadable_field(m), _append(true), _separator('|'), _enable_autoload(false), _sheetfile(NULL), _linee_rec(NULL), _external_record(false), _userput(NULL),_userget(NULL) { } // Certified 100% word TSheet_field::class_id() const { return CLASS_SHEET_FIELD; } bool TSheet_field::is_kind_of(word cid) const { return cid == CLASS_SHEET_FIELD || TOperable_field::is_kind_of(cid); } // Certified 100% TSheet_field::~TSheet_field() { if (_sheetfile!=NULL) delete _sheetfile; if (_linee_rec!=NULL && !_external_record) delete _linee_rec; } // Certified 100% void TSheet_field::reset() { TSpreadsheet* s = (TSpreadsheet*)_ctl; // if (s->items()) s->select(0, false); // Non si capisce a cosa servisse dal 1996 s->destroy(); s->sheet_mask().reset(); set_dirty(); // Reset any error (dirty = 3) set_dirty(mask().is_running()); // Set dirty state based on mask status force_update(); } // Certified 100% void TSheet_field::destroy(int r, bool update_sheet) { ((TSpreadsheet*)_ctl)->destroy(r, update_sheet); } // Certified 100% int TSheet_field::insert(int r, bool update_sheet, bool call_notify) { return ((TSpreadsheet*)_ctl)->insert(r, update_sheet, call_notify); } void TSheet_field::parse_head(TScanner& scanner) { _ctl_data._width = scanner.integer(); _ctl_data._height = scanner.integer(); if (_ctl_data._height == 0) _ctl_data._height = -1; } // Certified: ...under debug... bool TSheet_field::parse_item(TScanner& scanner) { if (scanner.key() == "IT") // ITEM { const char* h = dictionary_translate_header(scanner.string()); _ctl_data._park.add(h); return true; } if (scanner.key() == "FL") // FLAGS { const char* flags = scanner.string(); for (const char* f = flags; *f; f++) { switch(*f) { case 'A': _enable_autoload = true; break; case 'I': _append = false; break; case '|': _separator = SAFE_PIPE_CHR; break; default : break; } } return true; } if (scanner.key() == "US") // USE { const int logicnum = scanner.integer(); if (logicnum > 0) { TDir d; d.get(logicnum); const TFilename fn(d.filename()); if (fn.exist()) // Controlla l'esistenza del file { _sheetfile = new TLocalisamfile(logicnum); TString s = scanner.pop(); // cerca l'indicazione del campo di numerazione if (s == "KE") { s = scanner.pop(); _linee_rec= new TRecord_array(logicnum, s);// alloca i record } else { NFCHECK("Manca la definizione del campo di autonumerazione nel campo %d", (int)dlg()); } } } return true; } if (scanner.key() == "IN") // input (definisce la chiave) { parse_input(scanner); return true; } return TLoadable_field::parse_item(scanner); } void TSheet_field::parse_input(TScanner& scanner) { const char* s = scanner.pop(); _file_k_names.add(s); s = scanner.pop(); if (*s == '"') // Constant string { scanner.push(); TString& str = scanner.line(); _file_k_ids.add(str); } else // Field on the mask { CHECKS(_file_k_ids.get_pos(s) < 0, "Duplicate input field ", s); _file_k_ids.add(s); if (scanner.popkey() == "SE") _file_k_ids << '@'; // Special FILTERing field else scanner.push(); } } // Certified 100% void TSheet_field::create(WINDOW parent) { const TMask& m = mask(); const TString head(_ctl_data._park); _ctl = new TSpreadsheet(parent, dlg(), _ctl_data._x, _ctl_data._y, _ctl_data._width, _ctl_data._height, m.source_file(), m.sheets()+1, head, this); _ctl->show(shown()); if (!_flags.enable_default) { _flags.enabled = true; // Lo sheet e' sempre operabile anche se non editabile disable(); } const TMask & s = sheet_mask(); short id; for (id = FIRST_FIELD; ; id++) if (s.id2pos(id) < 0) break; _last_column_id = id - 1; ((TSpreadsheet*)_ctl)->load_columns_order(); } // Certified 100% TString_array& TSheet_field::rows_array() const { return ((TSpreadsheet*)_ctl)->rows_array(); } const char* TSheet_field::cell(int r, int c) const { if (c >= FIRST_FIELD) c = cid2index(c); if (c < 0 || r < 0 || r >= items()) return ""; TToken_string& riga = ((TSheet_field*)this)->row(r); const char* v = riga.get(c); return v ? v : " "; } // Certified 100% // Ritorna l'indice della prima riga vuota dello sheet int TSheet_field::first_empty() const { TSpreadsheet* s = (TSpreadsheet*)_ctl; const int max = (int)s->items(); int n; for (n = 0; n < max; n++) if (s->row(n).empty_items()) break; return n; } // @doc EXTERNAL // @mfunc Ritorna nuova riga dello spreadshhet // // @rdesc Ritorna la stringa letta TToken_string& TSheet_field::row( int n) // @parm Numero della riga da leggere/creare // @comm Se il parametro

e maggiore del numero massimo di righe presenti // o minore di 0 viene aggiunta una riga vuota in fondo allo spreadsheet // (viene chiamata la ) { TSpreadsheet* s = (TSpreadsheet*)_ctl; const int max = (int)s->items(); if (n < 0 || n >= max) { if (n < 0) n = first_empty(); if (n >= max) n = (int)s->add(new TToken_string(80, _separator)); } return s->row(n); } // @doc EXTERNAL // @mfunc Forza l'aggiornamento dei dati della riga sullo schermo void TSheet_field::force_update( int r) // @parm Numero della riga da aggiornare // @comm Se il parametro

assume valore -1 vengono aggiornate tutte le righe presenti // nello spreadsheet { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->update(r); } int TSheet_field::items() const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return (int)s->items(); } int TSheet_field::selected() const { TSpreadsheet* s = (TSpreadsheet*)_ctl; int sel = s != NULL ?(int)s->selected() : -1; return sel; } void TSheet_field::set_notify(SPREADSHEET_NOTIFY n) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_notify(n); } // Certified 50% void TSheet_field::highlight() const { // TEditable_field::highlight(); TSpreadsheet* s = (TSpreadsheet*)_ctl; if (s->_check_enabled) { int rows; xi_get_list_info(s->_obj, &rows); if (rows > 0 && s->_cur_rec >= 0 && s->_cur_rec < rows) { if (s->notify(s->_cur_rec, K_TAB)) { s->set_focus_cell(s->_cur_row, s->_cur_col); const int curr = selected(); if (curr >= 0) s->str2mask(curr); } } } } void TSheet_field::enable(bool on) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->activate(on); } void TSheet_field::set_auto_append(bool on) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_auto_append(on); } void TSheet_field::set_nav_column(short firstcol, short lastcol) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_nav_column(firstcol, lastcol); } bool TSheet_field::enabled() const { // return items() > 0; return TMask_field::enabled(); } int TSheet_field::cid2index(short cid) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->cid2index(cid); } void TSheet_field::enable_row(int row, bool on) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->enable_cell(row, -1 , on); } bool TSheet_field::row_enabled(int row) { TSpreadsheet* s = (TSpreadsheet*)_ctl; return (s->find_enabled_column(row,1,1) != 0); } void TSheet_field::enable_cell(int row, int column, bool on) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->enable_cell(row, column, on); } void TSheet_field::enable_column(int column, bool on) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->enable_column(column, on); } bool TSheet_field::column_enabled(int column) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->column_enabled(column); } bool TSheet_field::column_disabled(int column) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->column_disabled(column); } bool TSheet_field::exist_column(const int column) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->exist_column(column); } bool TSheet_field::cell_disabled(int row, int column) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->cell_disabled(row, column); } bool TSheet_field::cell_enabled(int row, int column) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return !s->cell_disabled(row, column); } void TSheet_field::show_column(int col, bool on) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->show_column(col, on); } // Matteo was here! void TSheet_field::delete_column( const int col ) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->delete_column( col ); } void TSheet_field::move_column( const int fromindex, const int toindex ) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; if (fromindex >= FIRST_FIELD) s->move_column(s->cid2col(fromindex), toindex); else s->move_column(fromindex, toindex); } void TSheet_field::swap_columns(const int fromid, const int toid) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->swap_columns(fromid, toid); } bool TSheet_field::user_saved_columns_order() const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->user_saved_columns_order(); } void TSheet_field::set_columns_order(TToken_string* order) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_columns_order(order); } void TSheet_field::set_columns_order() { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_columns_order(NULL); s->set_columns_order(); } const char* TSheet_field::get_column_header( const int col) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->get_column_header(col); } void TSheet_field::swap_rows( const int fromindex, const int toindex) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->swap_rows(fromindex, toindex); } void TSheet_field::move_row(const int fromindex, const int toindex) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->move_row(fromindex, toindex); } static int default_rows_compare(TSheet_field &s, int i, int j) { TToken_string &s1 = s.row(i); TToken_string &s2 = s.row(j); if (s1 == s2) return 0; return s1.compare(s2); } void TSheet_field::sort(ROWS_COMPARE_FUNCTION compare) { if (compare == NULL) compare = default_rows_compare; const long last = items()-1; for (int i = 0; i < last; i++) { for (int j = i+1; j <= last; j++) { const int cmp = compare(*this, i, j); if (cmp > 0) swap_rows( i,j); } } } int TSheet_field::set_line_number_width(int width ) { return TSpreadsheet::set_line_number_width(width); } void TSheet_field::set_column_width( const int col, const int width ) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_column_width(col, width); } void TSheet_field::update_column_width( const int col) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->update_column_default_width(col); } void TSheet_field::set_column_header( const int col, const TString& header ) const { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_column_header(col, header); } void TSheet_field::set_column_justify(int col, bool right) { if (col < FIRST_FIELD) col += FIRST_FIELD; sheet_mask().field(col).set_justify(right); TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_column_justify(col, right); } void TSheet_field::set_row_height( const int row, const int height ) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->set_row_height(row, height); } TMask& TSheet_field::sheet_mask() const { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->sheet_mask(); } TMask& TSheet_field::sheet_row_mask(int /* row */) const { return sheet_mask(); } bool TSheet_field::on_hit() { TMask& m = mask(); if (!m.is_running()) // Inizializzazione maschera { TSpreadsheet* s = (TSpreadsheet*)_ctl; if (s->auto_append() && items() == 0 && !m.query_mode()) { s->add_row_auto(); // Inserisco automaticamente la prima riga vuota nello sheet force_update(); } else { force_update(); if (items() > 0) { m.notify_focus_field(dlg()); // Fa' credere alla maschera che ha il focus ... select(0, -2, true); // ... cosi' la set_focus_cell funziona bene } } set_dirty(false); } const bool ok = handler(K_SPACE); return ok; } void TSheet_field::select(int r, bool scrollto) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->select(r, scrollto); } void TSheet_field::select(int r, int c , bool scrollto) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->select(r, c, scrollto); } void TSheet_field::post_select(int r) { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->post_select(r); } bool TSheet_field::set_focus_cell(int riga, int colonna) { TSpreadsheet* s = (TSpreadsheet*)_ctl; return s->set_focus_cell(riga, colonna); } bool TSheet_field::set_focus_cell_id(long rec, short cid) { TSpreadsheet* s = (TSpreadsheet*)_ctl; const int col = s->cid2col(cid); const int row = s->rec2row(rec); // Controlla che la cella sia veramente visibile int first_col, last_col, first_row, last_row; xi_get_visible_columns(s->_obj, &first_col, &last_col); xi_get_visible_rows(s->_obj, &first_row, &last_row); bool ok = false; if (col >= first_col && col <= last_col && row >= first_row && row <= last_row) ok = s->set_focus_cell(row, col); else s->select(rec, col, true); return ok; } bool TSheet_field::on_key(KEY k) { if (k == K_TAB && items() > 0) { if (focusdirty()) { TSpreadsheet* s = (TSpreadsheet*)_ctl; if (!s->test_focus_change()) return false; } } if (k == K_ROWEDIT && items() > 0) { select(items()-1); XI_EVENT xie; xie.type = XIE_DBL_CELL; xie.v.xi_obj = NULL; _ctl->event_handler(NULL, &xie); return true; } if (k == K_CTRL+'+') { TSpreadsheet* s = (TSpreadsheet*)_ctl; s->add_row_auto(); return true; } if (k == K_CTRL+'-') { TSpreadsheet* s = (TSpreadsheet*)_ctl; XI_EVENT e; memset(&e, 0, sizeof(e)); e.type = XIE_CHAR_CELL; e.v.chr.ch = '-'; e.v.chr.control = true; return s->event_handler(NULL, &e); } return TOperable_field::on_key(k); } void TSheet_field::on_idle() { ((TSpreadsheet*)_ctl)->on_idle(); } // Ricopia i campi della maschera nel record dato void TSheet_field::mask2row(int n, TToken_string & rec) { const TMask& m = sheet_row_mask(n); const TSpreadsheet& s = (const TSpreadsheet&)*_ctl; rec.cut(0); for (short id = FIRST_FIELD; id <= _last_column_id ; id++) { int pos = m.id2pos(id); const int firstpos = pos; if (pos >= 0) { for(int dlg = id; pos >= 0; pos = m.id2pos(dlg += 100)) { const TMask_field& f = m.fld(pos); if (f.shown() || f.ghost()) { rec.add(f.get()); if (active() && s.active()) { const int col = cid2index(id); if (!s.column_disabled(col)) { const bool on = f.enabled(); enable_cell(n, col, on); } } break; } } #ifdef DBG if (pos < 0 && s.cid2col(id) > 0) yesnofatal_box("Mask2row: Non e' visibile il campo %d", id); #endif } else { const int col = cid2index(id); if (!s.column_disabled(col)) enable_cell(n, col, false); } if (pos < 0) rec.add(firstpos >= 0 ? m.fld(firstpos).get() : " "); } rec.pack(); } // Ricopia i campi del record dato nella maschera void TSheet_field::row2mask(int n, TToken_string& r, int mode) { TMask& m = sheet_row_mask(n); m.reset(); // Azzera tutti i campi, soprattutto quelli con (id > _last_column_id) //const int campi = m.fields(); const TSpreadsheet& s = (const TSpreadsheet&)*_ctl; TString val; for (short id = FIRST_FIELD; id <= _last_column_id; id++) { const int index = cid2index(id); val = r.get(index); int dlg = id; for(int pos = m.id2pos(dlg); pos >= 0; pos = m.id2pos(dlg += 100)) { TMask_field& f = m.fld(pos); f.set(val); const bool on = s.active() && !cell_disabled(n, index); if (f.enabled() != on) f.enable(on); } } if (mode > 0) { WINDOW last_parent = NULL_WIN; FOR_EACH_MASK_FIELD(m, i, f) { const short id = f->dlg(); if (id >= FIRST_FIELD && (f->active() || f->ghost()) && !f->is_kind_of(CLASS_BUTTON_FIELD) && !f->is_kind_of(CLASS_BUTTON_TOOL)) { WINDOW current_parent = f->parent(); if (current_parent != last_parent) { // Assegna il focus all'interfaccia per evitare effetti indesiderati durante i messaggi low_set_focus_id(f->parent(), -1); } if (f->has_check() && (mode & 0x1)) f->check(STARTING_CHECK); f->set_dirty(false); if (mode & 0x2) f->on_hit(); } } } FOR_EACH_MASK_FIELD(m, i, f) { if (f->dirty() == 1) f->set_dirty(false); } // Imposta titolo se pagina singola if (m.page_win(1) == NULL_WIN) // Non esiste pagina 2? { TString caption; m.get_caption(caption); if (caption.full()) { const int spc = caption.find(" ["); if (spc > 0) caption.cut(spc); } else caption = TR("Riga"); caption << " [" << (n+1) << ']'; m.set_caption(caption); } } KEY TSheet_field::run_editmask(int n) { TMask& m = sheet_row_mask(n); return m.run(); } void TSheet_field::set_back_and_fore_color(COLOR back, COLOR fore, int row, int col) { TSpreadsheet& s = (TSpreadsheet&)*_ctl; s.set_back_and_fore_color(back, fore, row, col); } void TSheet_field::reset_columns_order() { TSpreadsheet& s = (TSpreadsheet&)*_ctl; s.set_columns_order(NULL); } void TSheet_field::save_columns_order() { const TSpreadsheet& s = (const TSpreadsheet&)*_ctl; s.save_columns_order(); } void TSheet_field::check_row(int n, int mode) { TSpreadsheet* s = (TSpreadsheet*)_ctl; const int current = s->_cur_rec; s->_cur_rec = n; TMask& m = sheet_row_mask(n); CHECK(!m.is_running(), "Can't use check_row while sheet mask is running"); TToken_string & r = row(n); const int max = m.fields(); TMaskmode mask_mode = (TMaskmode) m.mode(); m.set_mode(mask().mode()); for (int i = max - 1; i >= 0; i--) { TMask_field & f = m.fld(i); if (f.has_check()) f.set_dirty(); } row2mask(n, r, mode); mask2row(n, r); m.set_mode(mask_mode); s->_cur_rec = current; } // Certified: ...under debug.. void TSheet_field::set_lines_record(TRecord_array & r_a) { if (_linee_rec && !_external_record) delete _linee_rec; _linee_rec= & r_a; _external_record = true; // il record attuale è esterno... } // Certified: ...under debug.. TRectype* TSheet_field::putkey(const TRelation& r) { if (_sheetfile) { // costruisce la chiave per il record array _sheetfile->zero(); _file_k_ids.restart(); for (TString16 dbfieldname = _file_k_names.get(0); dbfieldname.full(); dbfieldname = _file_k_names.get()) { TString80 id = _file_k_ids.get(); if (id[0] == '"') { id.strip("\""); _sheetfile->curr().renum_key(dbfieldname, id); } else { TMask_field& f = mask().field(atoi(id)); if (mask().edit_mode()) ((TLoadable_field&)f).autoload(r); // Molto probabilmente inutile! _sheetfile->curr().renum_key(dbfieldname, f.get()); } } return &_sheetfile->curr(); } return NULL; } // Certified: ...under debug.. bool TSheet_field::autoload_line(int i, const TRectype& rec) { TToken_string &row= this->row(i-1); row.cut(0); for (short id = FIRST_FIELD; id <= _last_column_id; id++) { TMask_field& mf = sheet_mask().field(id); const TFieldref* dbfield = mf.field(); if (dbfield) row.add(dbfield->read(rec),id - FIRST_FIELD); } // completa l'operazione con le funzioni definite dall'utente if (_userget) _userget(*this,i); check_row(i-1); return true; } // Certified: ...under debug.. bool TSheet_field::autosave_line(int i,TRectype & rec) { for (short id = FIRST_FIELD; id <= _last_column_id; id++) { TMask_field& mf = sheet_mask().field(id); const TFieldref* dbfield = mf.field(); if (dbfield) dbfield->write(cell(i - 1, id - FIRST_FIELD), rec); } // completa l'operazione con le funzioni definite dall'utente if (_userput) _userput(*this,i); return false; } // Certified: ...under debug.. bool TSheet_field::autoload(const TRelation& rel) { if (_enable_autoload) { CHECK(_linee_rec !=NULL, "Iu ev forgotten tu declare de Record array for de scit"); // ******* // trasferisce le linee dal record array allo sheet destroy(); // cancella lo sheet const int last_line = _linee_rec->last_row(); for (int i= 1; i <= last_line; i++) autoload_line(i,_linee_rec->row(i, true)); return(0); } return _enable_autoload; } void TSheet_field::restart_key() { _file_k_names.restart(); _file_k_ids.restart(); } TMask_field *TSheet_field::get_key(TString & dbfieldname) { if ((dbfieldname = _file_k_names.get())!="") return &mask().field(atoi(_file_k_ids.get())); return NULL; } // Certified: ...under debug.. bool TSheet_field::autosave(TRelation& rel) { if (_enable_autoload) { CHECK(_linee_rec !=NULL, "Iu ev forgotten tu declare de Record array for de scit"); _linee_rec->destroy_rows(); if (_sheetfile) { // trasferisce dal file locale if (mask().insert_mode()) { // rinumera la chiave _file_k_ids.restart(); for (TString16 dbfieldname = _file_k_names.get(0); dbfieldname.full(); dbfieldname = _file_k_names.get()) { TString80 id = _file_k_ids.get(); if (id[0] == '"') { id.strip("\""); _linee_rec->renum_key(dbfieldname, id); } else { const TMask_field& f = mask().field(atoi(id)); _linee_rec->renum_key(dbfieldname, f.get()); } } } } // ******* // trasferisce le linee dallo sheet al record array (ignorando righe vuote alla fine) int i = items(); while (i > 0 && row(i-1).empty_items()) i--; for (; i > 0; i--) { TRectype &rec = _linee_rec->row(i, true); autosave_line(i,rec); // autosave_line parte da 1 :-( } } return _enable_autoload; } // Certified: ...under debug.. void TSheet_field::set_userget(SHEET_USERGETPUT handler) { _userget = handler; } // Certified: ...under debug.. void TSheet_field::set_userput(SHEET_USERGETPUT handler) { _userput = handler; } static TString& clean_white_space(TString& str) { str.trim(); char* buff = str.get_buffer(); for (char* b = buff; *b; b++) { if (*b > '\0' && *b < ' ') *b = ' '; } str.strip_double_spaces(); return str; } /////////////////////////////////////////////////////////// // TSheet_recordset /////////////////////////////////////////////////////////// class TSheet_recordset : public TRecordset { const TSpreadsheet* _sheet; int _cols; TRecnotype _curr; protected: virtual bool get_attr(int column, TAttributes& attr, bool header) const; public: virtual unsigned int columns() const { return _cols; } virtual const TRecordset_column_info& column_info(unsigned int i) const; virtual TRecnotype items() const { return _sheet->items(); } virtual bool move_to(TRecnotype pos); virtual TRecnotype current_row() const { return _curr; } virtual void requery() {} virtual const TVariant& get(unsigned int column) const; public: virtual const TString& query_text() const { return EMPTY_STRING; } TSheet_recordset(const TSpreadsheet* s) : _sheet(s), _curr(-1) { xi_get_member_list(((TSpreadsheet*)s)->xi_object(), &_cols); _cols--; } }; bool TSheet_recordset::move_to(TRecnotype pos) { const bool ok = pos >= 0 && pos < items(); if (ok) _curr = pos; return ok; } const TRecordset_column_info& TSheet_recordset::column_info(unsigned int i) const { static TRecordset_column_info _info; int num; XI_OBJ** columns = xi_get_member_list(((TSpreadsheet*)_sheet)->xi_object(), &num); XI_OBJ* column = int(i+1) < num ? columns[i+1] : NULL; if (column) { _info._pos = (column->cid % 100) - 1; xi_get_text(column, _info._name.get_buffer(), _info._name.size()); _info._name.trim(); const TMask_field& fld = _sheet->sheet_mask().field(column->cid); switch (fld.class_id()) { case CLASS_BOOLEAN_FIELD : _info._type = _boolfld; break; case CLASS_DATE_FIELD : _info._type = _datefld; break; case CLASS_CURRENCY_FIELD: case CLASS_REAL_FIELD : _info._type = _realfld; break; default : _info._type = _alfafld; break; } _info._width = fld.size(); } else { _info._pos = -1; _info._name.cut(0); _info._type = _nullfld; _info._width = 0; } return _info; } const TVariant& TSheet_recordset::get(unsigned int i) const { if (_curr >= 0 && _curr < items() && i < columns()) { const TRecordset_column_info& ci = column_info(i); if (ci._pos >= 0) { TToken_string& row = ((TSpreadsheet*)_sheet)->row(_curr); TVariant& tmp = get_tmp_var(); tmp = row.get(ci._pos); tmp.convert_to(ci._type); return tmp; } } return NULL_VARIANT; } bool TSheet_recordset::get_attr(int i, TAttributes& attr, bool header) const { const TRecordset_column_info& ci = column_info(i); bool is_valid = false; if (ci._pos >= 0) { COLOR back, fore; if (header) { back = BTN_BACK_COLOR; fore = PROMPT_COLOR; // is_valid = true; // Per ora non funziona } else { is_valid = ((TSpreadsheet*)_sheet)->get_back_and_fore_color(back, fore, _curr, ci._pos); } } return is_valid; } /////////////////////////////////////////////////////////// // Esportazione /////////////////////////////////////////////////////////// bool TSheet_field::esporta() const { TFilename name; name.tempdir(); name.add(mask().source_file().name_only()); name.ext("xls"); TSheet_recordset sr((TSpreadsheet*)_ctl); sr.save_as(name); /* ofstream xls(name); const char sep = '\t'; TToken_string tab(128, sep); TString str; int columns = 0; XI_OBJ** pcols = xi_get_member_list(_ctl->xi_object(), &columns); for (int c = 1; c < columns; c++) { xi_get_text(pcols[c], str.get_buffer(), str.size()); tab.add(clean_white_space(str)); } xls << tab << endl; for (long n = 0; n < items(); n++) { const TToken_string& r = ((TSheet_field*)this)->row(n); tab.cut(0); for (int c = 1; c < columns; c++) { const int idx = cid2index(pcols[c]->cid); r.get(idx, str); clean_white_space(str); if (real::is_real(str)) { if (real::is_null(str)) str.cut(0); else { if (real::is_natural(str)) str.trim(); else xvt_str_number_format(str.get_buffer(), str.size()); } } if (str.not_empty()) tab.add(str, c-1); } xls << tab << endl; } xls.close(); */ xvt_sys_goto_url(name, "open"); return true; } bool TSheet_field::error_box(const char* fmt, ...) const { char msg[256]; va_list argptr; va_start(argptr,fmt); _vsnprintf(msg,sizeof(msg),fmt,argptr); va_end(argptr); msg[255] = '\0'; return ((TSpreadsheet*)_ctl)->error_box(msg); } const int TSheet_field::current_column() const { TSpreadsheet* s = (TSpreadsheet*)_ctl; int sel = s->_cur_col; return sel; } bool TSheet_field::point2cell(const PNT& pnt, short& id, long& row) const { TSpreadsheet& s = (TSpreadsheet&)*_ctl; return s.point2cell(pnt, id, row); }