#include #include #include #include const short FIRST_FIELD = 101; #if XVT_OS == XVT_OS_WIN #include #include /////////////////////////////////////////////////////////// // TSpreadsheet // ///////////////////////////////////////////////////////// #define K_PLUS '+' class TSpreadsheet:public TWindow { enum { ITF_CID = 0, LIST_CID = 1 }; TArray _str; // Array di TToken_strings TBit_array _column_disabled; TArray _disabled; // Array di TBit_array TMask _mask; int _columns; bool _dirty; XI_OBJ *_list; SPREADSHEET_NOTIFY _notify; static void xiev_handler (XI_OBJ * itf, XI_EVENT * xiev); void init (); protected: void list_handler (XI_EVENT * xiev); TMask_field *cell2field (const XI_OBJ * cell) const; void update_rec (int rec); void set_focus_cell (int riga, int colonna); void mask2str (int n); void str2mask (int n); KEY edit (int n); TMask_field *field (short id) const; int rec2row (int rec); int row2rec (int row); bool notify (int r, KEY k); public: void update (int row); TToken_string & row (int n); TArray & rows_array () const { return (TArray &) _str; } int add (TToken_string &); int insert (int rec); bool destroy (int rec = -1); void enable_column (int col, bool on = TRUE); void enable_cell (int row, int column, bool on = TRUE); bool cell_disabled (int row, int column) const; TMask & mask () { return _mask; } int items () const { return _str.items (); } int columns () const { return _columns; } bool dirty () const { return _dirty; } void set_notify (SPREADSHEET_NOTIFY n) { _notify = n; } void set_dirty (bool spork = TRUE) { _dirty = spork; } TSpreadsheet (short x, short y, short dx, short dy, const char *maskname, int maskno, const char *head, WINDOW parent); }; // Certified 99% void TSpreadsheet ::init () { static bool first = TRUE; if (!first) return; xvt_set_font (TASK_WIN, FF_FIXED, 0); DRAW_CTOOLS ct; win_get_draw_ctools (TASK_WIN, &ct); xi_set_font (&ct.font); xi_init (); xi_set_pref (XI_PREF_3D_LOOK, TRUE); // xi_set_pref(XI_PREF_COLOR_LIGHT, COLOR_CYAN); // xi_set_pref(XI_PREF_COLOR_CTRL, MASK_BACK_COLOR); // xi_set_pref(XI_PREF_COLOR_DARK, COLOR_GRAY); first = FALSE; } TSpreadsheet ::TSpreadsheet (short x, short y, short dx, short dy, const char *maskname, int maskno, const char *head, WINDOW parent) : _mask (maskname, NO_MODE, maskno), _notify (NULL) { const int NUMBER_WIDTH = 3; const int MAX_COL = 32; int width[MAX_COL]; init (); // Calcolo larghezza massima tabella TToken_string header (head); TToken_string new_header (256); int i = 0, tot_width = NUMBER_WIDTH + 1; for (const char *h = header.get (); h; h = header.get (), i++) { CHECKD (i < MAX_COL, "Tu meni calumns in scit: ", i); int w; char *at = strchr (h, '@'); if (at) { w = atoi (at + 1); *at = '\0'; } else w = strlen (h); width[i] = w + 1; const int cid = FIRST_FIELD + i; // Column & Field ID const TMask_field * f = field (cid); // Field on mask CHECKD (f, "The spreadsheet mask needs ALSO field ", cid); if (f->has_query ())w += 2; tot_width += w; new_header.add (h); } _columns = i; if (x < 0) x = 0; if (y < 0) y = 0; if (dx == 0) { dx = tot_width; if (dx > 76) dx = -x; } RCT rct = resize_rect (x, y, dx, dy, WO_TE, parent); rct.right -= 20; rct.bottom -= 8; XI_OBJ_DEF * itfdef = xi_create_itf_def (ITF_CID, (XI_EVENT_HANDLER) xiev_handler, &rct, (char *) maskname, PTR_LONG (this)); itfdef->v.itf->automatic_back_color = FALSE; itfdef->v.itf->back_color = MASK_BACK_COLOR; XI_OBJ_DEF * listdef = xi_add_list_def (itfdef, LIST_CID, 0, 0, rct.bottom - rct.top, XI_ATR_ENABLED | XI_ATR_VISIBLE, NORMAL_COLOR, NORMAL_BACK_COLOR, NORMAL_COLOR, DISABLED_BACK_COLOR, NORMAL_COLOR, LIST_CID); listdef->v.list->scroll_bar = TRUE; listdef->v.list->sizable_columns = TRUE; listdef->v.list->movable_columns = TRUE; listdef->v.list->scroll_bar_button = TRUE; listdef->v.list->fixed_columns = 1; listdef->v.list->width = rct.right - rct.left; listdef->v.list->min_cell_height = CHARY; listdef->v.list->min_heading_height = CHARY; listdef->v.list->white_space_color = COLOR_GRAY; XI_OBJ_DEF * coldef = xi_add_column_def (listdef, 0, XI_ATR_RJUST, 0, NUMBER_WIDTH, NUMBER_WIDTH, ""); 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; h = new_header.get (), i++) { const int cid = FIRST_FIELD + i; // Column & Field ID const TMask_field * f = field (cid); // Field on mask const int w = width[i] + (f->has_query ()? 2 : 0); // Column // width long flags = XI_ATR_EDITMENU | XI_ATR_AUTOSCROLL; if (f->class_id () == CLASS_REAL_FIELD) flags |= XI_ATR_RJUST; if (f->active ())flags |= XI_ATR_ENABLED; else _column_disabled.set (i); coldef = xi_add_column_def (listdef, cid, flags, cid, w, width[i], (char *) h); coldef->v.column->heading_platform = TRUE; coldef->v.column->center_heading = TRUE; } RCT itfrct; xi_get_def_rect (itfdef, &itfrct); offset_rect (&itfrct, rct.left, rct.top); itfrct.bottom++; WINDOW win = create_window (W_NO_BORDER, &itfrct, "", 0, parent, 0, EM_ALL, (EVENT_HANDLER) xi_event, 0L); CHECK (win, "Can't create a window for the spreadsheet"); set_win (win); // Set TWindow::_win itfdef->v.itf->win = win; xi_create (NULL, itfdef); xi_tree_free (itfdef); XI_OBJ * itf = xi_get_itf (win); _list = xi_get_obj (itf, LIST_CID); } // Converts a row number in the correspondig record number int TSpreadsheet ::row2rec (int row) { int rows; const long *rec = xi_get_list_info (_list, &rows); #ifdef DBG if (row < 0 || row >= rows) { error_box ("Line %d is not visible", row); return 0L; } #endif return (int) rec[row]; } // Converts a row number in the correspondig record number int TSpreadsheet ::rec2row (int record) { int rows; const long *rec = xi_get_list_info (_list, &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 TMask_field * TSpreadsheet ::cell2field (const XI_OBJ * cell) const { const int pos = cell->v.cell.column; int num; XI_OBJ ** column = xi_get_member_list (_list, &num); TMask_field * good = NULL; for (short id = column[pos]->cid;; id += 100) { TMask_field * f = field (id); if (f == NULL) break; good = f; if (f->active ())break; } return good; } void TSpreadsheet ::update_rec (int rec) { const int riga = rec2row (rec); if (riga >= 0) { XI_OBJ row; XI_MAKE_ROW (&row, _list, riga); xi_cell_request (&row); // Update internal values xi_set_row_height (&row, CHARY + 1); // Force row updating } } void TSpreadsheet ::set_focus_cell (int riga, int colonna) { set_front_window (win ()); XI_OBJ cell; XI_MAKE_CELL (&cell, _list, rec2row (riga), colonna); xi_set_focus (&cell); } int TSpreadsheet ::insert (int rec) { const bool ok = notify (rec, K_INS); if (!ok) return -1; TToken_string s; // Empty row const int r = _str.insert (s, rec); _disabled.insert (NULL, rec); xi_insert_row (_list, INT_MAX); xi_cell_request (_list); return r; } bool TSpreadsheet ::destroy (int rec) { bool ok = TRUE; if (rec < 0) { _disabled.destroy (); _str.destroy (); } else { _disabled.destroy (rec, TRUE); // Destroy enable info ok = _str.destroy (rec, TRUE); // Destroy line enable_cell (_str.items (), -1); // Enable last line } if (ok) xi_cell_request (_list); return ok; } void TSpreadsheet ::update (int row) { if (row < 0) { xi_cell_request (_list); xi_scroll (_list, XI_SCROLL_FIRST); set_front_window (win ()); } else update_rec (row); } void TSpreadsheet ::xiev_handler (XI_OBJ * itf, XI_EVENT * xiev) { TSpreadsheet * es = (TSpreadsheet *) xi_get_app_data (itf); CHECK (es, "NULL Edit sheet in xi event"); es->list_handler (xiev); } // Certified 75% void TSpreadsheet ::list_handler (XI_EVENT * xiev) { static TMask_field * edit_field = NULL; // Current edit field static int cur_row = 0, cur_col = 0; // Current cell static bool row_dirty = FALSE; // Current row changed static bool check_enabled = TRUE; // Perform OFF_ROW checks switch (xiev->type) { case XIE_GET_FIRST: { const long max = items (); if (max <= 0L) { xiev->refused = TRUE; break; } long n = max * (long)xiev->v.rec_request.percent / 100L; if (n < 0L) n = 0L; xiev->v.rec_request.data_rec = n; } 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 ()) xiev->refused = TRUE; else xiev->v.rec_request.data_rec = n; } break; case XIE_CELL_REQUEST: { const int rec = (int)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; if (cid >= FIRST_FIELD) { if (rec < items ()) { const int col = cid - FIRST_FIELD; TMask_field * f = field (cid); src = row (rec).get (col); // Set value for cell if (src && *src && f->class_id () == CLASS_REAL_FIELD) { src = f->picture_data (src, FALSE); // Get formatted string } if (field (cid)->has_query ()) { xiev->v.cell_request.button = xiev->v.cell_request.button_on_focus = TRUE; } if (cell_disabled (rec, col)) xiev->v.cell_request.back_color = MASK_BACK_COLOR; } } else src = format ("%d", rec + 1); const int len = xiev->v.cell_request.len; char *dst = xiev->v.cell_request.s; if (src) { strncpy (dst, src, len); if (isspace (*dst)) { TFixed_string d (dst); d.ltrim (); } } else *dst = '\0'; } break; case XIE_BUTTON: if (xiev->v.xi_obj->type == XIT_CELL) { if (edit_field) { const char *val = xi_get_text (xiev->v.xi_obj, NULL, -1); edit_field->set (val); // Update current cell check_enabled = FALSE; // Disable checks if (!row_dirty) notify (cur_row, K_SPACE); if (edit_field->on_key (K_F9)) // Show search sheet { mask2str (cur_row); // Update row row_dirty = TRUE; } check_enabled = TRUE; // Enable checks xi_set_focus (xiev->v.xi_obj); // Restore focus to cell } } else if (xiev->v.xi_obj->type == XIT_LIST) insert (-1); break; case XIE_DBL_CELL: { check_enabled = FALSE; cur_row = row2rec (xiev->v.xi_obj->v.cell.row); cur_col = xiev->v.xi_obj->v.cell.column; const KEY k = edit (cur_row); if (k == K_ENTER) { update_rec (cur_row); row_dirty = TRUE; } xi_set_focus (xiev->v.xi_obj); check_enabled = TRUE; } break; case XIE_ON_ROW: { const int rec = row2rec (xiev->v.xi_obj->v.row); if (rec < items ()) { cur_row = rec; str2mask (rec); row_dirty = FALSE; } else xiev->refused = TRUE; } break; case XIE_OFF_ROW: if (row_dirty && check_enabled) { check_enabled = FALSE; // Avoid recursion! set_dirty (); str2mask (cur_row); // It shouldn't have to be necessary bool ok = _mask.check_fields (); if (ok) { mask2str (cur_row); ok = notify (cur_row, K_ENTER); // Notify edit } else { xiev->refused = TRUE; set_dirty (2); // Set error status set_focus_cell (cur_row, cur_col); } check_enabled = TRUE; } break; case XIE_ON_CELL: { TMask_field * f = cell2field (xiev->v.xi_obj); const int col = (f->dlg () - FIRST_FIELD) % 100; if (!cell_disabled (cur_row, col)) { edit_field = f; cur_col = xiev->v.xi_obj->v.cell.column; xi_set_color (xiev->v.xi_obj, XIC_BACK, FOCUS_BACK_COLOR); } else { xiev->refused = TRUE; // Refuse focus on disabled cells } } break; case XIE_OFF_CELL: if (edit_field && check_enabled) { TMask_field * c = edit_field; // Save field, it could turn out to // be NULL on error const TString80 old (c->get ()); // Save old value on mask const TString80 nuo (c->picture_data (xi_get_text (xiev->v.xi_obj, NULL, -1), TRUE)); if (old != nuo) { check_enabled = FALSE; if (!row_dirty) { notify (cur_row, K_SPACE); row_dirty = TRUE; } c->set (nuo); // Set new mask value c->set_dirty (); // Get it dirty! if (c->on_key (K_TAB) == FALSE) // Test it { c->set (old); xi_set_focus (xiev->v.xi_obj); } else { mask2str (cur_row); // Update sheet row edit_field = NULL; // Reset current field } check_enabled = TRUE; } xi_set_color (xiev->v.xi_obj, XIC_BACK, NORMAL_BACK_COLOR); } break; case XIE_GET_PERCENT: { const long rec = xiev->v.get_percent.record; long n = items (); if (n < 1) n = 1; xiev->v.get_percent.percent = int (rec * 100L / n); } break; case XIE_CLEANUP: set_win (NULL_WIN); break; case XIE_XVT_EVENT: { EVENT * ep = &xiev->v.xvte; switch (ep->type) { case E_FOCUS: if (ep->v.active == FALSE) { XI_OBJ * itf = xi_get_itf (win ()); const bool ok = (bool) xi_move_focus (itf); if (!ok) xiev->refused = TRUE; } break; case E_CHAR: if (edit_field) { const KEY k = e_char_to_key (ep); switch (k) { case K_F1: check_enabled = FALSE; // Disable checks edit_field->on_key (K_F1); set_focus_cell (cur_row, cur_col); check_enabled = TRUE; // Enable checks // xiev->refused = TRUE; break; case K_F2: case K_F3: case K_F8: case K_F9: { check_enabled = FALSE; // Disable checks if (!row_dirty) notify (cur_row, K_SPACE); if (edit_field->on_key (k)) { mask2str (cur_row); row_dirty = TRUE; } set_focus_cell (cur_row, cur_col); check_enabled = TRUE; // Enable checks } break; case K_PLUS: insert (cur_row); xiev->refused = TRUE; break; case K_PREV: xi_scroll (_list, XI_SCROLL_PGUP); // xiev->refused = TRUE; break; case K_NEXT: xi_scroll (_list, XI_SCROLL_PGDOWN); // xiev->refused = TRUE; break; case K_HOME: xi_scroll (_list, XI_SCROLL_FIRST); // xiev->refused = TRUE; break; case K_END: xi_scroll (_list, XI_SCROLL_LAST); // xiev->refused = TRUE; break; default: break; } } break; default: break; } } break; default: break; } } int TSpreadsheet ::add (TToken_string & t) { return _str.add (t); } TToken_string & TSpreadsheet ::row (int n) { return (TToken_string &) _str[n]; } #else #include /////////////////////////////////////////////////////////// // TSpreadsheet // ///////////////////////////////////////////////////////// class TSpreadsheet:public TArray_sheet { TMask _mask; SPREADSHEET_NOTIFY _notify; bool _dirty; TBit_array _column_disabled; TArray _disabled; // Array di TBit_array protected: virtual bool on_key (KEY key); KEY edit (int n); bool notify (int r, KEY k); TMask_field *field (short id) const; void mask2str (int riga); void str2mask (int riga); public: TSpreadsheet (short x, short y, short dx, short dy, const char *maskname, int maskno, const char *head, WINDOW parent); TArray & rows_array () const { return data (); } TMask & mask () { return _mask; } void set_notify (SPREADSHEET_NOTIFY n) { _notify = n; } void set_dirty (bool spork = TRUE) {_dirty = spork; } bool dirty ()const { return _dirty; } void enable_column (int col, bool on); void enable_cell (int row, int column, bool on = TRUE); bool cell_disabled (int row, int column) const; }; TSpreadsheet ::TSpreadsheet (short x, short y, short dx, short dy, const char *maskname, int maskno, const char *head, WINDOW parent) :TArray_sheet (x, y, dx, dy, maskname, head, 0, parent), _mask (maskname, NO_MODE, maskno), _notify (NULL) { } bool TSpreadsheet ::on_key (KEY k) { switch (k) { case K_SHIFT_ENTER: case K_ESC: mask ().send_key (k, 0); return TRUE; case K_ENTER: // Selezione riga per editing if (items () < 1) k = K_INS; // Se vuoto crea riga da editare case K_INS: case 'A': // Aggiunge dopo case 'I': // Aggiunge prima { int n = (int)selected (); if (k != K_ENTER) { if (k == K_INS) n = items (); else // Aggiunge alla fine if (k == 'A') n++; if (n < 0) n = 0; else if (n > items ())n = items (); // Controlla range n if (notify (n, K_INS) == FALSE) // Chiede l'ok alla applicazione return FALSE; insert (TToken_string (80), n); // Aggiunge una riga vuota k = K_INS; // Inserimento in corso } edit (n); // Edita riga selezionata o creata set_front_window (win ()); // Aggiorna sheet a video open (); } break; case K_TAB: case K_BTAB: case K_SHIFT_TAB: dispatch_e_char (get_parent (win ()), k); return TRUE; default: break; } return TArray_sheet ::on_key (k); } #endif TMask_field * TSpreadsheet ::field (short id) const { const int pos = _mask.id2pos (id); if (pos < 0) return NULL; return &_mask.fld (pos); } void TSpreadsheet ::mask2str (int riga) { TToken_string & r = row (riga); r.cut (0); for (short id = FIRST_FIELD;; id++) { const int pos = _mask.id2pos (id); if (pos < 0) break; r.add (_mask.fld (pos).get ()); } #if XVT_OS == XVT_OS_WIN update_rec (riga); #endif } // Certified 50% void TSpreadsheet ::enable_cell (int row, int column, bool on) { TBit_array * ba = (TBit_array *) _disabled.objptr (row); if (ba == NULL) { if (on) return; // Don't waste time and memory ba = new TBit_array (_column_disabled); _disabled.add (ba, row); } if (column >= 0) ba->set (column, !on); else { if (on) _disabled.destroy (row, FALSE); // Let's save some memory! else { #if XVT_OS == XVT_OS_WIN ba->set (_columns); // Force right array size #else ba->set (32); // Force array size #endif ba->set (); // Set all bits } } } void TSpreadsheet ::enable_column (int col, bool on) { _column_disabled.set (col, !on); } // Certified 99% bool TSpreadsheet ::cell_disabled (int row, int column) const { TBit_array * ba = (TBit_array *) _disabled.objptr (row); if (ba == NULL) return _column_disabled[column]; // Use default return (*ba)[column]; } // Certified 75% void TSpreadsheet ::str2mask (int riga) { if (riga == items ()) { _mask.reset (); mask2str (riga); return; } TToken_string & r = row (riga); r.restart (); TString80 val; for (short id = FIRST_FIELD;; id++) { int pos = _mask.id2pos (id); if (pos < 0) break; val = r.get (); // Value to set int rid = id; while (pos >= 0) { TMask_field & f = _mask.fld (pos); f.set (val); f.enable (!cell_disabled (riga, id - FIRST_FIELD)); if (f.active () || f.ghost ()) { if (f.has_check ())f.check (STARTING_CHECK); f.on_hit (); } f.set_dirty (FALSE); rid += 100; pos = _mask.id2pos (rid); } } _mask.set_caption (format ("Riga %d", riga + 1)); } // Certified 100% bool TSpreadsheet ::notify (int n, KEY k) { return _notify ? _notify (n, k) : TRUE; } // Certified 99% KEY TSpreadsheet ::edit (int n) { str2mask (n); notify (n, K_SPACE); // Notifica intenzione di modificare const KEY k = _mask.run (); 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); str2mask (n); } } return k; } /////////////////////////////////////////////////////////// // TSheet_field // ///////////////////////////////////////////////////////// // Certified 100% TSheet_field ::TSheet_field (TMask * m) : TMask_field (m), _sheet (NULL) { } // Certified 100% word TSheet_field ::class_id ()const { return CLASS_SHEET_FIELD; } // Certified 100% TSheet_field ::~TSheet_field () { CHECK (_sheet, "Can't delete NULL sheet"); delete _sheet; } // Certified 100% void TSheet_field ::reset () { _sheet->destroy (); } void TSheet_field ::parse_head (TScanner & scanner) { _width = scanner.integer (); _size = scanner.integer (); if (_size == 0) _size = -1; } // Certified 100% bool TSheet_field ::parse_item (TScanner & scanner) { if (scanner.key () == "IT") { _head.add (scanner.string ()); return TRUE; } return TMask_field ::parse_item (scanner); } // Certified 100% void TSheet_field ::create (WINDOW parent) { const TMask & m = mask (); _sheet = new TSpreadsheet (_x, _y, _width, _size, m.source_file (), m.sheets (), _head, parent); _win = _sheet->win (); enable_window (_win, enabled ()); show_window (_win, showed ()); } // Certified 100% TArray & TSheet_field ::rows_array ()const { return _sheet->rows_array (); } // Certified 100% // Ritorna l'indice della prima riga vuota dello sheet int TSheet_field ::first_empty ()const { const int max = _sheet->items (); for (int n = 0; n < max; n++) if (_sheet->row (n).empty_items ()) break; return n; } TToken_string & TSheet_field ::row (int n) { const int max = _sheet->items (); if (n < 0 || n >= max) { if (n < 0) n = first_empty (); if (n >= max) n = _sheet->add (TToken_string (80)); } return _sheet->row (n); } void TSheet_field ::force_update (int r) { #if XVTWS == WMWS _sheet->open (); #else _sheet->update (r); #endif } void TSheet_field ::set_window_data (const char *) { _sheet->set_dirty (FALSE); set_dirty (FALSE); } void TSheet_field ::set_field_data (const char *) { set_dirty (_sheet->dirty ()); } int TSheet_field ::items ()const { return (int)_sheet->items (); } void TSheet_field ::set_notify (SPREADSHEET_NOTIFY n) { _sheet->set_notify (n); } void TSheet_field ::enable_column (int column, bool on) { _sheet->enable_column (column, on); } void TSheet_field ::enable_cell (int row, int column, bool on) { _sheet->enable_cell (row, column, on); } TMask & TSheet_field ::sheet_mask ()const { return _sheet->mask (); } bool TSheet_field ::on_key (KEY k) { if (k == K_TAB) { const bool spork = _sheet->dirty (); if (spork < FALSE || spork > TRUE) return error_box ("Tabella inconsistente: correggere i valori errati prima di uscirne"); set_dirty (spork); } return TMask_field ::on_key (k); }