3893 lines
107 KiB
C++
Executable File
3893 lines
107 KiB
C++
Executable File
#define XI_INTERNAL
|
||
#include <xinclude.h>
|
||
|
||
extern "C"
|
||
{
|
||
#include <xiutils.h>
|
||
}
|
||
|
||
#include <colors.h>
|
||
#include <config.h>
|
||
#include <controls.h>
|
||
#include <currency.h>
|
||
#include <diction.h>
|
||
#include <msksheet.h>
|
||
#include <recarray.h>
|
||
#include <recset.h>
|
||
#include <relation.h>
|
||
#include <urldefid.h>
|
||
#include <utility.h>
|
||
|
||
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 _properties;
|
||
// @cmember:(INTERNAL) Array delle proprieta' standard di tutte le righe
|
||
TRow_property* _row_properties;
|
||
|
||
// @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 <t SPREADSHEET_NOTIFY>)
|
||
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 <mf TSpreadsheet::on_idle>)
|
||
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 <mf TSpreadsheet::xiev_handler>)
|
||
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; }
|
||
virtual void set_rect(const RCT& r) { xi_set_rect(_obj, (XI_RCT*)&r, true); }
|
||
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 <mf TSpreadsheet::list_handler>)
|
||
bool off_cell_handler(XI_OBJ* cell = NULL);
|
||
|
||
// @cmember Ritorna il campo della maschera corrispondente alla cella dello
|
||
// spreadsheet indicata da <p pos>
|
||
TOperable_field* col2field(int pos) const;
|
||
// @cmember Controlla se esiste il campo della maschera corrispondente alla cella dello
|
||
// spreadsheet indicata da <p pos>
|
||
TOperable_field* test_field(int pos) const;
|
||
// @cmember Ritorna il campo della maschera corrispondente alla cella dello
|
||
// spreadsheet indicata da <p cell> (chiama <mf TMask::col2field>)
|
||
TOperable_field* cell2field(const XI_OBJ* cell) const;
|
||
// @cmember Ritorna la posizione della colonna con identificatore <p cid>
|
||
int cid2col(short cid) const;
|
||
// @cmember Ritorna la colonna logica con identificatore <p cid>
|
||
int cid2index(short cid) const;
|
||
// @cmember Ritorna la colonna corrispondente al campo <p f> 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 <mf TSpreadsheet::set_notify>
|
||
bool notify(int row, KEY k);
|
||
// @cmember Chiama la funzione specificata con la <mf TSpreadsheet::set_notify>
|
||
// ogni volta che c'e' una modifica nello spreadsheet
|
||
void notify_change();
|
||
// @cmember Permette di fare tutti gli aggiornamenti necessari (indicati in
|
||
// <p _needs_update>)
|
||
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 <p col>
|
||
bool column_disabled(int col) const { return _column_disabled[col]; }
|
||
// @cmember Ritorna l' abilitazione della colonna <p col>
|
||
bool column_enabled(int col) const { return !column_disabled(col); }
|
||
|
||
// @cmember Ritorna il contenuto della riga <p n>-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 <mf TArray::add>)
|
||
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 <p rec>
|
||
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 <mf TMask::activate>)
|
||
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 <p fromindex> alla posizione
|
||
// <p toindex>
|
||
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 <p n>-esimo ed
|
||
// aggiorna il display
|
||
void mask2str(int n);
|
||
// @cmember Ricopia i campi del record <p n>-esimo nella maschera ed
|
||
// aggiorna il display
|
||
void str2mask(int n);
|
||
// @cmember Apre la maschera della riga <p n>-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 <p _notify> al valore <p n>
|
||
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),
|
||
_row_properties(NULL)
|
||
{
|
||
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;
|
||
|
||
// 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;
|
||
|
||
if (l->max_lines_in_cell > 1 && acqua == CLASS_EDIT_FIELD && v_width[i] > 20)
|
||
coldef->v.column->wrap_text = TRUE;
|
||
}
|
||
|
||
// 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()
|
||
{
|
||
delete _row_properties;
|
||
}
|
||
|
||
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 = 0;
|
||
XI_OBJ** column = xi_get_member_list(_obj, &num);
|
||
|
||
if (num > 1)
|
||
{
|
||
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);
|
||
|
||
_properties.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 <p rec> 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();
|
||
_properties.destroy();
|
||
set_dirty(_row_dirty = false);
|
||
}
|
||
else
|
||
{
|
||
_properties.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 <p row> 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
|
||
{
|
||
xiev->v.cell_request.color = COLOR_BLACK; // Ignored :-(
|
||
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)
|
||
{
|
||
if (active())
|
||
{
|
||
notify_change();
|
||
_cell_dirty = true;
|
||
_edit_field->set_dirty();
|
||
}
|
||
else
|
||
refused = true;
|
||
}
|
||
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())) && active())
|
||
{
|
||
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);
|
||
}
|
||
if (row < 0)
|
||
{
|
||
TRow_property* prop = get_property(row, true);
|
||
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)
|
||
prop = get_property(-1, 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);
|
||
_properties.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 = _properties.remove(fromindex);
|
||
_properties.insert(p, toindex, true);
|
||
_properties.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)
|
||
{
|
||
if (row < 0)
|
||
{
|
||
if (_row_properties == NULL && create)
|
||
_row_properties = new TRow_property;
|
||
return _row_properties;
|
||
}
|
||
|
||
TRow_property* p = (TRow_property*)_properties.objptr(row);
|
||
if (p == NULL)
|
||
{
|
||
if (create)
|
||
{
|
||
p = new TRow_property;
|
||
_properties.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 (owner().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;
|
||
case 'L': _flags.read_only = true; break;
|
||
case 'D': _flags.enable_default = false; 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();
|
||
((TSpreadsheet*)_ctl)->activate(false);
|
||
}
|
||
if (_flags.read_only)
|
||
{
|
||
((TSpreadsheet*)_ctl)->activate(false);
|
||
}
|
||
|
||
const TMask& s = sheet_mask();
|
||
for (short id = FIRST_FIELD; ; id++)
|
||
{
|
||
if (s.id2pos(id) < 0)
|
||
{
|
||
_last_column_id = id - 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
((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 <p n> e maggiore del numero massimo di righe presenti
|
||
// o minore di 0 viene aggiunta una riga vuota in fondo allo spreadsheet
|
||
// (viene chiamata la <mf TSpreadsheet::add>)
|
||
{
|
||
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 <p r> 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)
|
||
{
|
||
static short _last_cid = 0;
|
||
if (id != _last_cid)
|
||
{
|
||
_last_cid = id;
|
||
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 <20> 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)
|
||
{
|
||
TMask& m = sheet_row_mask(i-1);
|
||
TToken_string &row= this->row(i-1);
|
||
|
||
row.cut(0);
|
||
for (short id = FIRST_FIELD; id <= _last_column_id; id++)
|
||
{
|
||
const TFieldref* dbfield = m.field(id).field();
|
||
if (dbfield == NULL && m.id2pos(id+100) > 0) // Succede coi campi analitici
|
||
dbfield = m.field(id+100).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)
|
||
{
|
||
TMask& m = sheet_row_mask(i-1);
|
||
for (short id = FIRST_FIELD; id <= _last_column_id; id++)
|
||
{
|
||
const TFieldref* dbfield = m.field(id).field();
|
||
if (dbfield == NULL && m.id2pos(id+100) > 0) // Succede coi campi analitici
|
||
dbfield = m.field(id+100).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);
|
||
}
|