1047 lines
24 KiB
C++
Executable File
1047 lines
24 KiB
C++
Executable File
#include <msksheet.h>
|
|
#include <real.h>
|
|
#include <urldefid.h>
|
|
#include <utility.h>
|
|
|
|
const short FIRST_FIELD = 101;
|
|
|
|
#if XVT_OS == XVT_OS_WIN
|
|
|
|
#include <xil.h>
|
|
#include <colors.h>
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// 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!
|
|
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
|
|
}
|
|
if (!ok)
|
|
{
|
|
xiev->refused = TRUE;
|
|
set_dirty(2); // Set error status
|
|
}
|
|
else
|
|
{
|
|
xvt_statbar_set("");
|
|
set_dirty();
|
|
}
|
|
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
|
|
/*
|
|
const int r = xiev->v.xi_obj->v.cell.row;
|
|
int c = xiev->v.xi_obj->v.cell.column;
|
|
c += cur_col > col ? -1 : +1;
|
|
if (c < 1) c = _columns-1; else
|
|
if (c >= _columns) c = 1;
|
|
|
|
XI_OBJ cell;
|
|
XI_MAKE_CELL(&cell, _list, r, c);
|
|
xi_move_focus(&cell);
|
|
*/
|
|
}
|
|
}
|
|
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);
|
|
xiev->refused = TRUE;
|
|
}
|
|
else
|
|
{
|
|
mask2str(cur_row); // Update sheet row
|
|
edit_field = NULL; // Reset current field
|
|
}
|
|
check_enabled = TRUE;
|
|
}
|
|
if (!xiev->refused)
|
|
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
|
|
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_PREV:
|
|
xi_scroll(_list, XI_SCROLL_PGUP);
|
|
break;
|
|
case K_NEXT:
|
|
xi_scroll(_list, XI_SCROLL_PGDOWN);
|
|
break;
|
|
case K_HOME:
|
|
xi_scroll(_list, XI_SCROLL_FIRST);
|
|
break;
|
|
case K_END:
|
|
xi_scroll(_list, XI_SCROLL_LAST);
|
|
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 <sheet.h>
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// 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.set_dirty(FALSE);
|
|
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
|
|
}
|
|
|
|
|
|
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_hit()
|
|
{
|
|
if (!mask().is_running())
|
|
{
|
|
force_update();
|
|
_sheet->set_dirty(FALSE);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
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 le celle errate");
|
|
set_dirty(spork);
|
|
}
|
|
|
|
return TMask_field::on_key(k);
|
|
}
|