#define XI_INTERNAL #include #include #include #include #include #include #include #include #include #include #include #include /////////////////////////////////////////////////////////// // TSheet_control /////////////////////////////////////////////////////////// class TSheet_control : public TControl { enum { FIRST_FIELD = 101, MAX_COL = 256 }; TSheet* _sheet; long _cur_rec; // @cmember:(INTERNAL) Tipo di ogni colonna byte _type[MAX_COL]; // @cmember:(INTERNAL) Indica se e attivata la gestione dei check delle righe bool _check_enabled; // @cmember:(INTERNAL) Array di righe attivate TBit_array _checked; // @cmember:(INTERNAL) Array di righe disabilitate TBit_array _disabled; // @cmember:(INTERNAL) Array delle largezze standard int _default_width[MAX_COL]; byte _save_columns_order; protected: // TControl //@cmember Gestisce gli eventi delle celle virtual bool event_handler(XI_OBJ* itf, XI_EVENT* xiev); protected: long items() const { return _sheet->items(); } TToken_string& row(long n) const { return _sheet->row(n); } int rec2row(long rec) const; long row2rec(int row) const; void make_current(long rec); XI_OBJ* find_column(int col) const; XI_OBJ* find_column(const char* head) const; public: // TControl virtual void set_rect(const RCT& r); public: long selected() const { return _cur_rec; } void select(long n); void set_focus_rec(long rec); void enable_check(bool on); bool check_enabled() const { return _check_enabled; } void check(long n, bool on); void toggle(long n); bool checked(long n) const { return _checked[n]; } long checked() const { return _checked.ones(); } bool one_checked() const; int visible_rows() const; void enable_row(long r, bool on); void disable_row(long r); bool row_enabled(long n) const { return _disabled[n] == FALSE; } bool one_enabled() const { return _disabled.ones() < _sheet->items(); } byte column_type(int c) const { CHECKD(c >= 0 && c < MAX_COL, "Bad column ", c); return _type[c]; } void align_column(int c, bool right); void set_columns_order(TToken_string* order); void save_columns_order(const TEdit_field& f) const; void load_columns_order(const TEdit_field& f); void update(long n = -1); bool head(int c, TString& str) const; TSheet_control(WINDOW sheet, short cid, short x, short y, short dx, short dy, const char* flags, const char* head); virtual ~TSheet_control() {} }; TSheet_control::TSheet_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 : _sheet(NULL), _cur_rec(0), _check_enabled(FALSE), _save_columns_order(FALSE) { const int NUMBER_WIDTH = 7; short v_width[MAX_COL]; short m_width[MAX_COL]; int fixed_columns = 1; // Number of fixed columns int lines_in_cell = 1; _sheet = (TSheet*)xvt_vobj_get_data(parent); // Calcolo larghezza massima tabella TToken_string header(head); TToken_string new_header(header.size()); int f_width = NUMBER_WIDTH; // Stima larghezza colonne fisse int max_width = f_width; // Stima larghezza della colonna piu' grande int tot_width = f_width; int lines_in_header = 1; const char * h; int i = 0; for (h = header.get(); h && i < MAX_COL; h = header.get(), i++) { _type[i] = ' '; 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); } if (t[c] == '\n') lines_in_header = 2; } const int at = testa.rfind('@'); int v = testa.len(); // Video width if (at >= 0) { const TString& wi = testa.mid(at+1); v = atoi(wi); if (wi.find('F') >= 0) { fixed_columns++; f_width += v+1; } if (wi.find('R') >= 0) _type[i] = 'R'; else if (wi.find('V') >= 0) _type[i] = 'V'; else if (wi.find('P') >= 0) _type[i] = 'P'; else if (wi.find('M') >= 0) _type[i] = 'M'; else if (wi.find('C') >= 0) _type[i] = 'C'; if (i == 0 && v <= 1) { _type[i] = 'C'; _check_enabled = TRUE; fixed_columns++; f_width += v+1; } testa.cut(at); if (v == 0) v = at; } if (v > 64) { lines_in_cell = (v-1) / 64 + 1; v = 64; } m_width[i] = v*lines_in_cell + 1; v_width[i] = v+1; if (v > max_width) max_width = v; new_header.add(testa); tot_width += v_width[i]; if (tot_width > 2048) // Taglio corto con gli sheet enormi > 16000 pixel break; } // Calcola rettangolo massimo per lo sheet // RCT rct; coord2rct(parent, x, y, dx, dy, rct); // rct.right -= 2*XI_FU_MULTIPLE; // toglie scroll-bar RCT rct; coord2rct(parent, x, y, -2, -2, rct); // 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 | XI_ATR_NAVIGATE, NORMAL_COLOR, NORMAL_BACK_COLOR, // normal NORMAL_COLOR, DISABLED_BACK_COLOR, // disabled FOCUS_COLOR, // active 0); listdef->app_data = (long)this; XI_LIST_DEF* l = listdef->v.list; l->min_heading_height = xi_button_calc_height_font(xi_get_system_font()) * lines_in_header; l->max_lines_in_cell = lines_in_cell; 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; #ifdef LINUX l->scroll_on_thumb_track = TRUE; #endif // 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, 2 * XI_FU_MULTIPLE, NUMBER_WIDTH , ""); coldef->app_data = (long)this; coldef->v.column->heading_platform = TRUE; coldef->v.column->column_platform = TRUE; coldef->v.column->center_heading = TRUE; for (h = new_header.get(0), i = 0; h && i < MAX_COL; h = new_header.get(), i++) { long attr = XI_ATR_VISIBLE | XI_ATR_ENABLED | XI_ATR_AUTOSCROLL | XI_ATR_READONLY; if (i == 0 && _type[i] == 'C') attr |= XI_ATR_SELECTABLE; if (_type[i] == 'R' || _type[i] == 'V' || _type[i] == 'P') // Right, Currency, Price 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; coldef->v.column->heading_platform = TRUE; coldef->v.column->center_heading = TRUE; if (m_width[i] > 64) coldef->v.column->wrap_text = TRUE; if (i == 0 && _type[i] == 'C') { coldef->v.column->icon_rid = ICO_CHECK_ON; coldef->v.column->icon_mode = XIM_ICON_HAS_PRIORITY; if (l->min_heading_height < 20) l->min_heading_height = 20; } } // Attenzione: negli sheet molto grandi succede che rd.right < 0 XI_RCT rd; xi_get_def_rect(listdef, &rd); if (rd.right < 0 || (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 spreadsheet ", cid); update_tab_cid(); // Se la finestra e' troppo grande riducila RCT cli; xvt_vobj_get_client_rect(parent, &cli); xi_get_rect(_obj, (XinRect*)&rct); const int extra = (cli.right - (rct.right-rct.left+2*XI_FU_MULTIPLE)) / 2; if (extra > XI_FU_MULTIPLE) { WINDOW main_win = _sheet->win(); xvt_vobj_get_outer_rect(main_win, &cli); cli.left += extra; cli.right -= extra; xvt_vobj_move(main_win, &cli); xvt_vobj_get_client_rect(parent, &cli); } int num; XI_OBJ** column = xi_get_member_list(_obj, &num); for (i = 0; i < num; i++) { XI_RCT rct; xi_get_rect(column[i], &rct); _default_width[i] = rct.right - rct.left; } } void TSheet_control::set_rect(const RCT& r) { const int width = r.right - r.left - 2*XI_FU_MULTIPLE; const int height = r.bottom - r.top; xi_set_list_size(_obj, height, width); } // Converts a row number in the correspondig record number int TSheet_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 TSheet_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 TSheet_control::visible_rows() const { return xi_get_visible_rows(_obj, NULL, NULL); } void TSheet_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); const long tot = items(); bool scroll_first = tot == 0; // || !owner().mask().is_running(); if (!scroll_first) { int first = 0, last = 0; xi_get_visible_rows(_obj, &first, &last); n = handle[first]; scroll_first = n > tot; } if (scroll_first) xi_scroll(_obj, XI_SCROLL_FIRST); else xi_scroll_rec(_obj, n, NORMAL_COLOR, XI_ATR_ENABLED, 0); } } void TSheet_control::make_current(long rec) { const long old = _cur_rec; _cur_rec = rec; XI_OBJ o; const int oldrow = rec2row(old); if (oldrow >= 0) { XI_MAKE_ROW(&o, _obj, oldrow); xi_cell_request(&o); } const int newrow = rec2row(rec); if (newrow >= 0 && newrow != oldrow) { XI_MAKE_ROW(&o, _obj, newrow); xi_cell_request(&o); } } void TSheet_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); } make_current(rec); } void TSheet_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* TSheet_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); int c; for (c = num-1; c >= 0; c--) { if (columns[c]->cid == col) break; } return c >= 0 ? columns[c] : NULL; } XI_OBJ* TSheet_control::find_column( const char* head) const // @parm testata colonna { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); int c; for (c = num-1; c >= 0; c--) { char str[80]; xi_get_text(column[c], str, sizeof(str)); if (strcmp(str, head) == 0) break; } return c >= 0 ? column[c] : NULL; } bool TSheet_control::head(int c, TString& str) const { XI_OBJ* col = find_column(c); if (col != NULL) xi_get_text(col, str.get_buffer(80), 80); else str.cut(0); return col != NULL; } void TSheet_control::enable_check(bool on) { _check_enabled = on && _type[0] == 'C'; if (_type[0] == 'C') { XI_OBJ* column = find_column(1101); CHECK(column, "Can't find checkable column"); dword attr = xi_get_attrib(column); if (_check_enabled) attr |= XI_ATR_ENABLED; else attr &= ~XI_ATR_ENABLED; xi_set_attrib(column, attr); // Set new attributes } } bool TSheet_control::one_checked() const { bool yes = FALSE; if (check_enabled()) { const long first = _checked.first_one(); yes = first >= 0; } return yes; } // @mfunc Permette di attivare/disattivare una riga void TSheet_control::check( long n, // @parm Numero della riga da attivare/disattivare bool on) // @parm Operazione da effettuare sulla riga: // // @flag TRUE | Attiva la riga

.esima (default) // @flag FALSE | Disattiva la riga

.esima { if (n < 0) { if (on) { const long tot = items()-1; if (tot >= 0) { _checked.set(tot); // Forza le dimensioni del bit array _checked.set(); // Setta tutti i bit // Elimina i bit in eccesso alla fine dell'array for (long i = _checked.items()-1; i > tot; i--) _checked.reset(i); // Elimina i bit corrispondenti a righe disabilitate if (_disabled.first_one() >= 0) { for (long i = tot; i >= 0; i--) if (_disabled[i]) _checked.reset(i); } } } else _checked.reset(); // Aggiorna tutta la prima colonna XI_OBJ* column = xi_get_obj(_obj, FIRST_FIELD+1000); xi_cell_request(column); } else { if (!_disabled[n]) { _checked.set(n, on); // Aggiorna la riga if (_sheet->is_running()) { XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, rec2row(n), 1); xi_cell_request(&cell); } } } } void TSheet_control::toggle(long n) { check(n, !checked(n)); } // @doc INTERNAL // @mfunc Abilita/disabilita una riga void TSheet_control::enable_row( long n, // @parm Numero della riga da abilitare/diabilitare (default -1) bool on) // @parm Operazione da svolgere sulla riga // // @flag TRUE | Abilita la riga

-esima (default) // @flag FALSE | Disabilita la riga

-esima // @comm Se

e' minore di 0 allora vengono abilitate/disabilitate tutte le righe { if (n >= 0) { _disabled.set(n, !on); } else { if (on) _disabled.reset(); else { const long tot = items()-1; _disabled.set(tot); // Forza le dimensioni del bit-array _disabled.set(); // Disabilita tutte le righe // Resetta i bit in ecesso alla fine del bit-array for (long i = _disabled.items()-1; i > tot; i--) _disabled.reset(i); } } update(n); } void TSheet_control::save_columns_order(const TEdit_field& field) const { if (_save_columns_order) { TString parag; if (_sheet->get_ini_paragraph(field, parag)) { TConfig config(CONFIG_USER, parag); // Apre il file di configurazione TToken_string order(127); // Nuovo ordine delle colonne if (_save_columns_order == TRUE) // Se vale 3 devo solo resettare { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); for (int i = 0; i < num; i++) // Scorre tutte le colonne { TString h; char head[80]; xi_get_text(column[i], head, sizeof(head)); for (const char * s = head; *s; s++) { if (*s == '\n') h << "\\n"; else h << *s; } order.add(h); RCT rct; xi_get_rect(column[i], (XinRect *) &rct); order << ',' << rct.right - rct.left; } config.set("Browse", order, NULL, TRUE, field.dlg()); } else config.remove("Browse", field.dlg()); } } } void TSheet_control::load_columns_order(const TEdit_field& field) { TFilename parag; if (_sheet->get_ini_paragraph(field, parag)) { TConfig config(CONFIG_USER, parag); const int index = field.dlg(); TToken_string order(esc(config.get("Browse", NULL, index))); if (order.empty_items()) config.remove("Browse", index); else set_columns_order(&order); } _save_columns_order = FALSE; } void TSheet_control::align_column(int c, bool right) { XI_OBJ* column = find_column(c); CHECKD(column, "Can't find numeric column ", c); dword attr = xi_get_attrib(column); if (right) attr |= XI_ATR_RJUST; else attr &= ~XI_ATR_RJUST; xi_set_attrib(column, attr); _type[c] = right ? 'R' : ' '; } void TSheet_control::set_columns_order(TToken_string* order) { XI_OBJ* itf = get_interface(); XI_OBJ* focus = xi_get_focus(itf); xi_set_focus(itf); int num_cols; xi_get_member_list(_obj, &num_cols); //verificare // 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); if (order == NULL) { for (int index = 0; index < num_cols; index++) { const short cid = FIRST_FIELD + 1000 + index - 1; XI_OBJ* col = find_column(cid); if (col) { xi_move_column(col, index); RCT rct; xi_get_rect(col, (XinRect*)&rct); if (_default_width[index] != rct.right - rct.left) xi_column_set_pixel_width(col, _default_width[index]-offset); } } _save_columns_order = 0x3; } else { TString head(23); TToken_string col(8, ','); int pos = 0; for (col = order->get(0); !col.blank(); col = order->get(), pos++) { head = col.get(0); const int width = col.get_int(); XI_OBJ* column = find_column(head); if (column) // Controlla che esista ancora { if (pos > 0 && pos < num_cols) xi_move_column(column, pos); // Sposta la colonna se possibile if (width > XI_FU_MULTIPLE) // Se ha una larghezza valida xi_column_set_pixel_width(column, width - offset); } } } if (focus) xi_set_focus(focus); } HIDDEN long _rec_to_select = -1; // Certified 75% bool TSheet_control::event_handler(XI_OBJ* itf, XI_EVENT *xiev) { 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; } 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_CELL_REQUEST: { const long rec = xiev->v.cell_request.rec; const char* src = NULL; int nm; XI_OBJ** obj = xi_get_member_list(xiev->v.cell_request.list, &nm); const int num = xiev->v.cell_request.col_nbr; const int cid = obj[num]->cid - 1000; if (cid >= FIRST_FIELD) { if (rec >= 0 && rec < items()) { const int col = cid - FIRST_FIELD; switch (_type[col]) { case 'C': // Set value for "checkable" cell { const bool on = col == 0 ? checked(rec) : row(rec).get_char(col) > '0'; xiev->v.cell_request.icon_rid = on ? ICO_CHECK_ON : ICO_CHECK_OFF; } break; case 'M': // Set value for "roman" cell src = itor(row(rec).get_int(col)); break; case 'V': // Set value for "value" cell { const real r = row(rec).get(col); TCurrency c(r); src = c.string(true); } break; case 'P': // Set value for "price" cell { const real r = row(rec).get(col); TPrice c(r); src = c.string(true); } break; default: // Set value for "normal" cell src = row(rec).get(col); break; } if (_disabled[rec]) { xiev->v.cell_request.color = DISABLED_COLOR; xiev->v.cell_request.back_color = DISABLED_BACK_COLOR; } else if (rec == _cur_rec) { xiev->v.cell_request.color = FOCUS_COLOR; xiev->v.cell_request.back_color = FOCUS_BACK_COLOR; } else _sheet->get_cell_colors(rec, col, xiev->v.cell_request.color, xiev->v.cell_request.back_color); } else refused = TRUE; } else { xiev->v.cell_request.color = NORMAL_COLOR; // src = format("%ld", rec+1); // Niente piu' numeri di riga! } char* dst = xiev->v.cell_request.s; if (src && *src) { const int len = xiev->v.cell_request.len; strncpy(dst, src, len); dst[len-1] = '\0'; } else *dst = '\0'; } break; case XIE_DBL_CELL: { const long rec = row2rec(xiev->v.xi_obj->v.cell.row); if (!_disabled[rec]) { make_current(rec); _sheet->stop_run(K_ENTER); } else refused = TRUE; } break; case XIE_ON_LIST: _sheet->notify_focus_field(id()); break; case XIE_ON_ROW: { const long rec = row2rec(xiev->v.xi_obj->v.row); if (_disabled[rec]) { refused = TRUE; // Cerca la prossima riga abilitata e valida const int dir = rec > selected() ? +1: -1; const long max = items(); for (long r = rec+dir; r >= 0 && r < max; r += dir) if (!_disabled[r]) { _sheet->post_select(r); break; } } else { if (_sheet->_select_row < 0) make_current(rec); else _rec_to_select = rec; } } 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_SELECT: if (xiev->v.xi_obj->type == XIT_ROW) { const int row = xiev->v.select.xi_obj->v.row; const long rec = row2rec(row); if (row_enabled(rec)) { int num; XI_OBJ** column = xi_get_member_list(_obj, &num); const int col = xiev->v.select.column; const short cid = column[col]->cid - 1000; if (cid >= FIRST_FIELD) { if (check_enabled()) toggle(rec); } else { if (_cur_rec == rec) _sheet->on_key(K_CTRL+'G'); } const int c = _type[0] == 'C' ? 2 : 1; XI_OBJ cell; XI_MAKE_CELL(&cell, _obj, row, c); xi_set_focus(&cell); make_current(rec); } } refused = TRUE; 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: { if (_obj->v.list->hsb_win) _save_columns_order = TRUE; else refused = TRUE; // Non permetto il resize in assenza di scroll bar orizzontale } break; case XIE_BUTTON: if (xiev->v.xi_obj->type == XIT_LIST) _sheet->on_key(K_CTRL+'N'); break; case XIE_XVT_EVENT: if (xiev->v.xvte.type == E_CHAR) { const KEY k = e_char_to_key(&xiev->v.xvte); long rec = -1; switch (k) { case K_SPACE: if (_check_enabled) check(selected(), !checked(selected())); break; case K_ENTER: if (_disabled[_cur_rec]) break; case K_ESC: _sheet->stop_run(k); break; case K_LHOME: rec = 0; break; case K_PREV: rec = _cur_rec - visible_rows(); if (rec < 0) rec = 0; break; case K_NEXT: rec = _cur_rec + visible_rows(); if (rec >= items()) rec = items()-1; break; case K_LEND: rec = items()-1; break; default: break; } if (rec >= 0) { _sheet->post_select(rec); refused = TRUE; } } break; default: break; } return !refused; } /////////////////////////////////////////////////////////// // TQuery_field /////////////////////////////////////////////////////////// class TQuery_field : public TOperable_field { protected: virtual void create(WINDOW parent); public: TSheet_control* sheet() const { return (TSheet_control*)_ctl; } TQuery_field(TMask* m) : TOperable_field(m) { } virtual ~TQuery_field() { } }; void TQuery_field::create(WINDOW parent) { _ctl = new TSheet_control(parent, dlg(), _ctl_data._x, _ctl_data._y, _ctl_data._width, (int)_ctl_data._size, _ctl_data._flags, _ctl_data._prompt); } /////////////////////////////////////////////////////////// // TSheet /////////////////////////////////////////////////////////// #define DLG_QUERY 25883 TSheet::TSheet(short x, short y, short dx, short dy, const char* title, const char* head, byte buttons, short sht_y, WINDOW parent) : TMask(title, 1, dx, dy, x, y, parent), _sheet(NULL), _select_row(-1), _parked(-1) { create_bar(1); // Crea toolbar in alto TQuery_field* qf = new TQuery_field(this); qf->construct(DLG_QUERY, head, 1, sht_y, 0, page_win(0), "", 0); fields_array().add(qf); _sheet = qf->sheet(); if (!(buttons & 0x10)) add_button(DLG_SELECT, TR("~Selezione"), K_ENTER, TOOL_OK); if (check_enabled()) { add_button(DLG_USER, TR("~Tutti"), 0, TOOL_MULTISEL); set_handler(DLG_USER, tutti_handler); } if (buttons & 0x01) add_button(DLG_LINK, TR("Colle~ga"), K_CTRL+'G', TOOL_LINK); if (buttons & 0x02) add_button(DLG_NEWREC, TR("~Nuovo"), K_INS, TOOL_NEWREC); if (buttons & 0x04) add_button(DLG_DELREC, TR("~Elimina"), K_DEL, TOOL_DELREC); add_button(DLG_EXPORT, TR("~Excel"), 0, TOOL_EXCEL); set_handler(DLG_EXPORT, export_handler); if (buttons & 0x08) add_button(DLG_QUIT, "Fine", K_ESC, TOOL_QUIT); else add_button(DLG_CANCEL, "Annulla", K_ESC, TOOL_CANCEL); xvt_toolbar_realize(toolbar()); // Necessario per calcolare dimensioni corrette dello sheet } TSheet::~TSheet() { // delete _sheet; // Guy bestia: Already deleted by TMask! } TSheet_control& TSheet::sheet() { return *_sheet; } // Costruisce l'identificatore del paragrafo contenente la disposizione // delle colonne del campo f bool TSheet::get_ini_paragraph(const TEdit_field& f, TString& name) const { const TMask& m = f.mask(); name = m.source_file().name(); if (name.blank()) // Maschera dinamica name = main_app().name(); else name = name.before("."); // Nome della maschera senza estensione const int index = m.number(); CHECKD(index >= 0 && index <= 8, "Bad mask index:", index); if (index > 0) // Aggiunge l'eventuale numero di sotto-maschera name << '(' << index << ')'; return true; } // @doc INTERNAL // @mfunc Aggiunge un bottone nella finestra void TSheet::add_button( short id, // @parm Identificatore del bottone da aggiungere const char* caption, // @parm Testo del bottone da aggiungere KEY key, // @parm Combinazione di tasti corrispondente short bmp_up, // @parm Bitmap normale short bmp_dn) // @parm Bitmap premuta { if (id > 0 && bmp_up <= 0) { if (toolwin() == NULL_WIN) create_bar(-2); // Forza la creazione della bottom bar TButton_field& butt = TMask::add_button(id, MAX_PAGES, caption, 0, -1, 11, 2); butt.set_exit_key(key); } else { TButton_tool& butt = add_button_tool(id, caption, bmp_up); butt.set_exit_key(key); } } bool TSheet::get_cell_colors(int row, int col, COLOR& fore, COLOR& back) const { fore = back = 0; // Default colors if (EASY_RIDER && (row & 1)) back = EASY_RIDER_COLOR; return false; // Not assigned } void TSheet::repos_buttons() const { if (_sheet == NULL) return; // Sono ancora in fase di creazione: aspetta! WINDOW btnwin = toolwin(); // was win() if (btnwin != NULL_WIN) { int buttons = 0; RCT br; FOR_EACH_MASK_FIELD((*this), f, c) { if (c->parent() == btnwin && c->is_kind_of(CLASS_BUTTON_FIELD)) { if (buttons == 0) c->get_rect(br); buttons++; } } if (buttons > 0) { RCT wr; xvt_vobj_get_client_rect(btnwin, &wr); const short width = br.right - br.left; const short height = br.bottom - br.top; int space = (wr.right - buttons * width) / (buttons+1); if (space < 0) space = 0; int x = space; const int y = (wr.bottom - height) / 2; FOR_EACH_MASK_FIELD((*this), f, c) { if (c->parent() == btnwin && c->is_kind_of(CLASS_BUTTON_FIELD)) { buttons--; const PNT p = { y, x }; xvt_rect_set_pos(&br, p); c->set_rect(br); x += space+width; } } } } // Aggiusta anche lo spreadsheet se necessario TMask_field& s = field(DLG_QUERY); RCT wr; xvt_vobj_get_client_rect(s.parent(), &wr); RCT br; s.get_rect(br); if (br.bottom > wr.bottom || (wr.bottom-br.bottom) > 48) { br.bottom = wr.bottom - br.left; // Lascio uno spazio in fondo pari al bordo sinistro s.set_rect(br); } } void TSheet::force_update(int r) { _sheet->update(r); } void TSheet::start_run() { const bool on = items() > 0 && _sheet->one_enabled(); // Abilita selezione se c'e' almeno un elemento int pos = id2pos(DLG_SELECT); if (pos >= 0) fld(pos).enable(on); pos = id2pos(DLG_LINK); if (pos >= 0) fld(pos).enable(on); reset_parked(); repos_buttons(); force_update(); if (on) { long i = selected(); if (i < 0 || i >= items()) i = 0; if (row_disabled(i)) for (i = 0; row_disabled(i); i++); post_select(i); } } long TSheet::selected() const { return _sheet->selected(); } bool TSheet::check_enabled() const { return _sheet->check_enabled(); } void TSheet::enable_check(bool on) { _sheet->enable_check(on); } void TSheet::check(long n, bool on) { _sheet->check(n, on); } bool TSheet::checked(long n) const { return _sheet->checked(n); } long TSheet::checked() const { return _sheet->checked(); } bool TSheet::one_checked() const { return _sheet->one_checked(); } void TSheet::enable_row(long r, bool on) { _sheet->enable_row(r, on); } bool TSheet::row_enabled(long r) const { return _sheet->row_enabled(r); } bool TSheet::on_key(KEY key) { switch(key) { case K_ESC: stop_run(key); break; case K_CTRL+'E': if (items() && id2pos(DLG_SAVEREC) >= 0) export_handler(field(DLG_SAVEREC), K_SPACE); break; case K_DEL: if (items() && id2pos(DLG_DELREC) >= 0) stop_run(K_DEL); break; case K_CTRL+'N': if (id2pos(DLG_NEWREC) >= 0) stop_run(K_INS); break; case K_CTRL+'G': if (id2pos(DLG_LINK) >= 0) stop_run(key); break; case K_CTRL+'S': if (id2pos(DLG_SELECT) >= 0) stop_run(K_ENTER); break; default: key -= K_CTRL; if (key >= 'A' && key <= 'Z') { for (int i = fields()-1; i >= 0; i--) { TMask_field& f = fld(i); if (f.active() && f.is_kind_of(CLASS_BUTTON_FIELD)) { TButton_field& b = (TButton_field&)f; if (b.virtual_key() == key) { f.on_key(K_SPACE); if (b.exit_key() > 0) stop_run(b.exit_key()); return TRUE; } } } } break; } if (check_enabled() && items() > 0) { switch(key) { case K_CTRL+'T': tutti_handler(field(DLG_USER), K_SPACE); break; case K_F7: uncheck(-1); break; case K_F8: check(-1); break; default: break; } } return true; } // @doc INTERNAL // @mfunc Seleziona una riga facendola diventare corrente void TSheet::select( long n) // @parm Riga da selezionare { _sheet->select(n); } void TSheet::post_select(long rec) { _select_row = rec; } void TSheet::on_idle() { if (_select_row >= 0) { const short focus_id = low_get_focus_id(curr_win()); _sheet->select(_select_row); if (focus_id == _sheet->id()) _sheet->set_focus_rec(-1); _select_row = -1; } TMask::on_idle(); } // @doc INTERNAL // @mfunc Ritorna il contenuto di una riga // // @rdesc Ritorna la con tutti gli elemnti della riga TToken_string& TSheet::row( long n) // @parm Riga di cui ritorna il contenuto (default -1) // @comm Se viene passato un numero di riga minore di 1 viene ritornato il contenuto della riga // selezionata. { if (n < 0) n = selected(); if (n != _parked) get_row(_parked = n, _park); return _park; } bool TSheet::tutti_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TSheet& s = (TSheet&)f.mask(); if (s.check_enabled()) s.check(-1, !s.one_checked()); } return true; } 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; } bool TSheet::export_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TSheet& s = (TSheet&)f.mask(); TString cap; s.get_caption(cap); for (char* buf = cap.get_buffer(); *buf; buf++) { switch (*buf) { case '?': case '(': case ')': case '/': case '\\': case '*': *buf = ' '; break; default : break; } } cap.strip_spaces(); if (cap.blank()) cap = "export"; TFilename name; name.tempdir(); name.add(cap); name.ext("xls"); if (name.full()) // Dummy test { ofstream xls(name); const char sep = '\t'; TToken_string tab(128, sep); TString str; int columns = 0; XI_OBJ** pcols = xi_get_member_list(s._sheet->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 i = 0; i < s.items(); i++) { const TToken_string& r = s.row(i); tab.cut(0); for (int c = 1; c < columns; c++) { const int idx = pcols[c]->cid - 1101; r.get(idx, str); clean_white_space(str); if (str.not_empty()) { const char ct = s._sheet->column_type(idx); if (ct == 'P' || ct == 'V') xvt_str_number_format(str.get_buffer(), str.size()); tab.add(str, c-1); } } xls << tab << endl; } } if (name.exist()) xvt_sys_goto_url(name, "open"); } return true; } void TSheet::save_columns_order(const TEdit_field& field) { sheet().save_columns_order(field); } void TSheet::set_columns_order(TToken_string* col) { sheet().set_columns_order(col); } void TSheet::load_columns_order(const TEdit_field& field) { sheet().load_columns_order(field); } /////////////////////////////////////////////////////////// // TArray_sheet /////////////////////////////////////////////////////////// TArray_sheet::TArray_sheet(short x, short y, short dx, short dy, const char* caption, const char* head, byte buttons, short sht_y) : TSheet(x, y, dx, dy, caption, head, buttons, sht_y) { TToken_string cap(caption); if (cap.items() > 1) { cap.get(0); FOR_EACH_TOKEN(cap, tok) create_page(tok, 0); } } // Certified 99% bool TArray_sheet::destroy(int i) { uncheck(-1); enable_row(-1); reset_parked(); return _data.destroy(i, TRUE); } // @doc EXTERNAL // @mfunc Aggiunge un elemento allo sheet // // @rdesc Ritorna il numero di elementi presenti nello sheet long TArray_sheet::add( const TToken_string& s) // @parm Oggetto da aggiungere // @parm const TToken_string | *s | Oggetto da aggiungere passato per indirizzo // @syntax add(const TToken_string& s) // @syntax add(const TToken_string* s) { const long n = _data.add(s); if (n >= 0 && s[0]=='X' && check_enabled()) check(n); return n; } long TArray_sheet::add(TToken_string* s) { const long n = _data.add(s); if (n >= 0 && (*s)[0]=='X' && check_enabled()) check(n); return n; } long TArray_sheet::insert(const TToken_string& s, long n) { _data.insert(s, (int)n); const int maxrows=int(items()); for (int r = maxrows-1; r >n ; r--) { const bool enab= row_enabled(r); enable_row(r,row_enabled(r+1)); enable_row(r+1,enab); } return n; } /////////////////////////////////////////////////////////// // TCursor_sheet /////////////////////////////////////////////////////////// bool TCursor_sheet::in_key(const TFieldref& fr) const { const int logic = _cursor->file().num(); if (fr.file() != 0 && fr.file() != logic) return false; const RecDes& rd = prefix().get_recdes(logic); const int key = _cursor->key(); const KeyDes& kd = rd.Ky[key-1]; for (int i = 0; i < kd.NkFields; i++) { const int n = kd.FieldSeq[i] % MaxFields; if (strcmp(rd.Fd[n].Name, fr.name()) == 0) return true; } return false; } TCursor_sheet::TCursor_sheet(TCursor* cursor, const char* fields, const char* title, const char* head, byte buttons, short sht_y, WINDOW parent) : TSheet(sht_y ? 2 : 0, sht_y ? 2 : 0, sht_y ? -2 : 0, sht_y ? -2 : 0, title, head, buttons, sht_y, parent), _cursor(cursor) { TToken_string fldlst(fields); int campo = 0; for (const char* t = fldlst.get(0); t; t = fldlst.get(), campo++) { if (*t > ' ' && *t != '"') { const TFieldref fr(t, 0); TRecfield* rf = new TRecfield(_cursor->curr(fr.file()), fr.name(), fr.from(), fr.to() - 1); _fields.add(rf, campo); const TFieldtypes tipo = rf->type(); if (tipo == _intfld || tipo == _longfld || tipo == _realfld) { byte c = sheet().column_type(campo); if (c == ' ') sheet().align_column(campo, TRUE); } _key_cols.set(campo, in_key(fr)); } } } HIDDEN bool _can_post = FALSE; KEY TCursor_sheet::run() { _cursor->freeze(TRUE); select(_cursor->pos()); _can_post = TRUE; const KEY k = TSheet::run(); _cursor->freeze(FALSE); return k; } long TCursor_sheet::get_items() const { return _cursor ? _cursor->items() : 0; } void TCursor_sheet::get_row(long row, TToken_string& l) { *_cursor = (TRecnotype)row; l.cut(0); const int last = _fields.last(); for (int i = 0; i <= last; i++) { const TRecfield* rf = (const TRecfield*)_fields.objptr(i); const char* s = rf ? (const char*)*rf : ""; l.add(s); } } bool TCursor_sheet::get_cell_colors(int row, int col, COLOR& fore, COLOR& back) const { if (_key_cols[col]) { fore = 0; back = REQUIRED_BACK_COLOR; // Evidenzia i campi chiave return true; } return TSheet::get_cell_colors(row, col, fore, back); } /////////////////////////////////////////////////////////// // TBrowse_sheet /////////////////////////////////////////////////////////// HIDDEN TBrowse_sheet* _cur_browse = NULL; void TBrowse_sheet::add_custom_filter(const char* regexp) { TString filter = _original_filter; // Costruisco il nuovo filtro per estensione del vecchio if (regexp && *regexp) { if (_original_filter.not_empty()) { filter.insert("("); filter << ")&&("; } filter << regexp; if (_original_filter.not_empty()) filter << ')'; } TCursor& c = *cursor(); if (filter != c.filter()) // Cambio il filtro se necessario :-) { c.freeze(false); c.setfilter(filter, true); c.items(); // Forzo la ricostruzione del cursore c.freeze(true); // Forzo un aggiornamento a basso livello TQuery_field& qf = (TQuery_field&)TMask::field(DLG_QUERY); XI_OBJ* obj = qf.sheet()->xi_object(); xi_scroll_percent(obj, 100); xi_scroll_percent(obj, 0); _sel = 0; } } bool TBrowse_sheet::browse_field_handler(TMask_field& f, KEY k) { long rec = -1; if (k == K_F2) { f.reset(); k = K_SPACE; } if (k == K_SPACE) { TEdit_field& e = _cur_browse->field(); // Campo padre dello sheet TMask& m = e.mask(); // Maschera che contiene lo sheet TMask_field& c = m.field(f.dlg()); // Campo corrispondente sulla maschera // Ricopia su campo maschera if (f.is_edit()) { TEdit_field& ef = (TEdit_field&)f; const TString& wd = ef.get_window_data(); c.set(wd); } else c.set(f.get()); TBrowse* b = e.browse(); if (b != NULL) { b->do_input(FALSE); rec = b->cursor()->read(_isgteq); } } if (rec >= 0 && rec != _cur_browse->selected()) { _cur_browse->select(rec); // Non mettere post_select _can_post = TRUE; RCT r; f.get_rect(r); xvt_dwin_invalidate_rect(f.parent(), &r); // Non ricordo a cosa serva aggiornare il campo di testo } return TRUE; } bool TBrowse_sheet::last_browse_field_handler(TMask_field& f, KEY k) { const bool ok = browse_field_handler(f, k); if (ok) { if (k == K_TAB && _can_post) { _cur_browse->post_select(_cur_browse->selected()); _can_post = FALSE; } else { if (k == K_CTRL+K_TAB) { RCT r; f.get_rect(r); xvt_dwin_invalidate_rect(f.parent(), &r); } } } return ok; } bool TBrowse_sheet::filter_handler(TMask_field& f, KEY k) { if (k == K_SPACE) { TString expr; // Espressione di filtro complessiva if (!f.get().empty()) // Filtro attivato! { const short id = f.dlg()-500; TString e = f.mask().get(id); // Espressione regolare e.strip("\"'"); // Tolgo caratteri che potrebbero dare problemi if (!e.blank()) { const TBrowse& b = *_cur_browse->field().browse(); TToken_string ids = b.get_input_fields(); const int pos = ids.get_pos(id); if (pos >= 0) { TToken_string fns = b.get_input_field_names(); expr << fns.get(pos) << "?=\"" << e << '"'; } } if (expr.empty()) f.reset(); } _cur_browse->add_custom_filter(expr); } return true; } // @doc EXTERNAL // @mfunc Gestisce l'handler della finestra void TBrowse_sheet::handler( WINDOW win, // @parm Finestra da gestire EVENT* ep) // @parm Evento da gestire nella finestra { switch (ep->type) { case E_MOUSE_DOWN: switch (ep->v.mouse.button ) { case 1: { RCT r; sheet().get_rect(r); if (xvt_rect_has_point(&r, ep->v.mouse.where)) { MENU_ITEM* menu = xvt_res_get_menu(BROWSE_BAR); if (menu) { dictionary_translate_menu(menu); xvt_menu_popup(menu->child, win, ep->v.mouse.where, XVT_POPUP_LEFT_ALIGN, NULL); xvt_res_free_menu_tree(menu); return; // no default handling! } } } break; default: { RCT r; sheet().get_rect(r); if (xvt_rect_has_point(&r, ep->v.mouse.where) && _rec_to_select >= 0) { post_select(_rec_to_select); _rec_to_select = -1; } } break; } break; case E_COMMAND: switch (ep->v.cmd.tag-BROWSE_BAR) { case 1: save_columns_order(field()); return; case 2: set_columns_order(NULL); return; case 3: fld(0).on_key(K_F11); return; case 4: on_key(K_CTRL + 'E'); return; default: break; } break; default: break; } TCursor_sheet::handler(win, ep); } bool lst_handler(TMask_field& lst, KEY k) { if (k == K_SPACE) { TMask& m = lst.mask(); if (m.is_running()) { const int sel = atoi(lst.get()); m.on_key(K_CTRL + K_F1 + sel); } } return true; } void TBrowse_sheet::create_key_selector(TToken_string& ca) { const int items = ca.items(); if (items > 1) { TToken_string co; for (int i = 0; i < items; i++) co.add(i); TList_field& lst = add_list(69, 0, PR("Ordinamento per "), 1, 0, 16, "", co, ca); lst.set_handler(lst_handler); } } void TBrowse_sheet::update_key_selector(int sel) { if (id2pos(69) >= 0) // Has been created? set(69, sel, 0); // Don't fire any events! } TBrowse_sheet::TBrowse_sheet(TCursor* cursor, const char* fields, const char* title, const char* head, byte buttons, TEdit_field& f, TToken_string& sibling) : TCursor_sheet(cursor, fields, title, head, buttons, f.browse() ? f.browse()->input_fields()+1 : 1), _field(f), _sel(0), _original_filter(cursor->filter()) { const bool normal = f.browse() != NULL && sibling.full(); TToken_string ca; // Tag buttons if (normal) // normal mask query { int n = 0; for (const char* s = sibling.get(0); s && *s; s = sibling.get(), n++) { const short id = f.atodlg(s); const char* pr = sibling.get(); if (id == f.dlg()) _sel = n; ca.add(pr); } } else // ABnormal application query: F9 or F8 handler! { ca = f.prompt(); if (ca.blank()) ca = TR("Codice"); } create_key_selector(ca); TToken_string tids = head; TToken_string tfns = fields; TToken_string ids, fns; if (f.browse() != NULL) { ids = f.browse()->get_input_fields(); fns = f.browse()->get_input_field_names(); } TEditable_field* e = NULL; int y = 1; bool first = true; FOR_EACH_TOKEN(ids, i) { if (*i != '\0' && *i != '"' && strchr(i, '@') == NULL) { const short id = f.atodlg(i); const TMask_field& c = f.mask().field(id); if (c.is_editable() && c.active()) { int pos = ids.get_pos(id); CHECK(pos >= 0, "Invalid input field"); TString80 p = fns.get(pos); pos = tfns.get_pos(p); if (pos >= 0) { p = tids.get(pos); pos = p.find('@'); if (pos >= 0) p.cut(pos); } else p.cut(0); if (p.empty()) { p = c.prompt(); int a; // Toglie spazi e simboli iniziali dal prompt for (a = 0; p[a] && !isalnum(p[a]); a++); p.ltrim(a); } p.left_just(16); TString8 flags; if (c.roman()) flags << 'M'; if (c.right_justified()) flags << 'R'; if (c.uppercase()) flags << 'U'; if (c.zerofilled()) flags << 'Z'; if (!normal) flags << 'D'; // Disable ABnormal fields const int csize = c.size(); switch (c.class_id()) { case CLASS_EDIT_FIELD: { const int sz = csize > 50 ? 50 : csize; // Dimensione del campo di ricerca e = &add_string(c.dlg(), 0, p, 1, y++, csize, flags, sz); // Aggiunge campo con le icone di filtraggio add_checkbutton(c.dlg()+500, 0, "", sz+p.len()+ 2, y-1, 2, 1, "", 10112, 10113).set_handler(filter_handler); } break; case CLASS_ZOOM_FIELD: { e = &add_string(c.dlg(), 0, p, 1, y++, 32000, flags, 50); // Aggiunge campo con le icone di filtraggio add_checkbutton(c.dlg()+500, 0, "", 52 + p.len(), y-1, 2, 1, "", 10112, 10113).set_handler(filter_handler); } break; case CLASS_REAL_FIELD: e = &add_number(c.dlg(), 0, p, 1, y++, csize, flags); break; case CLASS_DATE_FIELD: e = &add_date(c.dlg(), 0, p, 1, y++, flags); break; default: e = NULL; break; } if (e != NULL && normal) { e->set_handler(browse_field_handler); e->set(c.get()); if (e->dlg() == f.dlg() || first) { first_focus(e->dlg()); first = false; } } } } } if (e != NULL && normal) e->set_handler(last_browse_field_handler); // carica ordine colonne anche per ricerche costruite run-time load_columns_order(f); } bool TBrowse_sheet::on_key(KEY k) { if (k >= K_CTRL+K_F1 && k < K_CTRL+K_F10) { const int what = k - K_CTRL - K_F1; if (what >= 0 && what != _sel) TWindow::stop_run(k); return true; } return TCursor_sheet::on_key(k); } KEY TBrowse_sheet::run() { _cur_browse = this; update_key_selector(_sel); const KEY key = TCursor_sheet::run(); _cur_browse = NULL; return key; }