81534d006f
Files correlati : tutti Ricompilazione Demo : [ ] Commento : 0001213: mappa di login, fuoco Lla mappa di login si apre ma per poter digitare la password bisogna cliccare sul campo col mouse. Stesso problema richiamando le ricerche, per esempio quella clienti. git-svn-id: svn://10.65.10.50/trunk@18591 c028cbd2-c16b-5b4b-a496-9718f37d4682
2650 lines
68 KiB
C++
Executable File
2650 lines
68 KiB
C++
Executable File
#include <xinclude.h>
|
|
|
|
#include <controls.h>
|
|
#include <browfile.h>
|
|
#include <colors.h>
|
|
#include <diction.h>
|
|
#include <msksheet.h>
|
|
#include <prefix.h>
|
|
#include <recarray.h>
|
|
#include <relation.h>
|
|
#include <statbar.h>
|
|
#include <toolfld.h>
|
|
#include <treectrl.h>
|
|
#include <urldefid.h>
|
|
#include <utility.h>
|
|
|
|
#include <user.h>
|
|
|
|
HIDDEN const char* const MASK_EXT = "msk";
|
|
|
|
///////////////////////////////////////////////////////////
|
|
// TMask methods
|
|
///////////////////////////////////////////////////////////
|
|
|
|
void TMask::init_mask()
|
|
{
|
|
_msg_field = 0;
|
|
_msg_key = 0;
|
|
_pages = 0; // Azzera numero pagine e sheets
|
|
if (_mask_num == 0) _sheet = NULL; // Non appartiene a nessuno sheet
|
|
|
|
_enabled.set(MAX_PAGES);
|
|
_enabled.set(); // Abilita tutte le pagine
|
|
_should_check = true;
|
|
|
|
_focus = _first_focus = 0; // Il primo ha il focus
|
|
_page = -1; // Nessuna pagina corrente
|
|
_handler = NULL; // Nessun handler utente
|
|
_mode = NO_MODE; // Inizializza modo
|
|
|
|
_error_severity = 0;
|
|
_msg_field = 0;
|
|
_msg_key = 0;
|
|
_test_fld = -1;
|
|
_last_test = -1;
|
|
|
|
_toolwin = _toolbar = _notebook = _single = NULL_WIN;
|
|
memset(_pagewin, 0, sizeof(_pagewin)); // Azzero lista pagine
|
|
}
|
|
|
|
WINDOW TMask::page_win(int p) const
|
|
{
|
|
if (p >= MAX_PAGES)
|
|
return p == MAX_PAGES ? toolwin() : toolbar();
|
|
if (p >= 0 && p < _pages)
|
|
return _pagewin[p];
|
|
return p <= 0 ? win() : NULL_WIN;
|
|
}
|
|
|
|
WINDOW TMask::curr_win() const
|
|
{
|
|
return page_win(_page > 0 && _page < _pages ? _page : 0);
|
|
}
|
|
|
|
TMask::TMask() : _mask_num(0)
|
|
{ init_mask(); }
|
|
|
|
TMask::TMask(const char* title, int pages, int cols, int rows,
|
|
int xpos, int ypos, WINDOW parent) : _mask_num(0)
|
|
{
|
|
init_mask();
|
|
if (pages > 1)
|
|
{
|
|
WINDOW w = create_interface(parent, xpos, ypos, cols, rows, title, this);
|
|
set_win(w); // Crea la pagina principale che ospitera' il notebook
|
|
create_book(false); // Crea il notebook che ospitera' le pagine
|
|
for (int p = 1; p <= pages; p++)
|
|
{
|
|
WINDOW w = create_interface(notebook(), 0, 0, 0, 0, title, this);
|
|
insert_page(w, -1); // Aggiunge nuova pagina in fondo
|
|
}
|
|
}
|
|
else
|
|
{
|
|
WINDOW w = create_interface(parent, xpos, ypos, cols, rows, title, this);
|
|
insert_page(w, 0);
|
|
}
|
|
}
|
|
|
|
void TMask::set_locking(TBit_array& read_only, TToken_string& list, bool on)
|
|
{
|
|
if (list.find('*') >= 0)
|
|
{
|
|
if (on)
|
|
{
|
|
read_only.set(fields());
|
|
read_only.set();
|
|
}
|
|
else
|
|
read_only.reset();
|
|
}
|
|
else
|
|
{
|
|
FOR_EACH_TOKEN(list, fld)
|
|
{
|
|
const TFixed_string id(fld);
|
|
if (id[0] == '-' || id.ends_with("@")) // Gestione gruppi
|
|
{
|
|
const int grp = abs(atoi(fld));
|
|
FOR_EACH_MASK_FIELD((*this), pos, f)
|
|
{
|
|
if (f->in_group(grp))
|
|
read_only.set(pos, on);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int pos = field2pos(fld);
|
|
if (pos >= 0)
|
|
read_only.set(pos, on);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// @mfunc Legge la maschera da file
|
|
void TMask::read_mask(
|
|
const char* name, // @parm Nome della maschera da leggere (senza estensione)
|
|
int num, // @parm Numero della maschera da leggere all'interno del file
|
|
int max) // @parm Numero massimo di pagine che deve avere la maschera
|
|
{
|
|
TWait_cursor hourglass;
|
|
if (max <= 0) max = MAX_PAGES;
|
|
|
|
_source_file = name;
|
|
_source_file.ext(MASK_EXT);
|
|
_source_file.lower();
|
|
_source_file.custom_path();
|
|
TScanner scanner(_source_file);
|
|
|
|
if (num != 0)
|
|
_mask_num = num;
|
|
|
|
// Salta la maschera principale e quelle degli sheet precedenti a pie' pari
|
|
for (int i = 0; i < num; i++)
|
|
{
|
|
while (scanner.ok())
|
|
if (scanner.line() == "ENDMASK") break;
|
|
}
|
|
|
|
init_mask();
|
|
|
|
while (scanner.ok() && scanner.popkey() != "EN")
|
|
{
|
|
if (scanner.key() == "PA")
|
|
{
|
|
read_page(scanner, false);
|
|
if (_pages >= max)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (scanner.key() == "TO")
|
|
read_page(scanner, true);
|
|
}
|
|
}
|
|
|
|
if (_pages <= 0)
|
|
fatal_box("Impossibile leggere la maschera %s : %d", (const char*)_source_file, num);
|
|
|
|
if (num == 0)
|
|
{
|
|
TFilename prof; make_profile_name(prof);
|
|
if (prof.exist())
|
|
{
|
|
// Costruisce la lista dei gruppi di appartenenza dell'utente
|
|
const TString_array& uag = user_and_groups();
|
|
TConfig ini(prof);
|
|
FOR_EACH_ARRAY_ROW_BACK(uag, c, utonto)
|
|
{
|
|
*utonto << "_Locks";
|
|
if (ini.set_paragraph(*utonto))
|
|
{
|
|
TBit_array read_only;
|
|
TAuto_token_string lfields(ini.get("Lock"));
|
|
TAuto_token_string ufields(ini.get("Unlock"));
|
|
if (ufields.find("*")>=0)
|
|
{
|
|
set_locking(read_only, lfields, true);
|
|
}
|
|
else
|
|
{
|
|
set_locking(read_only, lfields, true);
|
|
set_locking(read_only, ufields, false);
|
|
}
|
|
|
|
FOR_EACH_MASK_FIELD((*this), j, f)
|
|
{
|
|
if (f->is_loadable())
|
|
{
|
|
const bool ro = read_only[j];
|
|
if (f->read_only() != ro)
|
|
f->set_read_only(ro);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TAssoc_array & defs = ini.list_variables("defaults");
|
|
|
|
FOR_EACH_ASSOC_STRING(defs, obj, key, str)
|
|
{
|
|
short id = atoi(key);
|
|
const int pos = id2pos(id);
|
|
if (pos >= 0 && fld(pos).is_operable())
|
|
fld(pos).set_default(str);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void TMask::add_field(TMask_field* f)
|
|
{
|
|
const long pos = _field.add(f)+1;
|
|
const short id = f->dlg();
|
|
if (id > 0 && id < 512)
|
|
{
|
|
if (f->is_sheet())
|
|
{
|
|
for (short cid = 101; cid <= 110; cid++)
|
|
{
|
|
if (_position.objptr(cid) && f->parent() == field(cid).parent())
|
|
{
|
|
NFCHECK("Lo sheet %d mal sopporta la convivenza col campo %d", id, cid);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_position.objptr(id))
|
|
{
|
|
if (id > DLG_QUIT)
|
|
NFCHECK("Il campo %d e' duplicato!", id);
|
|
}
|
|
else
|
|
_position.TArray::add((TObject*)pos, id);
|
|
}
|
|
}
|
|
|
|
void TMask::set_tab_buttons(TToken_string& tabs)
|
|
{
|
|
int tab = 0;
|
|
|
|
WINDOW nb = notebook();
|
|
if (nb == NULL_WIN)
|
|
nb = create_book(false);
|
|
FOR_EACH_TOKEN(tabs, title)
|
|
{
|
|
if (tab >= _pages) // Se devo creare piu' orecchie che pagine...
|
|
insert_page(page_win(_pages-1), -1); // ... ripeto l'ultima pagina
|
|
xvt_notebk_set_tab_title(nb, tab, title);
|
|
tab++;
|
|
}
|
|
}
|
|
|
|
void TMask::set_default_tab_buttons()
|
|
{
|
|
TToken_string tabs;
|
|
TString16 pag;
|
|
for (int i = 1; i <= _pages; i++)
|
|
{
|
|
pag.format(FR("Pag.%d"), i);
|
|
tabs.add(pag);
|
|
}
|
|
set_tab_buttons(tabs);
|
|
}
|
|
|
|
TMask::TMask(const char* maskname, int num, int max)
|
|
: _mask_num(num), _sheet(NULL)
|
|
{
|
|
if (maskname && *maskname)
|
|
read_mask(maskname, num, max);
|
|
}
|
|
|
|
|
|
TMask::~TMask()
|
|
{
|
|
_field.destroy();
|
|
}
|
|
|
|
word TMask::class_id() const
|
|
{ return CLASS_MASK; }
|
|
|
|
bool TMask::is_kind_of(word c) const
|
|
{
|
|
if (c == CLASS_MASK)
|
|
return true;
|
|
return TWindow::is_kind_of(c);
|
|
}
|
|
|
|
void TMask::open()
|
|
{
|
|
if (!_open || _page != 0)
|
|
_open = true;
|
|
|
|
if (toolbar() != NULL_WIN)
|
|
xvt_toolbar_realize(toolbar()); // Non si capisce perche' ce ne sia bisogno, ma tant'e'
|
|
|
|
_focus = first_focus(0);
|
|
set_focus_field(fld(_focus).dlg());
|
|
}
|
|
|
|
int TMask::find_first_active(WINDOW p) const
|
|
{
|
|
const int max = fields();
|
|
for (int f = 0; f < max; f++)
|
|
{
|
|
TMask_field& c = fld(f);
|
|
if (c.active() && c.parent() == p)
|
|
return f;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int TMask::first_focus(short id, bool dirty)
|
|
{
|
|
static int tempfirstfocus = -1;
|
|
static bool tempdirty = false;
|
|
|
|
int f = _first_focus;
|
|
if (id == 0)
|
|
{
|
|
if (tempfirstfocus >= 0)
|
|
{
|
|
f = tempfirstfocus;
|
|
if (tempdirty && fld(f).dirty() == false)
|
|
fld(f).set_dirty();
|
|
tempfirstfocus = -1;
|
|
}
|
|
else
|
|
{
|
|
if (f < 0 || !fld(f).active())
|
|
{
|
|
f = find_first_active(curr_win());
|
|
if (f < 0 && toolwin() != NULL_WIN)
|
|
f = find_first_active(toolwin());
|
|
if (f < 0 && toolbar() != NULL_WIN)
|
|
f = find_first_active(toolbar());
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (id > 0)
|
|
{
|
|
f = _first_focus = id2pos(id);
|
|
tempfirstfocus = -1;
|
|
}
|
|
else
|
|
{
|
|
f = tempfirstfocus = id2pos(-id);
|
|
tempdirty = dirty;
|
|
}
|
|
}
|
|
|
|
CHECKD(f >= 0 && f < fields(), "Invalid focus field ", f);
|
|
return f;
|
|
}
|
|
|
|
TOperable_field& TMask::focus_field() const
|
|
{
|
|
if (is_open())
|
|
{
|
|
const short focus = low_get_focus_id(curr_win());
|
|
if (focus > 0 )
|
|
((TMask *)this)->notify_focus_field(focus);
|
|
}
|
|
|
|
TMask_field & f = fld(_focus);
|
|
CHECK(f.is_kind_of(CLASS_OPERABLE_FIELD), "A non operable_field has the focus");
|
|
return (TOperable_field&)f;
|
|
}
|
|
|
|
void TMask::set_focus_field(short id)
|
|
{
|
|
notify_focus_field(id);
|
|
if (is_open())
|
|
{
|
|
const TMask_field& f = field(id);
|
|
int p = _page > 0 ? _page : 0;
|
|
if (f.parent() != curr_win()) // Controllo se devo cambiar pagina
|
|
{
|
|
p = find_parent_page(f);
|
|
if (p < 0 || p >= _pages || !page_enabled(p))
|
|
p = 0;
|
|
}
|
|
show_page(p);
|
|
}
|
|
}
|
|
|
|
void TMask::notify_focus_field(short id)
|
|
{
|
|
const int pos = id2pos(id);
|
|
CHECKD(pos >= 0, "Can't set focus to field ", id);
|
|
_focus = pos;
|
|
}
|
|
|
|
bool TMask::can_be_closed() const
|
|
{
|
|
if (is_running())
|
|
{
|
|
bool ok = true;
|
|
if ((edit_mode() || insert_mode()) && dirty() && id2pos(DLG_QUIT) < 0)
|
|
ok = yesno_box(TR("Annullare i dati inseriti?"));
|
|
if (ok)
|
|
((TMask*)this)->stop_run(K_FORCE_CLOSE);
|
|
return false;
|
|
}
|
|
return true; // Should never happen!
|
|
}
|
|
|
|
void TMask::close()
|
|
{
|
|
_open = false;
|
|
_page = -1;
|
|
xvt_vobj_set_visible(win(), false);
|
|
}
|
|
|
|
short TMask::dirty() const
|
|
{
|
|
const int max = fields();
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
const TMask_field& f = fld(i);
|
|
if (f.dirty() && f.active())
|
|
return f.dlg();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void TMask::load_checks() const
|
|
{
|
|
const int max = fields();
|
|
int i;
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
const TMask_field& f = fld(i);
|
|
if (f.is_edit())
|
|
((TEdit_field&)f).test_drivers();
|
|
}
|
|
for (i = 0; i < max; i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.has_check())
|
|
f.check(STARTING_CHECK);
|
|
}
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Abilita/disabilita una pagina e tutte le successive
|
|
void TMask::enable_page(
|
|
byte page, // @parm Pagina da abilitare/disabilitare
|
|
bool on) // @parm Operazione da svolgere:
|
|
//
|
|
// @flag true | Abilita la pagina <p page> (default)
|
|
// @flag false | Disabilita la pagina <p page>
|
|
{
|
|
if (page > 0 && page < _pages) // Non posso spegnere la prima pagina
|
|
{
|
|
if (_enabled[page] != on)
|
|
{
|
|
CHECK(_notebook, "Can't find an useful notebook");
|
|
const short tabs = xvt_notebk_get_num_tabs(_notebook);
|
|
if (on)
|
|
{
|
|
// Riaggiungo i tab buttons (normalmente tabs == page)
|
|
for (short i = tabs; i < _pages; i++)
|
|
{
|
|
_enabled.set(i, true);
|
|
xvt_notebk_add_page(_notebook, _pagewin[i], NULL, NULL, i);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Elimino i tab buttons (normalmente tabs == _pages)
|
|
for (short i = tabs-1; i >= page; i--)
|
|
{
|
|
_enabled.set(i, false);
|
|
xvt_notebk_rem_tab(_notebook, i);
|
|
}
|
|
_page = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Controlla lo stato di abilitazione di una pagina
|
|
// Certified 99%
|
|
bool TMask::page_enabled(byte p) const
|
|
{
|
|
// First page and toolbars are always enabled
|
|
const bool on = p == 0 || p >= MAX_PAGES || _enabled[p];
|
|
return on;
|
|
}
|
|
|
|
|
|
void TMask::start_run()
|
|
{
|
|
const long start = clock();
|
|
const int max = fields();
|
|
|
|
if (_should_check)
|
|
{
|
|
TWait_cursor hourglass;
|
|
|
|
load_checks();
|
|
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.dirty() <= 1) // Attenzione puo' valere anche 3 per i very dirty!
|
|
{
|
|
f.set_dirty(false);
|
|
const bool op = f.is_operable() && (f.active() || f.ghost()) &&
|
|
!f.is_kind_of(CLASS_BUTTON_FIELD) &&
|
|
!f.is_kind_of(CLASS_BUTTON_TOOL);
|
|
if (op)
|
|
f.on_hit(); // Lancia messaggio di inizializzazione
|
|
}
|
|
}
|
|
}
|
|
_should_check = true;
|
|
|
|
// Make sure that "nearly" all fields are clean!
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.dirty() == 1) // Attenzione puo' valere anche 3 per i very dirty!
|
|
f.set_dirty(false);
|
|
}
|
|
|
|
_last_test = -1;
|
|
}
|
|
|
|
bool TMask::check_fields()
|
|
{
|
|
WINDOW curpage = NULL_WIN; // Page under test
|
|
|
|
const bool sheet = is_sheetmask() && !is_open();
|
|
|
|
const int max = fields();
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
const bool on = f.active() || (f.shown() && f.is_sheet());
|
|
if (on) // Don't test inactive fields
|
|
{
|
|
if (f.parent() != curpage)
|
|
{
|
|
const int pa = find_parent_page(f);
|
|
if (!page_enabled(pa))
|
|
break; // Page disabled: end of test
|
|
curpage = f.parent(); // Update current page
|
|
}
|
|
|
|
if (sheet) f.set_dirty(); // Force check in sheet masks
|
|
if (f.on_key(K_ENTER) == false)
|
|
{
|
|
if (is_open())
|
|
set_focus_field(f.dlg());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void TMask::check_field( short fld_id )
|
|
{
|
|
if (fld_id <= 0)
|
|
{
|
|
const int gr = -fld_id;
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (gr == 0 || f.in_group(gr))
|
|
{
|
|
f.on_hit();
|
|
f.check();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
field(fld_id).on_hit();
|
|
field(fld_id).check();
|
|
}
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Converte un identificatore di campo nella sua posizione
|
|
//
|
|
// @rdesc Ritorna la posizione del campo nella maschera (-1 se non lo trova)
|
|
int TMask::id2pos(
|
|
short id) const // @parm Identificatore del campo del quale trovare la posizione
|
|
|
|
// @comm Cerca nella maschera il campo con identificatore <p id> e ne ritorna il numero ordinale
|
|
// (es. il campo 120 e' il quarto della maschera)
|
|
{
|
|
int pos = -1;
|
|
if (id > 0)
|
|
{
|
|
if (id < 512)
|
|
pos = int(long(_position.objptr(id))-1);
|
|
else
|
|
{
|
|
for (pos = fields()-1; pos >= 0; pos--)
|
|
if (fld(pos).dlg() == id)
|
|
break;
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
|
|
TMask_field& TMask::field(short id) const
|
|
{
|
|
int pos = id2pos(id);
|
|
if (pos < 0)
|
|
{
|
|
yesnofatal_box("Non esiste il campo %d sulla maschera %s", id, (const char*)_source_file);
|
|
pos = 0;
|
|
}
|
|
return fld(pos);
|
|
}
|
|
|
|
int TMask::field2pos(const char* fieldname) const
|
|
{
|
|
const int id = atoi(fieldname);
|
|
int i;
|
|
for (i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (id == 0)
|
|
{
|
|
const TFieldref* fr = f.field();
|
|
if (fr != NULL && fr->name() == fieldname)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if (f.dlg() == id)
|
|
break;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
TMask_field* TMask::find_by_id(short id) const
|
|
{
|
|
const int i = id2pos(id);
|
|
return i >= 0 ? &fld(i) : NULL;
|
|
}
|
|
|
|
TMask_field* TMask::find_by_fieldname(const char* fieldname) const
|
|
{
|
|
const int i = field2pos(fieldname);
|
|
return i >= 0 ? &fld(i) : NULL;
|
|
}
|
|
|
|
TEdit_field& TMask::efield(short id) const
|
|
{
|
|
TMask_field& f = field(id);
|
|
CHECKD(f.is_edit(), "Impossibile trattare come editabile il campo ", id);
|
|
return (TEdit_field&)f;
|
|
}
|
|
|
|
TList_field& TMask::lfield(short id) const
|
|
{
|
|
TMask_field& f = field(id);
|
|
CHECKD(f.is_list(), "Impossibile trattare come listbox il campo ", id);
|
|
return (TList_field&)f;
|
|
}
|
|
|
|
TSheet_field& TMask::sfield(short id) const
|
|
{
|
|
TMask_field& f = field(id);
|
|
CHECKD(f.is_sheet(), "Impossibile trattare come spreadsheet il campo ", id);
|
|
return (TSheet_field&)f;
|
|
}
|
|
|
|
TTree_field& TMask::tfield(short id) const
|
|
{
|
|
TMask_field& f = field(id);
|
|
CHECKD(f.is_kind_of(CLASS_TREE_FIELD), "Impossibile trattare come albero il campo ", id);
|
|
return (TTree_field&)f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
int TMask::find_parent_page(const TMask_field& f) const
|
|
{
|
|
return win2page(f.parent());
|
|
}
|
|
|
|
// @mfunc Forza la chiusura della maschera
|
|
//
|
|
// @rdesc Ritorna il risultato dell'operazione:
|
|
//
|
|
// @flag true | E' riuscita a chiudere la maschera
|
|
// @flag false | Non e' riuscita a chiudere la maschera
|
|
bool TMask::stop_run(
|
|
KEY key) // @parm Tasto che deve provocare la chiusura
|
|
|
|
// @comm Permette di chiudere la maschera come se l'utente avesse premuto il tasto <p key>.
|
|
// Nel caso la maschera non si chiuda (es. un check fallito), ritorna false.
|
|
{
|
|
if (key != K_AUTO_ENTER && key != K_FORCE_CLOSE)
|
|
{
|
|
const int last = fields();
|
|
int i;
|
|
// Cerca se esiste un bottone avente come exit_key() il tasto key
|
|
for (i = 0; i < last; i++)
|
|
{
|
|
const TMask_field& f = fld(i);
|
|
if (f.is_operable() && !f.is_editable() && f.active())
|
|
{
|
|
if (f.is_kind_of(CLASS_BUTTON_FIELD))
|
|
{
|
|
const TButton_field& b = (const TButton_field&)f;
|
|
if (b.exit_key() == key)
|
|
break;
|
|
} else
|
|
if (f.is_kind_of(CLASS_BUTTON_TOOL))
|
|
{
|
|
const TButton_tool& b = (const TButton_tool&)f;
|
|
if (b.exit_key() == key)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (i >= last)
|
|
return false;
|
|
}
|
|
|
|
if (key == K_CTRL_ENTER || key == K_AUTO_ENTER)
|
|
key = K_ENTER;
|
|
else
|
|
{
|
|
if (key == K_FORCE_CLOSE)
|
|
key = (id2pos(DLG_QUIT)<0 && id2pos(DLG_CANCEL)>=0) ? K_ESC : K_QUIT;
|
|
}
|
|
|
|
if (key != K_ESC && key != K_QUIT && key != K_DEL && key != K_F9)
|
|
{
|
|
bool ok = check_current_field();
|
|
if (ok)
|
|
ok = check_fields();
|
|
if (ok)
|
|
{
|
|
if (id2pos(DLG_PROFILE) >= 0 && field(DLG_PROFILE).hidden())
|
|
save_profile();
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
return TWindow::stop_run(key);
|
|
}
|
|
|
|
void TMask::on_button(short)
|
|
{
|
|
/* Non devo fare niente !!! non essendo una TWindow */
|
|
}
|
|
|
|
void TMask::handler(WINDOW w, EVENT* ep)
|
|
{
|
|
static TSheet_field* _last_sheet = NULL;
|
|
|
|
if (ep->type == E_MOUSE_DOWN && ep->v.mouse.button == 1)
|
|
{
|
|
_last_sheet = NULL;
|
|
w = curr_win();
|
|
for (int f = fields()-1; f >= 0; f--)
|
|
{
|
|
TMask_field& cur_fld = fld(f);
|
|
if (cur_fld.shown() && cur_fld.parent() == w)
|
|
{
|
|
RCT rct; cur_fld.get_rect(rct);
|
|
if (xvt_rect_has_point(&rct, ep->v.mouse.where))
|
|
{
|
|
if (cur_fld.is_kind_of(CLASS_SHEET_FIELD))
|
|
_last_sheet = (TSheet_field*)&cur_fld;
|
|
else
|
|
cur_fld.on_key(K_F11);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_last_sheet)
|
|
{
|
|
MENU_ITEM* menu = xvt_res_get_menu(BROWSE_BAR);
|
|
if (menu != NULL)
|
|
{
|
|
const PNT& p = ep->v.mouse.where;
|
|
xvt_menu_popup(menu->child, w, p, XVT_POPUP_CENTER, 0); // verificare
|
|
xvt_res_free_menu_tree(menu);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (ep->type == E_COMMAND)
|
|
{
|
|
const int tag = ep->v.cmd.tag;
|
|
if (_last_sheet != NULL)
|
|
{
|
|
switch (tag - BROWSE_BAR)
|
|
{
|
|
case 1: _last_sheet->save_columns_order(); break;
|
|
case 2: _last_sheet->reset_columns_order(); break;
|
|
case 3: _last_sheet->on_key(K_F11); break;
|
|
case 4: _last_sheet->esporta(); break;
|
|
default: break;
|
|
}
|
|
return;
|
|
}
|
|
if (tag == M_HELP_ONCONTEXT)
|
|
{
|
|
on_key(K_F1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ep->type == E_VSCROLL)
|
|
{
|
|
for (int i = 0; i < fields(); i++)
|
|
{
|
|
const TMask_field& ff = fld(i);
|
|
if (ff._ctl != NULL)
|
|
{
|
|
XI_OBJ* obj = ff._ctl->xi_object();
|
|
if (obj->type == 11)
|
|
{
|
|
const int dir = ep->v.scroll.what == SC_LINE_UP ? -3 : +3;
|
|
xi_scroll(obj, dir);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ep->type == E_CONTROL)
|
|
{
|
|
if (is_running()) // Scarto a priori gli eventi di inizializzazione
|
|
{
|
|
switch (ep->v.ctl.ci.type)
|
|
{
|
|
case WC_NOTEBK:
|
|
if (ep->v.ctl.ci.win == _notebook)
|
|
{
|
|
const int new_page = ep->v.ctl.ci.v.notebk.tab_no;
|
|
if (new_page != _page && new_page < _pages) // Cambio pagina effettivo
|
|
{
|
|
if (fld(_focus).on_key(K_TAB))
|
|
show_page(new_page);
|
|
}
|
|
}
|
|
break;
|
|
case WC_ICON: // In realta' trattasi di bottone di toolbar
|
|
if (ep->v.ctl.ci.win == toolbar()) // Test di sicurezza semi-inutile
|
|
{
|
|
TMask_field& tool = field(ep->v.ctl.id);
|
|
if (tool.active())
|
|
tool.on_key(K_SPACE); // Gestisco l'evento di pressione del bottone
|
|
}
|
|
break;
|
|
case WC_HSCROLL:
|
|
case WC_VSCROLL:
|
|
if (id2pos(ep->v.ctl.id) < 0) // potrebbero non avere un controllo associato
|
|
break;
|
|
// fall down
|
|
case WC_HSLIDER:
|
|
case WC_VSLIDER:
|
|
case WC_PROPGRID:
|
|
{
|
|
TMask_field& f = field(ep->v.ctl.id);
|
|
f.set_focusdirty();
|
|
f.on_key(K_SPACE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ep->type == E_UPDATE && w == page_win(0))
|
|
{
|
|
if (source_file().find("custom") > 0)
|
|
{
|
|
RCT rct; xvt_vobj_get_client_rect(w, &rct);
|
|
rct.left += 2; rct.right -= 2; rct.bottom -= 2;
|
|
DRAW_CTOOLS dct;
|
|
xvt_dwin_get_draw_ctools(w, &dct);
|
|
dct.fore_color = FOCUS_COLOR;
|
|
dct.back_color = FOCUS_BACK_COLOR;
|
|
dct.opaque_text = TRUE;
|
|
xvt_dwin_set_draw_ctools(w, &dct);
|
|
//xvt_dwin_set_font(w, DEF_FONT);
|
|
xvt_dwin_set_clip(w, NULL);
|
|
const char* text = "Custom";
|
|
const int tw = xvt_dwin_get_text_width(w, text, -1);
|
|
xvt_dwin_draw_text(w, rct.left, rct.bottom, text, -1);
|
|
xvt_dwin_draw_text(w, (rct.right-rct.left-tw)/2, rct.bottom, text, -1);
|
|
xvt_dwin_draw_text(w, rct.right-tw, rct.bottom, text, -1);
|
|
}
|
|
}
|
|
|
|
TWindow::handler(w, ep);
|
|
}
|
|
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Assegna una azione al tasto non standard
|
|
//
|
|
// @rdesc Ritrna se e' stto effetuato una azione:
|
|
//
|
|
// @flag true | Era prevista una azione collegata al tasto ed e' stata eseguita
|
|
// @flag false | Non era prevista nessuna azione collegata al tasto
|
|
bool TMask::on_key(
|
|
KEY key) // @parm Tasto premuto sulla maschera
|
|
|
|
// @comm Controlla se il tasto e' tra quelli standard previsti dalla maschera corrente, in caso
|
|
// contrario ne assegna una azione
|
|
{
|
|
if (_handler)
|
|
{
|
|
const bool cont = _handler(*this, key);
|
|
if (!cont)
|
|
return false;
|
|
}
|
|
|
|
switch(key)
|
|
{
|
|
case K_AUTO_ENTER:
|
|
case K_CTRL_ENTER:
|
|
case K_QUIT:
|
|
case K_ESC:
|
|
stop_run(key);
|
|
break;
|
|
case K_PREV:
|
|
if (fld(_focus).on_key(K_TAB))
|
|
next_page(-1);
|
|
break;
|
|
case K_NEXT:
|
|
if (fld(_focus).on_key(K_TAB))
|
|
next_page(+1);
|
|
break;
|
|
case K_F1:
|
|
{
|
|
const char* key = source_file().name_only();
|
|
EVENT e; memset(&e, 0, sizeof(e));
|
|
e.type = E_HELP;
|
|
e.v.help.tag = M_HELP_ONCONTEXT;
|
|
e.v.help.obj = win();
|
|
e.v.help.tid = (long)key;
|
|
|
|
TFilename n = "campo";
|
|
TString4 module; module.strncpy(key, 2); module.lower();
|
|
if (module != "ba")
|
|
n.insert(module);
|
|
FILE_SPEC fs; xvt_fsys_convert_str_to_fspec(n, &fs);
|
|
XVT_HELP_INFO hi = xvt_help_open_helpfile(&fs, 0);
|
|
xvt_help_process_event(hi, win(), &e);
|
|
}
|
|
break;
|
|
case K_F12:
|
|
send_key(K_F12, focus_field().dlg());
|
|
break;
|
|
case K_CTRL+'+':
|
|
if (is_running())
|
|
{
|
|
// Cerco nella pagina corrente il primo spreadsheet a partire dal campo col focus
|
|
if (_focus < 0) _focus = 0;
|
|
const WINDOW myparent = fld(_focus).parent();
|
|
for (int fire = _focus; fire < fields(); fire++)
|
|
{
|
|
TMask_field& f = fld(fire);
|
|
if (f.parent() != myparent)
|
|
break;
|
|
if (f.is_sheet())
|
|
{
|
|
send_key(key, f.dlg());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
if (key > K_CTRL)
|
|
{
|
|
key -= K_CTRL;
|
|
if (key >= K_F1 && key <= K_F12)
|
|
{
|
|
const int page = key - K_F1;
|
|
if (page < _pages && fld(_focus).on_key(K_TAB))
|
|
show_page(page);
|
|
}
|
|
else
|
|
{
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.is_operable() && !f.is_editable() && f.active())
|
|
{
|
|
KEY vk = 0;
|
|
if (f.is_kind_of(CLASS_BUTTON_FIELD))
|
|
{
|
|
TButton_field& b = (TButton_field&)f;
|
|
vk = b.virtual_key();
|
|
} else
|
|
if (f.is_kind_of(CLASS_BUTTON_TOOL))
|
|
{
|
|
TButton_tool& t = (TButton_tool&)f;
|
|
vk = t.virtual_key();
|
|
}
|
|
if (vk > 0 && vk == key)
|
|
{
|
|
f.on_key(K_SPACE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool TMask::on_dirty(TMask_field&)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int TMask::win2page(WINDOW w) const
|
|
{
|
|
if (w == toolwin())
|
|
return MAX_PAGES;
|
|
if (w == toolbar())
|
|
return MAX_PAGES+1;
|
|
int p;
|
|
for (p = _pages-1; p >= 0; p--)
|
|
if (w == page_win(p))
|
|
break;
|
|
return p;
|
|
}
|
|
|
|
TMask_field* TMask::parse_field(TScanner& scanner)
|
|
{
|
|
const TString& k = scanner.key();
|
|
if (k == "BO") return new TBoolean_field(this);
|
|
if (k == "BR") return new TBrowsefile_field(this);
|
|
if (k == "BU") return new TButton_field(this);
|
|
if (k == "CL") return new TGolem_client_field(this);
|
|
if (k == "CU") return new TCurrency_field(this);
|
|
if (k == "DA") return new TDate_field(this);
|
|
if (k == "GO") return new TGolem_field(this);
|
|
if (k == "GR") return new TGroup_field(this);
|
|
if (k == "LI") return new TList_field(this);
|
|
if (k == "ME") return new TMemo_field(this);
|
|
if (k == "NU") return new TReal_field(this);
|
|
if (k == "PR") return new TProp_field(this);
|
|
if (k == "RA") return new TRadio_field(this);
|
|
if (k == "SL") return new TSlider_field(this);
|
|
if (k == "SP") return new TSheet_field(this);
|
|
if (k == "ST") return new TEdit_field(this);
|
|
if (k == "TE") return new TText_field(this);
|
|
if (k == "TR") return new TTree_field(this);
|
|
if (k == "ZO") return new TZoom_field(this);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
TMask_field* TMask::parse_tool(TScanner& scanner)
|
|
{
|
|
const TString& k = scanner.key();
|
|
if (k == "BU") return new TButton_tool(this);
|
|
return NULL;
|
|
}
|
|
|
|
int TMask::sheets() const
|
|
{
|
|
int count = 0;
|
|
for (int f = fields()-1; f >= 0; f--)
|
|
{
|
|
if (fld(f).is_sheet())
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Legge la pagina dal file
|
|
//
|
|
// @rdesc Ritorna l'handle della finestra creata
|
|
void TMask::read_page(
|
|
TScanner& scanner, // @parm File dal quale leggere la pagina
|
|
bool is_toolbar) // @parm Indica se e' la toolbar
|
|
|
|
// @comm Il parametro <p toolbar> e' utilizzato per indicare se la pagina deve essere visualizzata
|
|
// a tutto schermo (true) oppure no
|
|
{
|
|
const TString title = dictionary_translate(scanner.string());
|
|
|
|
RCT r;
|
|
if (!is_toolbar && notebook() != NULL_WIN) // Pagina oltre la prima
|
|
{
|
|
scanner.line(); // Scarto rettangolo inutile
|
|
xvt_rect_set(&r, 0, 0, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
scanner.rectangle(r.left, r.top, r.right, r.bottom);
|
|
if (is_toolbar)
|
|
{
|
|
if (r.top > 15) // Rendi negative le coordinate delle toolbar per ...
|
|
r.top -= 23; // ... ottimizzare l'uso dello schermo ad alta risoluzione
|
|
}
|
|
}
|
|
|
|
if (_notebook == NULL_WIN && _single == NULL) // Controlla se la maschera ha piu' di una pagina ...
|
|
{ // ... nel caso serva un notebook per le prossime
|
|
const streampos pos = scanner.tellg(); // Memorizza posizione dello scanner
|
|
while (true)
|
|
{
|
|
const TString& l = scanner.line();
|
|
if (l.empty() || l == "ENDMASK") // Fine maschera
|
|
break;
|
|
if (l.starts_with("PA") || l.starts_with("TO")) // Ho trovato un'altra pagina!
|
|
{
|
|
if (is_toolbar)
|
|
create_book(false); // Crea notebook
|
|
else
|
|
{
|
|
WINDOW w = create_interface(NULL_WIN, r.left, r.top, r.right, r.bottom, title, this);
|
|
insert_page(w, 0); // Crea pagina principale
|
|
const bool single = l.starts_with("TO"); // Crea sotto-pagina singola o multipla?
|
|
create_book(single);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
scanner.seekg(pos); // Ripristina posizione dello scanner
|
|
}
|
|
|
|
WINDOW w = NULL_WIN;
|
|
if (is_toolbar)
|
|
{
|
|
if (r.top == 0)
|
|
w = create_bar(1);
|
|
else
|
|
{
|
|
CHECKD(r.top < 0, "Bad bottom bar height ", r.top);
|
|
w = create_bar(r.top);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (notebook() != NULL_WIN)
|
|
w = create_page(title, -1);
|
|
else
|
|
{
|
|
if (_single != NULL_WIN)
|
|
w = _single;
|
|
else
|
|
{
|
|
CHECK(win() == NULL_WIN, "bad page sequence");
|
|
w = create_interface(NULL_WIN, r.left, r.top, r.right, r.bottom, title, this);
|
|
insert_page(w, 0);
|
|
}
|
|
}
|
|
}
|
|
while (scanner.popkey() != "EN")
|
|
{
|
|
TMask_field* f = NULL;
|
|
if (is_toolbar && r.top == 0)
|
|
f = parse_tool(scanner);
|
|
else
|
|
f = parse_field(scanner);
|
|
if (f == NULL)
|
|
{
|
|
#ifdef DBG
|
|
const int pf = fields();
|
|
TString e;
|
|
e << "Campo non riconosciuto alla posizione " << pf;
|
|
if (pf > 0)
|
|
e << "\nL'ultimo riconosciuto e' " << fld(pf-1).dlg() << ": " << fld(pf-1).prompt();
|
|
error_box(e);
|
|
#endif
|
|
while (scanner.popkey() != "EN");
|
|
}
|
|
else
|
|
{
|
|
f->construct(scanner, w);
|
|
add_field(f);
|
|
}
|
|
}
|
|
|
|
if (is_toolbar && r.top ==0 && _toolbar != NULL_WIN)
|
|
xvt_toolbar_realize(_toolbar);
|
|
}
|
|
|
|
bool TMask::check_current_field() const
|
|
{
|
|
bool ok = true;
|
|
if (_focus >= 0)
|
|
{
|
|
TMask_field& ff = fld(_focus);
|
|
if (ff.focusdirty())
|
|
ok = ff.on_key(K_TAB);
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
WINDOW TMask::create_book(bool single)
|
|
{
|
|
WINDOW parent = win();
|
|
if (parent == NULL_WIN)
|
|
{
|
|
parent = create_interface(NULL_WIN, 0, 0, 0, 0, "", this);
|
|
set_win(parent);
|
|
}
|
|
if (single)
|
|
{
|
|
CHECK(_single == NULL_WIN, "One single page pane, please!");
|
|
_single = create_interface(parent, 0, 0, 0, 0, "", this);
|
|
xvt_pane_add(parent, _single, "_MainSingle_", 0, 0);
|
|
_pagewin[0] = _single;
|
|
_pages = 1;
|
|
}
|
|
else
|
|
{
|
|
CHECK(_notebook == NULL_WIN, "One single notebook, please!");
|
|
XVT_COLOR_COMPONENT xcc[4]; memset(xcc, 0, sizeof(xcc));
|
|
xcc[0].type = XVT_COLOR_BACKGROUND; xcc[0].color = MASK_BACK_COLOR;
|
|
xcc[1].type = XVT_COLOR_FOREGROUND; xcc[1].color = NORMAL_COLOR;
|
|
|
|
WIN_DEF wd; memset(&wd, 0, sizeof(wd));
|
|
wd.wtype = WC_NOTEBK;
|
|
wd.ctlcolors = xcc;
|
|
wd.v.ctl.flags = CTL_FLAG_TAB_DEFAULT | CTL_FLAG_TAB_TOP;
|
|
xvt_vobj_get_client_rect(parent, &wd.rct);
|
|
_notebook = xvt_ctl_create_def(&wd, parent, long(this));
|
|
xvt_pane_add(parent, _notebook, "_MainNoteBook_", 0, 0);
|
|
|
|
if (_single != NULL) // Bello ... se solo andasse!
|
|
{
|
|
xvt_pane_detach(_single);
|
|
insert_page(_single, 0);
|
|
_single = NULL_WIN;
|
|
}
|
|
}
|
|
|
|
return single ? _single : _notebook;
|
|
}
|
|
|
|
void TMask::insert_bar(WINDOW page)
|
|
{
|
|
WINDOW parent = win();
|
|
|
|
CHECK(page != NULL_WIN, "Invalid toolbar");
|
|
CHECK(parent != NULL_WIN, "Invalid main window for toolbar");
|
|
|
|
if (_notebook == NULL_WIN && _single == NULL_WIN)
|
|
create_book(true); // In assenza di notebook crea un pane a pagina singola
|
|
|
|
RCT rct_win; xvt_vobj_get_outer_rect(parent, &rct_win);
|
|
RCT rct_bar; xvt_vobj_get_outer_rect(page, &rct_bar);
|
|
|
|
if (rct_bar.top > (rct_win.bottom-rct_win.top)/2) // BottomBar
|
|
{
|
|
CHECK(_toolwin == NULL_WIN, "One single bottom bar, please!");
|
|
_toolwin = page;
|
|
xvt_pane_add(parent, _toolwin, "_BottomBar_", 4, 0);
|
|
// Ammazza la caption e ne blocca le dimensioni
|
|
xvt_pane_change_flags(page, 0, 1 <<10); // No caption
|
|
xvt_pane_change_flags(page, 0, 1 << 8); // No resize
|
|
}
|
|
else // TopBar
|
|
{
|
|
CHECK(_toolbar == NULL_WIN, "One single top bar, please!");
|
|
|
|
if (rows() < 18) // Ridimensiono maschera piccola (non massimizzata)
|
|
{
|
|
const short bar_height = max(rct_bar.bottom - rct_bar.top, TOOL_SIZE + TOOL_TEXT * 12);
|
|
RCT rct_new; xvt_vobj_get_client_rect(parent, &rct_new);
|
|
rct_new.bottom += bar_height;
|
|
xvt_rect_offset(&rct_new, rct_win.left, rct_win.top-bar_height/2);
|
|
xvt_vobj_move(parent, &rct_new);
|
|
}
|
|
|
|
_toolbar = page;
|
|
xvt_pane_add(parent, _toolbar, "_TopBar_", 62, 0);
|
|
}
|
|
}
|
|
|
|
WINDOW TMask::create_bar(int height)
|
|
{
|
|
WINDOW w = NULL_WIN;
|
|
if (height < 0)
|
|
w = create_interface(win(), 0, height, 0, 0, "", this); // Bottom bar
|
|
else
|
|
{
|
|
const long flags = TOOL_TEXT ? CTL_FLAG_PASSWORD : 0;
|
|
w = xvt_toolbar_create(-1, 0, 0, -1, TOOL_SIZE, flags, win()); // Top bar
|
|
XVT_COLOR_COMPONENT cc[4]; memset(cc, 0, sizeof(cc));
|
|
cc[0].type = XVT_COLOR_BLEND; cc[0].color = MASK_BACK_COLOR;
|
|
cc[1].type = XVT_COLOR_FOREGROUND; cc[1].color = NORMAL_COLOR;
|
|
xvt_ctl_set_colors(w, cc, XVT_COLOR_ACTION_SET);
|
|
}
|
|
insert_bar(w); // Inserisce toolbar e crea notebook, se necessario
|
|
return w;
|
|
}
|
|
|
|
void TMask::insert_page(WINDOW page, int dove)
|
|
{
|
|
CHECKD(dove >= -1 && dove <= _pages, "Invalid page position:", dove);
|
|
// Per le pagine dopo la prima devo creare il notebook
|
|
if (_pages == 1 && _notebook == NULL_WIN)
|
|
create_book(false);
|
|
|
|
memset(_pagewin, 0, sizeof(_pagewin)); // Azzero lista pagine
|
|
if (_notebook != NULL_WIN) // property sheet
|
|
{
|
|
xvt_notebk_add_page(_notebook, page, NULL, NULL, dove);
|
|
_pages = xvt_notebk_get_num_tabs(_notebook);
|
|
// Ricostruisco lista pagine
|
|
for (short p = 0; p < _pages; p++)
|
|
_pagewin[p] = xvt_notebk_get_page(_notebook, p);
|
|
}
|
|
else // single page
|
|
{
|
|
CHECK(win() == NULL_WIN, "One single main page, please!");
|
|
set_win(page);
|
|
_pagewin[0] = page; // Assegno la prima ed unica pagina
|
|
_pages = 1;
|
|
}
|
|
}
|
|
|
|
WINDOW TMask::create_page(const char* title, int dove)
|
|
{
|
|
CHECKD(dove < 0 || dove < MAX_PAGES, "invalid page position ", dove);
|
|
WINDOW w = create_interface(notebook(), 0, 0, 0, 0, title, this);
|
|
insert_page(w, dove);
|
|
return w;
|
|
}
|
|
|
|
void TMask::set_focus()
|
|
{
|
|
TWindow::set_focus();
|
|
WINDOW w = curr_win();
|
|
if (w != win()) // Succede se ci sono notebook o toolbar
|
|
{
|
|
xvt_scr_set_focus_vobj(w);
|
|
xvt_vobj_raise(w);
|
|
}
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
// @mfunc Mostra la prossima/precedente pagina
|
|
void TMask::next_page(
|
|
int p) // @parm Pagina alla quale saltare
|
|
|
|
// @comm Il valore <p p> puo' assumere i seguenti valori:
|
|
//
|
|
// @flag -1 | Mostra la pagina precedente
|
|
// @flag 0 | Mostra la pagina corrente
|
|
// @flag +1 | Mostra la pagina successiva
|
|
// @flag 1000+n | Mostra la pagina n-esima
|
|
{
|
|
const int prev = _page; // Previous page
|
|
|
|
if (p != 0)
|
|
{
|
|
const int k = (p < 1000) ? _page+p : p-1000;
|
|
if (k < 0 || k >= _pages || !page_enabled(k))
|
|
{
|
|
beep();
|
|
return;
|
|
}
|
|
_page = k;
|
|
}
|
|
else
|
|
{
|
|
if (_page < 0 || _page >= _pages)
|
|
_page = 0;
|
|
}
|
|
if (_page != prev)
|
|
{
|
|
xvt_vobj_set_visible(win(), true);
|
|
if (notebook() != NULL)
|
|
xvt_notebk_set_front_page(notebook(), _page);
|
|
}
|
|
|
|
if (_focus < 0 || fld(_focus).parent() != curr_win() || !fld(_focus).active())
|
|
{
|
|
_focus = find_first_active(curr_win());
|
|
if (_focus < 0 && toolwin() != NULL_WIN)
|
|
_focus = find_first_active(toolwin());
|
|
if (_focus < 0 && toolbar() != NULL_WIN)
|
|
_focus = find_first_active(toolbar());
|
|
}
|
|
|
|
set_focus();
|
|
|
|
if (_focus >= 0)
|
|
{
|
|
TMask_field& ff = fld(_focus);
|
|
if (ff.active())
|
|
ff.highlight();
|
|
}
|
|
}
|
|
|
|
void TMask::show_page(int p)
|
|
{
|
|
CHECKD(p >= 0 && p < _pages, "Pagina errata ", p);
|
|
next_page(1000 + p);
|
|
}
|
|
|
|
void TMask::reset(short fld_id)
|
|
{
|
|
if (fld_id <= 0)
|
|
{
|
|
const int gr = -fld_id;
|
|
for (int f = fields()-1; f >= 0; f--)
|
|
{
|
|
TMask_field& c = fld(f);
|
|
if (gr == 0 || c.in_group(gr))
|
|
{
|
|
c._flags.dirty = c._flags.focusdirty = false;
|
|
c.reset();
|
|
}
|
|
}
|
|
}
|
|
else field(fld_id).reset();
|
|
}
|
|
|
|
const TString& TMask::get(short fld_id) const
|
|
{
|
|
const TString& s = field(fld_id).get();
|
|
return s;
|
|
}
|
|
|
|
const TString& TMask::get(const char * fld_id) const
|
|
{
|
|
const TString * s = &EMPTY_STRING;
|
|
for (int i = 0 ; i < fields(); i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
const TFieldref * campo = f.field();
|
|
if (campo != NULL && campo->name() == fld_id)
|
|
{
|
|
s= &f.get();
|
|
if (f.active())
|
|
break;
|
|
}
|
|
}
|
|
return *s;
|
|
}
|
|
|
|
long TMask::get_long(short fld_id) const
|
|
{
|
|
// const TString& s = field(fld_id).get();
|
|
const TString& s = get(fld_id);
|
|
return atol(s);
|
|
}
|
|
|
|
bool TMask::get_bool(short fld_id) const
|
|
{
|
|
// const TString& s = field(fld_id).get();
|
|
const TString& s = get(fld_id);
|
|
return s.not_empty();
|
|
}
|
|
|
|
real TMask::get_real(short fld_id) const
|
|
{
|
|
// const TString& s = field(fld_id).get();
|
|
const TString& s = get(fld_id);
|
|
return real(s);
|
|
}
|
|
|
|
TDate TMask::get_date(short fld_id) const
|
|
{
|
|
// const TString& s = field(fld_id).get();
|
|
const TString& s = get(fld_id);
|
|
return TDate(s);
|
|
}
|
|
|
|
TCurrency& TMask::get_currency(short fld_id, TCurrency& c) const
|
|
{
|
|
const TMask_field& cf = field(fld_id);
|
|
if (cf.class_id() == CLASS_CURRENCY_FIELD)
|
|
{
|
|
((TCurrency_field&)cf).get_currency(c);
|
|
}
|
|
else
|
|
{
|
|
real n(cf.get());
|
|
c.force_value("");
|
|
c.set_num(n);
|
|
}
|
|
return c;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Setta il campo con un valore
|
|
void TMask::set(
|
|
short fld_id, // @parm Identificatore del campo da settare
|
|
const char* s, // @parm Stringa da assegnare al campo
|
|
byte hit) // @parm Indica se occorre rifare i controlli una volta settato il campo
|
|
// con il nuovo valore (default false)
|
|
// @parm long | n | Numero da asegnare al campo
|
|
|
|
// @syntax set(short fld_id, const char *s, bool hit);
|
|
// @syntax set(short fld_id, long n, bool hit);
|
|
{
|
|
TMask_field& f = field(fld_id);
|
|
f.set(s);
|
|
if ((f.active() || f.ghost()))
|
|
{
|
|
f.set_dirty();
|
|
if (hit & 0x2)
|
|
f.check();
|
|
if (hit & 0x1)
|
|
f.on_hit();
|
|
}
|
|
}
|
|
|
|
void TMask::set(
|
|
const char * fld_id, // @parm FIELD del campo da settare
|
|
const char* str, // @parm Stringa da assegnare al campo
|
|
byte hit) // @parm Indica se occorre rifare i controlli una volta settato il campo
|
|
// con il nuovo valore (default false)
|
|
// @parm long | n | Numero da asegnare al campo
|
|
|
|
// @syntax set(short fld_id, const char *s, bool hit);
|
|
// @syntax set(short fld_id, long n, bool hit);
|
|
{
|
|
for (int i = 0 ; i < fields(); i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
const TFieldref * campo = f.field();
|
|
if (campo != NULL && campo->name() == fld_id)
|
|
set(f.dlg(), str, hit);
|
|
}
|
|
}
|
|
|
|
void TMask::set(short fld_id, long n, byte hit)
|
|
{
|
|
TString16 s;
|
|
s << n;
|
|
set(fld_id, s, hit);
|
|
}
|
|
|
|
void TMask::set(short fld_id, const real& n, byte hit)
|
|
{
|
|
CHECK(id2pos(fld_id) < 0 || field(fld_id).is_edit(), "Can't set a real value in a non edit field");
|
|
set(fld_id, n.string(), hit);
|
|
}
|
|
|
|
void TMask::set(short fld_id, const TDate& d, byte hit)
|
|
{
|
|
CHECK(id2pos(fld_id) < 0 || field(fld_id).is_kind_of(CLASS_DATE_FIELD), "Can't set a date in a non-date field");
|
|
set(fld_id, d.string(), hit);
|
|
}
|
|
|
|
void TMask::set(short fld_id, const TCurrency& n, byte hit)
|
|
{
|
|
CHECK(field(fld_id).is_kind_of(CLASS_CURRENCY_FIELD), "Can't set a currency in a non-currency field");
|
|
// CHECK(id2pos(fld_id) < 0 || field(fld_id).is_edit(), "Can't set a currency in a non edit field");
|
|
set(fld_id, n.get_num().string(), hit);
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Permette di attivare/disattivare tutta la maschera
|
|
void TMask::activate(
|
|
bool on) // @parm Indica l'operazione da svolgere sul campo:
|
|
//
|
|
// @flag true | Attiva la pagina(default)
|
|
// @flag false | Disattiva la pagina
|
|
{
|
|
TWindow::activate(on);
|
|
|
|
// Maschera vecchia e decrepita senza toolbar: nascondo i bottoni
|
|
if (toolwin() != NULL_WIN && toolbar() == NULL_WIN)
|
|
xvt_vobj_set_visible(toolwin(), on);
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Abilita/disabilita un campo
|
|
void TMask::enable(
|
|
short fld_id, // @parm Identificatore del campo da abilitare (0 tutti i campi, <0 id gruppo)
|
|
bool on) // @parm Indica l'operazione da svolgere sul campo:
|
|
//
|
|
// @flag true | Abilita il campo (default)
|
|
// @flag false | Disabilita il campo
|
|
{
|
|
if (fld_id <= 0)
|
|
{
|
|
const int gr = -fld_id;
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (gr == 0 || f.in_group(gr))
|
|
f.enable(on);
|
|
}
|
|
}
|
|
else
|
|
field(fld_id).enable(on);
|
|
}
|
|
|
|
|
|
void TMask::enable_default(short fld_id)
|
|
{
|
|
if (fld_id <= 0)
|
|
{
|
|
const int gr = -fld_id;
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (gr == 0 || f.in_group(gr))
|
|
f.enable_default();
|
|
}
|
|
}
|
|
else
|
|
field(fld_id).enable_default();
|
|
}
|
|
|
|
|
|
word TMask::num_keys() const
|
|
{
|
|
word max = 0;
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
const TMask_field& f = fld(i);
|
|
if (f.is_editable())
|
|
{
|
|
word k = ((const TEditable_field&)f).last_key();
|
|
if (k > max) max = k;
|
|
}
|
|
}
|
|
return max;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Abilita/disabilita i campi di una chiave sulla maschera
|
|
void TMask::enable_key(
|
|
word key, // @parm Chiave di cui abilitare il campo
|
|
bool on) // @parm Indica l'operazione da svolgere sul tasto:
|
|
//
|
|
// @flag true | Abilita il campo (default)
|
|
// @flag false | Disabilita il campo
|
|
|
|
{
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.in_key(key))
|
|
{
|
|
if (on)
|
|
{
|
|
f.enable_default();
|
|
if (!f.shown())
|
|
f.show_default();
|
|
}
|
|
else
|
|
f.disable();
|
|
}
|
|
}
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Ritorna il l'identificatore di un campo della chiave <p key>
|
|
//
|
|
// @rdesc Ritorna l'identificatore del campo cercato
|
|
TEditable_field* TMask::get_key_field(
|
|
word key, // @parm Chiave di cui controllare l'esistenza
|
|
bool first) const // @parm Indica se la ricerca dev partire dell'inizio. Assume i valori:
|
|
//
|
|
// @flag true | Comincia la ricerca dal primo campo della maschera
|
|
// @flag false | Comincia la ricerca dal campo attuale nella maschera
|
|
{
|
|
static int last = 0;
|
|
|
|
if (first) last = 0;
|
|
|
|
const int max = fields();
|
|
for (int i = last; i < max; i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.in_key(key))
|
|
{
|
|
last = i+1;
|
|
return (TEditable_field*)&f;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
bool TMask::key_valid(word key) const
|
|
{
|
|
const int maxflds = fields();
|
|
bool full = false;
|
|
for (int f = 0; f < maxflds; f++)
|
|
|
|
{
|
|
TMask_field& c = fld(f);
|
|
if (c.is_editable() && c.shown() && c.in_key(key))
|
|
{
|
|
if (c.empty())
|
|
{
|
|
if (c.required())
|
|
return false;
|
|
|
|
}
|
|
else
|
|
full = true;
|
|
}
|
|
}
|
|
return full;
|
|
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Permette di mostrare/nascondere un campo
|
|
void TMask::show(
|
|
short fld_id, // @parm Campo da mostrare/nascondere (default -1)
|
|
bool on) // @parm Indica l'operazione da svolgere sul campo:
|
|
//
|
|
// @flag true | Mostra il campo(default)
|
|
// @flag false | Nasconde il campo
|
|
|
|
// @comm Se <p fld_id> e' -1 allora permette di operare su tutti i campi della maschera
|
|
|
|
{
|
|
if (fld_id <= 0)
|
|
{
|
|
const int gr = -fld_id;
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (gr == 0 || f.in_group(gr))
|
|
f.show(on);
|
|
}
|
|
}
|
|
else field(fld_id).show(on);
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
// @mfunc Rimette lo stato di default del campo
|
|
void TMask::show_default(
|
|
short fld_id) // @parm Identificatore del campo da risettare (default -1)
|
|
|
|
// @comm Se <p parm> Assume il valore -1 vuole dire che vengono risettati tutti i campi della amschera
|
|
{
|
|
if (fld_id <= 0)
|
|
{
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
fld(i).show_default();
|
|
} else field(fld_id).show_default();
|
|
}
|
|
|
|
void TMask::autoload(const TRelation& r)
|
|
{
|
|
FOR_EACH_MASK_FIELD((*this), i, f) if (f->is_loadable())
|
|
((TLoadable_field*)f)->autoload(r);
|
|
}
|
|
|
|
void TMask::autosave(TRelation& r) const
|
|
{
|
|
FOR_EACH_MASK_FIELD((*this), i, f) if (f->is_loadable())
|
|
{
|
|
bool save = f->shown();
|
|
if (!save && f->is_editable())
|
|
{
|
|
// tenta di effettuare il save dei campi Edit hidden:
|
|
// salva il nuovo valore solo se il precedente era blank
|
|
if (f->field() != NULL)
|
|
{
|
|
const char* str = f->field()->read(r);
|
|
save = *str == '\0';
|
|
}
|
|
}
|
|
if (save)
|
|
((TLoadable_field*)f)->autosave(r);
|
|
}
|
|
}
|
|
|
|
void TMask::on_firm_change()
|
|
{
|
|
TString16 firm;
|
|
firm << prefix().get_codditta();
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f._flags.firm)
|
|
{
|
|
f.set(firm);
|
|
f.check(STARTING_CHECK);
|
|
f.on_hit();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void TMask::on_idle()
|
|
{
|
|
if (_focus >= 0 && _focus < fields())
|
|
{
|
|
if (fld(_focus).is_operable())
|
|
{
|
|
TOperable_field& s = (TOperable_field&)fld(_focus);
|
|
s.on_idle();
|
|
|
|
if (_msg_field > 0)
|
|
{
|
|
TMask_field& f = field(_msg_field);
|
|
_msg_field = 0;
|
|
if (_msg_key > 0)
|
|
f.on_key(_msg_key);
|
|
}
|
|
}
|
|
if (_error_severity > 0)
|
|
{
|
|
set_focus();
|
|
switch(_error_severity)
|
|
{
|
|
case 2:
|
|
warning_box("%s", (const char*)_error_message); break;
|
|
case 3:
|
|
error_box("%s", (const char*)_error_message); break;
|
|
default:
|
|
message_box("%s", (const char*)_error_message); break;
|
|
}
|
|
_error_severity = 0;
|
|
}
|
|
if (_test_fld >= 0)
|
|
{
|
|
const TOperable_field & f = focus_field();
|
|
if (_last_test != f.dlg())
|
|
{
|
|
TEditable_field & e = (TEditable_field &) field(_test_fld);
|
|
|
|
if (!f.in_key(0) || !e.has_a_common_key(f))
|
|
{
|
|
e.test_key_complete(false);
|
|
_test_fld = -1;
|
|
}
|
|
_last_test = f.dlg();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Permette di mandare un tasto ad un campo
|
|
void TMask::send_key(
|
|
KEY key, // @parm Codice del tasto da spedire
|
|
short to, // @parm Identificatore del campo che deve ricevere
|
|
TMask_field* from) // @parm Campo che spedisce il tasto
|
|
{
|
|
if (to == 0)
|
|
{
|
|
WINDOW w = from ? from->parent() : win();
|
|
dispatch_e_char(w, key);
|
|
return;
|
|
}
|
|
|
|
if (to > 0)
|
|
{
|
|
if (to == DLG_PAGE)
|
|
{
|
|
CHECK(from, "You should specify a sender!");
|
|
const int p = find_parent_page(*from)+1;
|
|
CHECKD(p > 0 && p < _pages, "You should specify a good page, not ", p);
|
|
key -= K_CTRL+K_SHIFT;
|
|
enable_page(p, key == 's' || key == 'e');
|
|
}
|
|
else
|
|
{
|
|
const int pos = id2pos(to);
|
|
if (pos >= 0)
|
|
{
|
|
if (_msg_field > 0)
|
|
on_idle();
|
|
_msg_field = to;
|
|
_msg_key = key;
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
NFCHECK("Can't send key %u to field %d", key, to);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const int gr = -to;
|
|
for (int i = fields()-1; i >= 0; i--)
|
|
{
|
|
TMask_field& campo = fld(i);
|
|
if (campo.in_group(gr))
|
|
campo.on_key(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Permette di mandare un handler ad controllo o ad una maschera
|
|
void TMask::set_handler(
|
|
short fld_id, // @parm Identificatere del campo che deve ricevere l'handler
|
|
CONTROL_HANDLER handler) // @parm Handler da spedire al campo
|
|
// @parm MASK_HANDLER | handler | Handler da spedire alla maschera
|
|
|
|
// @syntax set_handler(short fld_id, CONTROL_HANDLER handler);
|
|
// @syntax set_handler(MASK_HANDLER handler);
|
|
//
|
|
// @comm Nel primo caso viene mandato un <t CONTROL_HANDLER> al campo indicato
|
|
// da <p fld_id>, mentre nel secondo viene mandato un <t MASK_HANDLER>
|
|
// alla maschera corrente
|
|
|
|
{
|
|
TMask_field& f = field(fld_id);
|
|
CHECKD(f.is_operable(), "Can't set an handler to non-operable field ", fld_id);
|
|
((TOperable_field&)f).set_handler(handler);
|
|
}
|
|
|
|
void TMask::set_handler(MASK_HANDLER handler)
|
|
{
|
|
_handler = handler;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo testo alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TMask_field& TMask::add_static (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
const char* flags) // @parm Flag di controllo del campo (deafult "")
|
|
|
|
// @xref <mf TMask::add_string> <mf TMask::add_number> <mf TMask::add_date>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TText_field* f = new TText_field(this);
|
|
f->construct(id, prompt, x, y, 0, page_win(page), flags);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo stringa alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TEdit_field& TMask::add_string (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dim, // @parm Lunghezza del campo sulla maschera
|
|
const char* flags, // @parm Flag di controllo del campo (defailt "")
|
|
int width) // @parm Lunghezza totale del campo stringa (default 0)
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_number> <mf TMask::add_date>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TEdit_field* f = new TEdit_field(this);
|
|
f->construct(id, prompt, x, y, dim, page_win(page), flags, width);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo bottone alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TButton_field& TMask::add_button (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dx, // @parm Larghezza del campo (in caratteri, default 9)
|
|
int dy, // @parm Altezza del campo (in caratteri, default 1)
|
|
const char* flags, // @parm Flag di controllo del campo (default "")
|
|
short bmpup, // @parm Icona normale
|
|
short bmpdn) // @parm Icona premuta
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
|
|
// <mf TMask::add_date> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TButton_field* f = new TButton_field(this);
|
|
f->construct(id, prompt, x, y, dy, page_win(page), flags, dx, bmpup, bmpdn);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo boolean alla maschera
|
|
//
|
|
// @rdesc Ritorna il descrittore del campo aggiunto
|
|
TBoolean_field& TMask::add_boolean (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
const char* flags) // @parm Flag di controllo del campo (default "")
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
|
|
// <mf TMask::add_date> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TBoolean_field* f = new TBoolean_field(this);
|
|
f->construct(id, prompt, x, y, strlen(prompt), page_win(page), flags);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
TCheckbutton_field& TMask::add_checkbutton (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dx, // @parm Larghezza del campo (in caratteri, default 9)
|
|
int dy, // @parm Altezza del campo (in caratteri, default 1)
|
|
const char* flags, // @parm Flag di controllo del campo (default "")
|
|
short bmpup, // @parm Icona normale
|
|
short bmpdn) // @parm Icona premuta
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
|
|
// <mf TMask::add_date> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TCheckbutton_field* f = new TCheckbutton_field(this);
|
|
f->construct(id, prompt, x, y, dy, page_win(page), flags, dx);
|
|
if (bmpup > 0)
|
|
((TCheckbutton_control*)f->_ctl)->set_icon(bmpup, bmpdn);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo numerico alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TReal_field& TMask::add_number (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dim, // @parm Lunghezza del campo sulla maschera
|
|
const char* flags, // @parm Flag di controllo del campo (default "")
|
|
int ndec) // @parm Numero di decimali (default 0)
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_date>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TReal_field* f = new TReal_field(this);
|
|
f->construct(id, prompt, x, y, dim, page_win(page), flags, ndec);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo importo alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TCurrency_field& TMask::add_currency (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dim, // @parm Lunghezza del campo sulla maschera
|
|
const char* flags, // @parm Flag di controllo del campo (default "")
|
|
short driver) // @parm Campo con codice valuta
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_date>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TCurrency_field* f = new TCurrency_field(this);
|
|
f->construct(id, prompt, x, y, dim, page_win(page), flags, 0);
|
|
if (driver != 0)
|
|
f->add_driver(driver);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo data alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TDate_field& TMask::add_date (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
const char* flags) // @parm Flag di controllo del campo (default "")
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TDate_field* f = new TDate_field(this);
|
|
f->construct(id, prompt, x, y, 10, page_win(page), flags);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo lista alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TList_field& TMask::add_list (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dim, // @parm Lunghezza del campo sulla maschera
|
|
const char* flags, // @parm Flag di controllo del campo (default "")
|
|
const char* codes, // @parm tokenstring con i codici (NULL def.)
|
|
const char* items) // @parm tokenstring con gli items (NULL def.)
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_number> <mf TMask::add_date>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TList_field* f = new TList_field(this);
|
|
f->construct(id, prompt, x, y, dim, page_win(page), flags);
|
|
f->replace_items(codes,items);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo radio button alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TRadio_field& TMask::add_radio(
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dx, // @parm Larghezza del campo (in caratteri)
|
|
const char* codes, // @parm Array di codici delle voci
|
|
const char* items, // @parm Array di prompt delle voci
|
|
const char* flags) // @parm Flag di controllo del campo (default "")
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
|
|
// <mf TMask::add_date> <mf TMask::add_button> <mf TMask::add_memo>
|
|
{
|
|
TRadio_field* f = new TRadio_field(this);
|
|
f->replace_items(codes, items);
|
|
f->construct(id, prompt, x, y, dx, page_win(page), flags, dx);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo zoom alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TZoom_field& TMask::add_zoom (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dim, // @parm Lunghezza del campo sulla maschera
|
|
const char* flags, // @parm Flag di controllo del campo (defailt "")
|
|
int width) // @parm Lunghezza totale del campo stringa (default 0)
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_number> <mf TMask::add_date>
|
|
// <mf TMask::add_button> <mf TMask::add_radio> <mf TMask::add_memo>
|
|
{
|
|
TZoom_field* f = new TZoom_field(this);
|
|
f->construct(id, prompt, x, y, dim, page_win(page), flags, width);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Aggiunge runtime un campo memo alla maschera
|
|
//
|
|
// @rdesc Ritorna l'handle del campo creato
|
|
TMemo_field& TMask::add_memo(
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dx, // @parm Larghezza del campo (in caratteri, deafilt 78)
|
|
int dy, // @parm Altezza del campo (in caratteri, default 4)
|
|
const char* flags) // @parm Flag di controllo del campo (default "")
|
|
|
|
// @xref <mf TMask::add_static> <mf TMask::add_string> <mf TMask::add_number>
|
|
// <mf TMask::add_date> <mf TMask::add_button> <mf TMask::add_radio>
|
|
{
|
|
TMemo_field* f = new TMemo_field(this);
|
|
f->construct(id, prompt, x, y, dy, page_win(page), flags, dx);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
TTree_field& TMask::add_tree (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dx, // @parm Larghezza del campo sulla maschera
|
|
int dy, // @parm Altezza del campo sulla maschera
|
|
const char* flags)// @parm Flag di controllo del campo (default "")
|
|
{
|
|
TTree_field* f = new TTree_field(this);
|
|
f->construct(id, "", x, y, dy, page_win(page), flags, dx);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
TGroup_field& TMask::add_groupbox (
|
|
short id, // @parm Numero identificatore del campo da aggiungere
|
|
int page, // @parm Pagina nel quale aggiungere il campo
|
|
const char* prompt, // @parm Prompt del campo
|
|
int x, // @parm Coordinata x (in caratteri)
|
|
int y, // @parm Coordinata y (in caratteri)
|
|
int dx, // @parm Larghezza del campo sulla maschera
|
|
int dy, // @parm Altezza del campo sulla maschera
|
|
const char* flags)// @parm Flag di controllo del campo (default "")
|
|
{
|
|
TGroup_field* f = new TGroup_field(this);
|
|
f->construct(id, prompt, x, y, dy, page_win(page), flags, dx);
|
|
add_field(f);
|
|
return *f;
|
|
}
|
|
|
|
TButton_tool& TMask::add_button_tool(short id, const char* prompt, short bmpup)
|
|
{
|
|
if (toolbar() == NULL_WIN) // Se non c'e' ancora la toolbar, me l'invento adesso
|
|
create_bar(1);
|
|
CHECKD(id <= 0 || bmpup > 0, "Toolbar button needs a wonderful icon ", id);
|
|
TButton_tool* t = new TButton_tool(this);
|
|
t->construct(id, prompt, 0, 0, 0, toolbar(), "", 0, bmpup, 0);
|
|
add_field(t);
|
|
return *t;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Salva i valori dei campi della maschera sul file di salvataggio
|
|
//
|
|
// @rdesc Ritorna il risultato dell'operazione:
|
|
//
|
|
// @flag true | Se l'operazione e' avvenuta corretamente
|
|
// @flag false | Se non si riesce a creare il file di salvataggio
|
|
bool TMask::save(
|
|
bool append) const // @parm Indica se creare il file o appendere (true) le informazioni
|
|
// ad uno gia' esistente (false, default).
|
|
|
|
{
|
|
FILE* f = fopen(_workfile, append ? "a" : "w");
|
|
|
|
if (f == NULL)
|
|
return yesnofatal_box("Non posso aprire %s ", (const char*) _workfile);
|
|
|
|
const int max = fields();
|
|
for (int i = 0; i < max; i++)
|
|
{
|
|
TMask_field& c = fld(i);
|
|
if (c.is_editable())
|
|
fprintf(f, "%d|%s\n", c.dlg(), (const char*)c.get());
|
|
}
|
|
fprintf(f, "[EOM]\n");
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Legge i valori dei campi della maschera da file di salvataggioo
|
|
//
|
|
// @rdesc Ritorna il risultato dell'operazione:
|
|
//
|
|
// @flag true | Se l'operazione e' avvenuta corretamente
|
|
// @flag false | Se non si riesce a leggere il file di salvataggio
|
|
bool TMask::load(
|
|
bool reset) // @parm Indica la posizione di lettura del file:
|
|
//
|
|
// @flag true | Comincia la lettura dell'inizio
|
|
// @flag false | Comincia la lettura dalla posizione corrente dell'offset
|
|
|
|
{
|
|
FILE* f = fopen(_workfile, "r");
|
|
|
|
if (f == NULL) return false;
|
|
if (reset) _lastpos = 0;
|
|
fseek(f, _lastpos, SEEK_SET);
|
|
TToken_string t(256);
|
|
char* buffer = t.get_buffer();
|
|
while (fgets(buffer, t.size(), f) != NULL && t != "[EOM]")
|
|
{
|
|
if (t.not_empty())
|
|
{
|
|
t.rtrim();
|
|
const int pos = id2pos(t.get_int(0));
|
|
if (pos >= 0) fld(pos).set(t.get());
|
|
}
|
|
}
|
|
_lastpos = ftell(f);
|
|
fclose(f);
|
|
return true;
|
|
}
|
|
|
|
// @doc EXTERNAL
|
|
|
|
// @mfunc Copia i valori dei campi dalla maschera <p m>
|
|
//
|
|
void TMask::copy_values(
|
|
const TMask& m) // @parm Maschera sorgente
|
|
|
|
{
|
|
reset();
|
|
const int nfields = fields();
|
|
for (int i = 0; i < nfields; i++)
|
|
{
|
|
TMask_field& dest_field = fld( i );
|
|
|
|
if (dest_field.is_editable() )
|
|
{
|
|
const int pos = m.id2pos(dest_field.dlg());
|
|
if (pos >= 0)
|
|
{
|
|
const TMask_field& source_field = m.fld(pos);
|
|
dest_field.set( source_field.get( ) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool TMask::make_profile_name(TFilename& f) const
|
|
{
|
|
f = source_file().name();
|
|
f.ext("ini");
|
|
if (!f.custom_path()) // Prova a cercare la configurazione in custom ...
|
|
{ // ... altrimenti riprova in config
|
|
f =::firm2dir(-1); // Directory dati
|
|
f.add("config"); // Directory config
|
|
f.add(source_file().name()); // Nome Maschera
|
|
f.ext("ini"); // Estensione
|
|
}
|
|
return f.exist();
|
|
}
|
|
|
|
int TMask::save_profile(int num, const char* desc) const
|
|
{
|
|
TFilename prof;
|
|
make_profile_name(prof);
|
|
TConfig ini(prof, "Main");
|
|
|
|
if (num == 0)
|
|
num = ini.get_int(user());
|
|
if (num <= 0)
|
|
{
|
|
TString_array p;
|
|
TBit_array b(256);
|
|
b.set(255); b.set(); b.reset(0);
|
|
ini.list_paragraphs(p);
|
|
FOR_EACH_ARRAY_ROW_BACK(p, r, row)
|
|
{
|
|
num = atoi(*row);
|
|
if (num > 0)
|
|
b.reset(num);
|
|
}
|
|
num = int(b.first_one());
|
|
}
|
|
|
|
TString16 para; para << num;
|
|
ini.set(user(), para);
|
|
|
|
TString description = desc;
|
|
if (ini.set_paragraph(para))
|
|
{
|
|
if (description.blank())
|
|
description = ini.get("Description");
|
|
ini.remove_all();
|
|
}
|
|
else
|
|
{
|
|
if (description.blank())
|
|
description << "Profilo standard per " << user();
|
|
}
|
|
ini.set("Description", description);
|
|
|
|
TString16 name;
|
|
|
|
for (int i = 0; i < fields(); i++)
|
|
{
|
|
TMask_field& f = fld(i);
|
|
if (f.is_loadable() && f.get_default().empty())
|
|
{
|
|
name.format("F_%d", f.dlg());
|
|
if (f.is_sheet())
|
|
{
|
|
TSheet_field& s = (TSheet_field&)f;
|
|
FOR_EACH_SHEET_ROW(s, r, row)
|
|
ini.set(name, *row, NULL, true, r);
|
|
}
|
|
else
|
|
{
|
|
if (!f.is_firm())
|
|
ini.set(name, f.get());
|
|
}
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
int TMask::load_profile(int num, bool reset)
|
|
{
|
|
TFilename prof;
|
|
if (make_profile_name(prof))
|
|
{
|
|
TConfig ini(prof, "Main");
|
|
if (num <= 0)
|
|
num = ini.get_int(user());
|
|
else
|
|
ini.set(user(), num);
|
|
|
|
TString16 name; name << num;
|
|
TAssoc_array& var = ini.list_variables(name);
|
|
if (var.items() > 0)
|
|
{
|
|
for (int pos = fields()-1; pos >= 0; pos--)
|
|
{
|
|
TMask_field& f = fld(pos);
|
|
if (f.is_loadable() && f.get_default().empty())
|
|
{
|
|
name.format("F_%d", f.dlg());
|
|
if (reset || var.objptr(name) != NULL)
|
|
{
|
|
if (f.is_sheet())
|
|
{
|
|
TSheet_field& sf = (TSheet_field&)f;
|
|
sf.destroy();
|
|
for (int r = 0; ini.exist(name,r); r++)
|
|
sf.row(r) = ini.get(name, NULL, r);
|
|
sf.force_update();
|
|
}
|
|
else
|
|
{
|
|
if (!f.is_firm())
|
|
f.set(ini.get(name));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
bool TMask::kill_profile(int num)
|
|
{
|
|
TFilename prof;
|
|
make_profile_name(prof);
|
|
|
|
TConfig ini(prof, "Main");
|
|
if (num <= 0)
|
|
num = ini.get_int(user());
|
|
|
|
TString8 name; name << num;
|
|
const bool ok = ini.set_paragraph(name);
|
|
if (ok)
|
|
ini.remove_all();
|
|
|
|
return ok;
|
|
}
|
|
|
|
void TMask::load_defaults()
|
|
{
|
|
for (int pos = 0; pos < fields(); pos++)
|
|
{
|
|
TMask_field& f = fld(pos);
|
|
|
|
if (f.is_loadable())
|
|
{
|
|
const TString & def = f.get_default();
|
|
|
|
if (def.full())
|
|
f.set(def);
|
|
}
|
|
}
|
|
}
|
|
|
|
const char* TMask::get_caption(TString& str) const
|
|
{
|
|
char* title = str.get_buffer(128);
|
|
xvt_vobj_get_title(win(), title, str.size());
|
|
return title;
|
|
}
|
|
|
|
void TMask::set_caption(const char* c)
|
|
{
|
|
TToken_string captions(c);
|
|
if (_notebook != NULL_WIN)
|
|
{
|
|
for (int p = 0; p < _pages; p++)
|
|
{
|
|
const char* title = captions.get();
|
|
if (title == NULL)
|
|
title = captions.get(0);
|
|
xvt_notebk_set_tab_title(_notebook, p, title);
|
|
}
|
|
}
|
|
else
|
|
xvt_vobj_set_title(win(), captions.get(0));
|
|
}
|
|
|
|
void TMask::post_error_message(const char* msg, int sev)
|
|
{
|
|
CHECK(sev > 0 && msg, "Bad error message posted");
|
|
if (_error_severity > 0) // C'e' gia' un messaggio d'errore in coda
|
|
on_idle();
|
|
_error_message = msg;
|
|
_error_severity = sev;
|
|
}
|
|
|
|
// @doc INTERNAL
|
|
|
|
// @mfunc costruttore di copia
|
|
TTimed_box::TTimed_box(const char * header,const char * message,int seconds,short button_id,int x,int y)
|
|
: TMask(header,1,x,y)
|
|
{
|
|
// costruisce una maschera run time
|
|
add_memo(FIRST_FIELD, 0, "", 1, 0,-1,-3);
|
|
set(FIRST_FIELD, message);
|
|
|
|
// setta il timer per l'evento
|
|
_timer_delay=seconds * 1000 + 1;
|
|
_timer_id=XVT_TIMER_ERROR;
|
|
_button_id=button_id;
|
|
}
|
|
|
|
void TTimed_box::start_run()
|
|
{
|
|
if (_timer_id!=XVT_TIMER_ERROR)
|
|
xvt_timer_destroy(_timer_id);
|
|
_timer_id=xvt_timer_create(win(),_timer_delay);
|
|
TMask::start_run();
|
|
}
|
|
|
|
void TTimed_box::handler(WINDOW win, EVENT* ep)
|
|
{
|
|
if (ep->type == E_TIMER && ep->v.timer.id==_timer_id)
|
|
send_key(K_SPACE, _button_id);
|
|
TMask::handler(win, ep);
|
|
}
|
|
|
|
TTimed_box::~TTimed_box()
|
|
{
|
|
if (_timer_id != XVT_TIMER_ERROR)
|
|
xvt_timer_destroy(_timer_id);
|
|
}
|
|
|
|
TTimed_breakbox::TTimed_breakbox(const char * message,int seconds,int x,int y)
|
|
: TTimed_box(TR("Richiesta di interruzione"),message,seconds,DLG_OK,x,y)
|
|
{
|
|
add_button(DLG_CANCEL, 0, TR("Interrompi"), -22, -1, 12, 2,"",0);
|
|
add_button(DLG_OK, 0, TR("Riprova"), -12, -1, 12, 2,"",0);
|
|
}
|
|
|
|
TTimed_breakbox::~TTimed_breakbox()
|
|
{}
|
|
|
|
// @doc INTERNAL
|
|
|
|
// @mfunc costruttore di copia
|
|
TYesnoallnone_box::TYesnoallnone_box(const char * message, int default_key)
|
|
: TMask(TR("Richiesta"),1,40,8)
|
|
|
|
{
|
|
// costruisce una maschera run time
|
|
add_memo(FIRST_FIELD, 0, "", 1, 0,-1,-3);
|
|
set(FIRST_FIELD, message);
|
|
disable(FIRST_FIELD);
|
|
add_button(DLG_OK, 0, TR("Tutti"), -14, -1, 8, 2,"",0);
|
|
add_button(FIRST_FIELD+1, 0, TR("Si"), -24, -1, 8, 2,"",0).set_exit_key(K_YES);
|
|
add_button(FIRST_FIELD+2, 0, TR("No"), -34, -1, 8, 2,"",0).set_exit_key(K_NO);
|
|
add_button(DLG_CANCEL, 0, TR("Nessuno"), -44, -1, 8, 2,"",0);
|
|
switch (default_key)
|
|
{
|
|
case K_ENTER:
|
|
first_focus(DLG_OK); break;
|
|
case K_ESC:
|
|
first_focus(DLG_CANCEL); break;
|
|
case K_NO:
|
|
first_focus(FIRST_FIELD+2); break;
|
|
default:
|
|
first_focus(FIRST_FIELD+1);
|
|
}
|
|
}
|
|
|
|
TYesnoallnone_box::~TYesnoallnone_box()
|
|
{}
|