campo-sirio/include/relapp.cpp
guy 6bac6177c4 Patch level : 2.0 nopatch
Files correlati     :
Ricompilazione Demo : [ ]
Commento            :

Aggiunte utili per gestione dizionario
assoc.cpp    Aggiunta possibilita' di selezionare un elemento causale di un assoc
controls.cpp Corretta gestione scrollbar dei listbox
diction.cpp  Migliorata selezione caratteri da trimmare dalle traduzioni
isam.cpp     Corretto azzeramento dei memo (non azzerava il TString_array corrispondente)
msksheet.cpp Resa personalizzabile la larghezza della colonna col numero di riga
netsock.cpp  Migliorata gestione "a capo" in protocollo soap
progind.cpp  Corretto posizionamento progind sovrapposte
relapp.cpp   Cambiato un messaggio di richiesta annullamento


git-svn-id: svn://10.65.10.50/trunk@11651 c028cbd2-c16b-5b4b-a496-9718f37d4682
2003-12-03 09:41:16 +00:00

1949 lines
47 KiB
C++
Executable File
Raw Blame History

#include <defmask.h>
#include <mailbox.h>
#include <msksheet.h>
#include <postman.h>
#include <progind.h>
#include <sheet.h>
#include <recarray.h>
#include <relapp.h>
#include <urldefid.h>
#include <utility.h>
///////////////////////////////////////////////////////////
// TRelation_application
///////////////////////////////////////////////////////////
TRelation_application::TRelation_application()
: _mask(NULL), _search_id(-1), _lnflag(0),
_autodelete(0), _navigating(false)
{ }
TRelation_application::~TRelation_application()
{ }
TCursor& TRelation_application::get_filtered_cursor() const
{
TEdit_field& f = get_search_field();
TCursor* cur = f.browse()->cursor();
return *cur;
}
void TRelation_application::setkey()
{
if (has_filtered_cursor())
{
get_filtered_cursor().setkey();
return; // ?????
}
file().setkey(1);
}
void TRelation_application::set_key_filter()
{
TString rf = get_user_read_filter(); rf.trim();
if (rf.not_empty())
{
TString expr;
for (int f = _mask->fields()-1; f >= 0; f--)
{
TMask_field& fld= _mask->fld(f);
if (fld.is_edit() && fld.in_key(0))
{
TBrowse* b = ((TEdit_field&)fld).browse();
if (b && b->cursor()->relation()->lfile().num() == get_relation()->lfile().num())
{
expr = b->get_filter();
if (expr.find(rf) < 0)
{
if (expr.not_empty())
{
expr.insert("(", 0);
expr << ")AND(" << rf << ')';
}
else
expr = rf;
b->set_filter(expr);
}
}
}
}
}
}
// @doc INTERNAL
// @mfunc Setta i limiti
void TRelation_application::set_limits(
byte what) // @parm tipo di limite da assegnare al record
// @comm I limiti possibili sono:
// @flag 0 | Nessuna operazione
// @flag 1 | Primo record
// @flag 2 | Ultimo record
// @flag 3 | Entrambi
{
if (has_filtered_cursor())
{
TEdit_field& f = get_search_field();
TBrowse* b = f.browse();
TCursor* cur = b != NULL ? b->cursor() : NULL;
if (cur)
{
cur->setkey();
b->do_input(TRUE);
if (cur->items() == 0)
_first = _last = -1;
else
{
if (what & 0x1)
{
*cur = 0;
_first = cur->file().recno();
}
if (what & 0x2)
{
*cur = cur->items() - 1;
_last = cur->file().recno();
}
}
return;
}
}
file().setkey(1);
if (what & 0x1)
{
if (!file().empty() && file().first() == NOERR)
_first = file().recno();
else
_first = -1;
}
if (what & 0x2)
{
if (!file().empty() && file().last() == NOERR)
_last = file().recno();
else
_last = -1;
}
}
void TRelation_application::set_find_button()
{
int pos = _mask->id2pos(DLG_FINDREC);
if (pos >= 0 && _mask->id2pos(DLG_FIRSTREC) >= 0) //se e' un bottone pentapartito...
{
TButton_field& f_find = (TButton_field &)_mask->fld(pos);
RCT rct_base; f_find.get_rect(rct_base);
const int bwidth = (rct_base.right - rct_base.left);
const int bheight = (rct_base.bottom - rct_base.top);
if (bwidth > 3*bheight/2) // Controllo se ho gia' ridimensionato i bottoni in precedenza
{
int bx = bwidth / 3;
int by = bheight / 2;
RCT r = rct_base; r.left += bx-2; r.right -= bx-2;
f_find.set_rect(r); // Ridimensiona il bottone centrale di ricerca
bx += 5; by += 3; // Aggiusta dimensioni bottoni sussidiari
pos = _mask->id2pos(DLG_FIRSTREC);
if (pos >= 0)
{
r = rct_base; r.top = r.bottom - by; r.right = r.left + bx;
_mask->fld(pos).set_rect(r);
}
pos = _mask->id2pos(DLG_PREVREC);
if (pos >= 0)
{
r = rct_base; r.bottom = r.top + by; r.right = r.left + bx;
_mask->fld(pos).set_rect(r);
}
pos = _mask->id2pos(DLG_NEXTREC);
if (pos >= 0)
{
r = rct_base; r.bottom = r.top + by; r.left = r.right - bx;
_mask->fld(pos).set_rect(r);
}
pos = _mask->id2pos(DLG_LASTREC);
if (pos >= 0)
{
r = rct_base; r.top = r.bottom - by; r.left = r.right - bx;
_mask->fld(pos).set_rect(r);
}
}
}
}
bool TRelation_application::create()
{
bool ok = user_create();
if (ok)
{
write_enable();
_mask = get_mask(MODE_QUERY);
filter();
set_key_filter();
set_limits();
}
return ok ? TSkeleton_application::create() : ok;
}
bool TRelation_application::destroy()
{
user_destroy();
return TSkeleton_application::destroy();
}
void TRelation_application::set_fixed()
{
TToken_string s(256, '=');
for (const char* f = _fixed.get(0); f && *f; f = _fixed.get())
{
s = f;
const int id = s.get_int(0);
s = s.get();
if (s.not_empty())
_mask->set(id, s);
if (_lnflag < 2)
_mask->disable(id);
}
}
void TRelation_application::enable_query()
{
const bool query = _mask->query_mode();
const bool keyon = query || get_relation()->status() == _isreinsert;
for (int i = _mask->fields() - 1; i >= 0; i--)
{
TMask_field& c = _mask->fld(i);
if (c.in_key(0) && c.enabled_default())
{
if (c.in_key(1))
c.enable(keyon);
if (c.is_edit())
{
TEdit_field& e = (TEdit_field&)c;
if (e.browse() != NULL)
e.enable_check(query);
}
}
}
set_fixed();
}
void TRelation_application::set_toolbar()
{
const int mode = _mask->mode();
const bool can_edit_some = user_can_write(NULL);
int pos = _mask->id2pos(DLG_SAVEREC);
if (pos >= 0)
{
bool enabsave=mode != MODE_QUERY;
if (enabsave)
enabsave = user_can_write(get_relation());
_mask->fld(pos).enable(enabsave);
}
pos = _mask->id2pos(DLG_DELREC);
if (pos >= 0)
{
bool enabdel = (mode == MODE_QUERY && can_edit_some) || (mode == MODE_MOD);
if (enabdel && mode == MODE_MOD)
{
TRelation& r = *get_relation();
const TRecnotype oldpos = r.lfile().recno();
enabdel = !protected_record(r);
if (r.lfile().recno() != oldpos)
r.lfile().readat(oldpos);
}
_mask->fld(pos).enable(enabdel);
}
pos = _mask->id2pos(DLG_FINDREC);
if (pos >= 0)
{
_mask->fld(pos).enable(_lnflag == 0);
const long recno = get_relation()->lfile().recno();
const bool enable_next_prev = _mask->edit_mode();
pos = _mask->id2pos(DLG_FIRSTREC);
if (pos >= 0)
_mask->fld(pos).enable(_lnflag == 0 && (enable_next_prev ? _first != recno : _first > 0));
pos = _mask->id2pos(DLG_PREVREC);
if (pos >= 0)
_mask->fld(pos).enable(_lnflag == 0 && enable_next_prev && _first > 0 && _first != recno);
pos = _mask->id2pos(DLG_NEXTREC);
if (pos >= 0)
_mask->fld(pos).enable(_lnflag == 0 && enable_next_prev && _last > 0 && _last != recno);
pos = _mask->id2pos(DLG_LASTREC);
if (pos >= 0)
_mask->fld(pos).enable(_lnflag == 0 && (enable_next_prev ? _last != recno : _last > 0));
}
pos = _mask->id2pos(DLG_NEWREC);
if (pos >= 0)
{
bool enabins = (mode == MODE_QUERY || _lnflag == 0) && can_edit_some;
_mask->fld(pos).enable(enabins);
}
set_find_button();
enable_query();
}
void TRelation_application::update_navigation_bar()
{
if (_mask->query_mode())
{
set_limits();
set_toolbar();
}
}
bool TRelation_application::save_and_new() const
{ return FALSE; }
int TRelation_application::set_mode(int mode)
{
static int _mode = NO_MODE;
if (mode < NO_MODE) mode = _mode;
const int m = ((TMaskmode)mode == NO_MODE) ? (int) MODE_QUERY : mode;
_mask->set_mode(m);
set_toolbar();
_mode = mode;
const char* t = "";
switch(mode)
{
case MODE_QUERY:
t = "Ricerca"; break;
case MODE_MOD:
t = "Modifica"; break;
case NO_MODE:
t = "Ricerca/Inserimento"; break;
case MODE_INS:
t = "Inserimento"; break;
default:
break;
}
xvt_statbar_set(t, TRUE);
return _mode;
}
// @doc INTERNAL
// @mfunc Permette di autonumerare un record
//
// @rdesc Ritorna se e' riuscito a creare una nuova autonumerazione:
//
// @flag TRUE | La chiave non e' vuota
// @flag FALSE | Non e' riuscito ad autonumerare il documento
bool TRelation_application::autonum(
TMask* m, // @parm Maschera a cui applicare l'autonumerazione
bool rec) // @parm Indica se registrare la chiave anche sul record corrente
{
TToken_string k;
if (!get_next_key(k))
{
k = get_next_key();
/*
if (k.not_empty())
NFCHECK("La 'const char* get_next_key()' verra' sostituita dalla 'bool get_next_key(TToken_string&)'");
*/
}
if (!rec && !m->query_mode())
m->reset();
_renum_message = "";
for (const char* n = k.get(0); n && *n; n = k.get())
{
const short id = atoi(n);
CHECKD (id > 0 && m->id2pos(id) >= 0, "Identificatore di autonumerazione errato: ", id);
const char* val = k.get();
TMask_field& f = m->field(id);
if (rec || f.empty())
f.set(val);
if (rec)
((TEditable_field&)f).autosave(*get_relation());
if (_renum_message.empty() || f.in_key(1))
_renum_message.format("L'elemento e' stato registrato con :\n %s = %s", (const char *) f.prompt(), (const char *) f.get());
}
return k.not_empty();
}
// @doc EXTERNAL
// @mfunc Entra in modo di ricerca
void TRelation_application::query_mode(
bool pre_ins) // @parm Indica in quale modo andare:
//
// @flag TRUE | Entra in modo MODE_QUERY_INSERT
// @flag FALSE | Entra in modo MODE_QUERY (default)
{
TMask* old = _mask;
const bool was_open = old != NULL && old->is_open();
const bool changing = changing_mask(MODE_QUERY);
if (changing && was_open)
old->close_modal();
_mask = get_mask(MODE_QUERY);
if (changing)
{
if (was_open)
_mask->open_modal();
set_limits();
}
_mask->set_mode(pre_ins ? MODE_QUERYINS : MODE_QUERY);
_mask->reset();
_mask->disable_page(1); // Nasconde pagine inutili
if (pre_ins)
{
set_mode(NO_MODE);
init_query_insert_mode(*_mask);
}
else
{
set_mode(MODE_QUERY);
init_query_mode(*_mask);
// Aggiorna bottoni di ricerca: utile soprattutto per ve0 che imposta CODNUM
if (has_filtered_cursor())
{
set_limits();
set_toolbar();
}
}
}
void TRelation_application::insert_mode()
{
bool try_auto = TRUE;
if (_mask->query_mode())
try_auto = test_key(1, FALSE) == FALSE;
if (try_auto && !autonum(_mask, FALSE))
{
query_insert_mode();
return;
}
TRelation* r = get_relation();
for (int f = _mask->fields()-1; f >= 0; f--)
{
TMask_field& fld = _mask->fld(f);
if (fld.is_loadable() && fld.shown() && fld.in_key(0))
((TLoadable_field&)fld).autosave(*r);
}
if (!user_can_write(r))
{
warning_box("L'utente %s non puo' inserire in questo archivio",
(const char*)user());
return;
}
const bool changing = changing_mask(MODE_INS);
TFilename workname; workname.temp("msk");
if (changing)
{
_mask->set_workfile(workname);
_mask->save();
_mask->close_modal();
}
_mask = get_mask(MODE_INS);
if (changing)
{
_mask->reset();
_mask->set_workfile(workname);
_mask->load();
::remove(workname);
_mask->open_modal();
}
else
_mask->enable_page(1);
set_mode(MODE_INS);
r->zero(); // Azzera tutta la relazione!
init_insert_mode(*_mask);
// ....possibilmente spostare questa chiamata .....
if (_curr_transaction == TRANSACTION_INSERT)
ini2insert_mask();
}
bool TRelation_application::modify_mode()
{
TRelation* rel = get_relation();
if (!user_can_read(rel))
{
warning_box(TR("I dati non sono accessibili per l'utente %s"), (const char*)user());
query_mode();
return FALSE;
}
const TReclock block = user_can_write(rel) ? _testandlock : _nolock;
int err = rel->read(_isequal, block);
if (err != NOERR)
{
if (err == _islocked)
message_box("I dati sono gia' usati da un altro programma");
else
error_box("Impossibile leggere i dati: errore %d", err);
query_mode();
return FALSE;
}
const bool changing = changing_mask(MODE_MOD);
if (changing)
_mask->close_modal();
_mask = get_mask(MODE_MOD);
if (changing)
_mask->open_modal();
else
_mask->enable_page(1);
set_mode(MODE_MOD);
err = read(*_mask);
if (err != NOERR)
{
query_mode();
return FALSE;
}
rel->save_status();
init_modify_mode(*_mask);
// ....possibilmente spostare questa chiamata .....
// Forse deve essere fatta prima della init_modify_mode()!
if (_curr_transaction == TRANSACTION_MODIFY)
ini2insert_mask();
return TRUE;
}
TEdit_field& TRelation_application::get_search_field() const
{
short id = _search_id;
if (id <= 0 || !_mask->field(id).shown())
{
for (int i = _mask->fields()-1; i >= 0; i--)
{
const TMask_field& f = _mask->fld(i);
if (f.is_edit() && f.in_key(1) && f.shown())
{
id = f.dlg();
break;
}
}
}
return _mask->efield(id);
}
bool TRelation_application::search_mode()
{
if (_mask->mode() != MODE_QUERY)
query_mode();
TEdit_field* prima = &get_search_field();
while (prima)
{
if (prima->on_key(K_F9))
{
if (find(1))
return modify_mode();
else
break;
}
TMask_field* dopo = &_mask->focus_field();
if (dopo != prima && dopo->is_edit() && dopo->in_key(0))
prima = (TEdit_field*)dopo;
else
break;
}
return FALSE;
}
HIDDEN bool delete_handler(TMask_field& f, KEY k)
{
if (k == K_TAB && f.focusdirty() && !f.empty())
{
TMask& m = f.mask();
const bool finale = *f.prompt() == 'A';
const int pos = m.id2pos(f.dlg());
if (pos >= 3)
{
const TMask_field& e = m.fld(pos - 3);
if (e.is_edit() && e.get().blank())
{
const short id = e.dlg() - (finale ? 200 : 100);
const TRelation_application& app = (TRelation_application&)main_app();
const TMask_field& orig = app.curr_mask().field(id);
bool req = orig.required();
if (!req && orig.is_edit())
{
const TEdit_field& e = (TEdit_field&)orig;
req = e.validate_func() == 12;
}
if (req)
{
TString str; str << (finale ? "A " : "Da ") << orig.prompt();
return f.error_box("Specificare anche il valore %s", (const char*)str);
}
}
}
if (finale && !f.get().blank())
{
const TMask_field& p = m.fld(pos - 1);
TString80 prec = p.get();
TString80 curr = f.get();
bool ok;
switch (p.class_id())
{
case CLASS_REAL_FIELD: ok = real(prec) <= real(curr); break;
case CLASS_DATE_FIELD: ok = TDate(prec) <= TDate(curr); break;
default : ok = prec <= curr; break;
}
if (!ok)
return f.error_box("Inserire un valore non inferiore a '%s'", (const char*)prec);
}
}
return TRUE;
}
int TRelation_application::delete_mode()
{
TEdit_field& fld = get_search_field();
TBrowse* brw = fld.browse();
if (brw)
{
brw->do_input(TRUE);
TCursor& cur = *brw->cursor();
TToken_string head(brw->head());
head.insert("@1|", 0);
TToken_string items(brw->items());
items.insert(" |", 0);
int tab1 = 0, tab2 = 0, y = 0;
// Nuovo modo basato sugli input
TToken_string inplist = brw->get_input_fields();
if (inplist.not_empty())
{
FOR_EACH_TOKEN(inplist, tok)
{
if (*tok != '"' && strchr(tok, '@') == NULL)
{
TMask_field& e = _mask->field(short(atoi(tok)));
if (e.active())
{
const int len = strlen(e.prompt());
if (len > tab1) tab1 = len;
const int size = e.size();
if (size > tab2) tab2 = size;
y++;
}
}
}
}
tab1 += 5;
tab2 += tab1+2;
cur = 0L;
TCursor_sheet sht(&cur, items, "Eliminazione", head, 0x4, y);
y = -1; // Posizione del campo precedente
TString prompt; // Prompt del campo corrente
const short delta = 100;
FOR_EACH_TOKEN(inplist, tok)
{
if (*tok != '"' && strchr(tok, '@') == NULL)
{
TMask_field& e = _mask->field(short(atoi(tok)));
if (!e.active())
continue;
const short id = e.dlg()+delta;
prompt = "Da "; prompt << e.prompt();
sht.add_static(DLG_NULL, 0, prompt, 1, ++y);
TString16 flags;
if (e.automagic()) flags << 'A';
if (e.roman()) flags << 'M';
if (e.right_justified()) flags << 'R';
if (e.uppercase()) flags << 'U';
if (e.zerofilled()) flags << 'Z';
switch (e.class_id())
{
case CLASS_DATE_FIELD:
{
TDate_field& d1 = sht.add_date(id, 0, "", tab1, y, flags);
TDate_field& d2 = sht.add_date(id+delta, 0, "A ", tab2, y, flags);
d1.set_handler(delete_handler);
d2.set_handler(delete_handler);
}
break;
case CLASS_REAL_FIELD:
{
TReal_field& r1 = sht.add_number(id, 0, "", tab1, y, e.size(), flags);
TReal_field& r2 = sht.add_number(id+delta, 0, "A ", tab2, y, e.size(), flags);
r1.set_handler(delete_handler);
r2.set_handler(delete_handler);
}
break;
default:
{
TEdit_field& e1 = sht.add_string(id, 0, "", tab1, y, e.size(), flags);
TEdit_field& e2 = sht.add_string(id+delta, 0, "A ", tab2, y, e.size(), flags);
e1.set_handler(delete_handler);
e2.set_handler(delete_handler);
}
break;
}
if (y == 0)
sht.first_focus(id);
}
}
sht.open_modal();
KEY tasto;
bool keep_running = TRUE;
while (keep_running)
{
tasto = sht.run();
if (tasto == K_ENTER)
{
TRectype rec_from(cur.curr()), rec_to(cur.curr());
rec_from.zero(); rec_to.zero();
TToken_string fldlist = brw->get_input_field_names();
TString80 str;
int fi = 0;
for (TString80 tok = inplist.get(fi); tok.not_empty(); tok = inplist.get(), fi++)
{
const TString16 fn = fldlist.get(fi);
const TFieldref fr(fn, 0);
if (*tok == '"')
{
str = tok;
str.ltrim(1); str.rtrim(1);
fr.write(str, rec_from);
fr.write(str, rec_to);
}
else
{
const short id = short(atoi(tok));
if (sht.id2pos(id+delta) >= 0)
{
str = sht.get(id+delta);
if (str.not_empty())
fr.write(str, rec_from);
str = sht.get(id+2*delta);
if (str.not_empty())
fr.write(str, rec_to);
}
else
{
str = _mask->get(id);
fr.write(str, rec_from);
fr.write(str, rec_to);
}
}
}
if (rec_from.empty() && rec_to.empty())
{
sht.check(-1);
}
else
{
const long totit = cur.items();
cur.freeze(TRUE);
cur.curr() = rec_from;
cur.read();
while (cur.pos() < totit && cur.curr() <= rec_to)
{
sht.check(cur.pos());
++cur;
}
cur.freeze(FALSE);
}
}
else
{
keep_running = FALSE;
if (tasto == K_DEL && sht.checked() == 0)
{
error_box("Non e' stato selezionato nessun elemento");
sht.select(0);
keep_running = TRUE;
}
}
}
sht.close_modal();
if (tasto == K_DEL)
{
long deleting = sht.checked();
TString msg;
msg.format(FR("Confermare l'eliminazione di %d elementi"), deleting);
bool can_delete = yesno_box(msg);
if (can_delete && deleting > 100)
{
msg.insert(TR("ATTENZIONE: "), 0);
can_delete = yesno_box(msg);
}
if (can_delete)
{
TWait_cursor hourglass;
long skipped = 0; // Record non cancellati perche' protetti
cur.freeze(TRUE); // Congelo il cursore altrimenti si riaggiorna troppo
for (long pos = sht.items()-1; deleting > 0; pos--)
{
if (sht.checked(pos))
{
cur = pos;
brw->do_output();
bool can_delete = FALSE;
if (find(1))
{
TRelation& r = *get_relation();
_autodelete = 0x3;
if (!protected_record(r))
{
if (modify_mode())
{
r.restore_status();
can_delete = remove();
}
query_mode();
}
_autodelete = FALSE;
}
if (!can_delete)
skipped++;
deleting--;
}
}
cur.freeze(FALSE);
set_limits(); // Riaggiorno il numero del primo/ultimo record
if (skipped > 0)
{
warning_box(FR("%ld elementi non sono stati cancellati in quanto protetti."), skipped);
query_mode();
}
}
}
}
else
{
if (search_mode())
_autodelete = TRUE;
}
return TRUE;
}
// @doc INTERNAL
// @mfunc Controlla se una chiave e' completa ed esiste su file
//
// @rdesc Ritorna se la chave esiste sul file
bool TRelation_application::test_key(
word k, // @parm Chiave da ricercare
bool err) // @parm Indica se visualizzare eventuali errori occorsi
{
bool onereq = FALSE, onefill = FALSE;
for (TEditable_field* e = _mask->get_key_field(k, TRUE);
e != NULL;
e = _mask->get_key_field(k, FALSE))
{
if (e->required() && e->shown())
{
onereq = TRUE;
if (e->empty())
{
if (err)
{
TString msg(80);
msg = TR("Manca un valore indispensabile per la ricerca.");
#ifdef DBG
msg << "\nChiave " << int(k) << " - Campo " << e->dlg();
const TFieldref* fr = e->field();
if (fr != NULL)
{
msg << " - " << fr->name();
if (fr->to() > 0)
msg << '[' << fr->from() << ',' << fr->to() << ']';
}
#endif
error_box(msg);
_mask->first_focus(-e->dlg());
}
return FALSE;
}
}
else
/* if (k == 1 && !onereq && !onefill && c.get().not_empty()) */
if (!onereq && !onefill && e->is_edit() && !e->empty())
onefill = TRUE;
}
if (k == 1 && !onereq && !onefill)
{
if (err)
error_box(TR("Manca un valore indispensabile per la ricerca"));
return FALSE;
}
return onefill || onereq;
}
bool TRelation_application::find(word k)
{
if (k == 0)
{
for (k = 1; k <= MAX_KEYS && !test_key(k, FALSE); k++);
if (k > MAX_KEYS)
return test_key(1, TRUE);
}
file().setkey(k);
file().zero();
for (TEditable_field* e = _mask->get_key_field(k, TRUE); e; e = _mask->get_key_field(k, FALSE))
{
if (e->shown()) // Ignora campi invisibili
e->autosave(*get_relation());
}
const int err = file().read(_isequal);
return err == NOERR;
}
bool TRelation_application::save(bool check_dirty)
{
static bool was_dirty = FALSE;
int pos = _mask->id2pos(DLG_SAVEREC);
if (pos < 0 || !_mask->fld(pos).active())
return TRUE;
int err = NOERR;
const int mode = _mask->mode();
if (check_dirty)
{
const int dirty = _mask->dirty();
if (mode == MODE_QUERY)
{
const bool cont = !dirty || yesno_box(TR("Annullare i dati inseriti?"));
return cont;
}
if (!dirty && !was_dirty)
{
if (mode == MODE_MOD)
{
get_relation()->restore_status();
get_relation()->lfile().reread(_unlock); // Unlock main file
}
return TRUE;
}
const KEY last = _mask->last_key();
const bool annulla = last == K_ESC || last == K_QUIT || last == K_F9;
const bool errore = dirty && _mask->field(dirty).dirty() > 1;
KEY k;
if (errore)
{
if (annulla)
{
TString w;
TMask_field& df = _mask->field(dirty);
if (df.is_edit())
w = ((TEdit_field&)df).get_warning();
if (w.blank())
{
w = df.prompt();
if (!w.blank())
{
w.trim();
w << ' ' << TR("inconsistente.");
}
}
if (w.blank())
w = TR("Campo inconsistente.");
w << '\n';
switch (last)
{
case K_ESC:
w << TR("Si desidera annullare?"); break;
case K_QUIT:
w << TR("Si desidera uscire?"); break;
default:
w << TR("Si desidera continuare?"); break;
}
k = yesno_box(w) ? K_NO : K_ESC;
if (k == K_ESC)
_mask->first_focus(-dirty);
}
else k = K_ESC;
}
else
k = yesnocancel_box("Si desidera registrare?");
if (k == K_ESC || k == K_NO)
{
if (mode == MODE_MOD)
{
get_relation()->restore_status();
get_relation()->lfile().reread(_unlock); // Unlock main file
}
was_dirty = FALSE;
return k == K_NO;
}
if (annulla)
{
if (!_mask->check_fields()) // Exit with ESC didn't check values
{
_mask->first_focus(-_mask->focus_field().dlg());
was_dirty = TRUE;
return FALSE;
}
}
}
was_dirty = FALSE;
TWait_cursor hourglass;
if (mode == MODE_INS)
{
bool changed = TRUE;
bool changed_key = FALSE;
while (changed)
{
err = write(*_mask);
if (err == _isreinsert)
{
changed = autonum(_mask, TRUE);
if (!changed)
{
_mask->disable_starting_check();
enable_query(); // Abilita chiave 1 per rinumerazione manuale
}
else
changed_key = TRUE;
}
else
changed = FALSE;
}
if (err == NOERR)
{
if (changed_key)
message_box(_renum_message);
get_relation()->save_status();
set_limits();
get_relation()->restore_status();
}
}
else
{
get_relation()->restore_status();
err = rewrite(*_mask);
}
switch(err)
{
case NOERR:
_recins = get_relation()->lfile().recno();
mask2mail(*_mask);
break;
case _isreinsert:
warning_box(TR("Esiste gia' un elemento con la stessa chiave"));
break;
default:
error_box(FR("Impossibile registrare i dati: errore %d"), err);
break;
}
return err == NOERR;
}
int TRelation_application::read(TMask& m)
{
const TRelation &r = *get_relation();
const int max = m.fields();
for (int i = 0; i < max; i++)
{
if (m.fld(i).is_sheet())
{
TSheet_field& f = (TSheet_field&)m.fld(i);
if (f.record() && !f.external_record())
f.record()->read(*f.putkey(r));
}
}
m.autoload(r);
return NOERR;
}
int TRelation_application::write(const TMask& m)
{
TRelation &r = *get_relation();
m.autosave(r);
// write relation and all independent sheets
int err=r.write();
const int max = m.fields();
for (int i = 0; i < max; i++)
{
if (m.fld(i).is_sheet())
{
TSheet_field& f = (TSheet_field& )m.fld(i);
if (f.record() && !f.external_record())
err|=f.record()->write(FALSE);
}
}
return err;
}
int TRelation_application::rewrite(const TMask& m)
{
int err;
TRelation& r = *get_relation();
m.autosave(r);
// rewrite relation and all independent sheets
r.rewrite();
err=r.status();
const int max = m.fields();
for (int i = 0; i < max; i++)
{
if (m.fld(i).is_sheet()) {
TSheet_field& f = (TSheet_field& )m.fld(i);
if (f.record()&& !f.external_record())
err|=f.record()->write(TRUE);
}
}
return err;
}
// @doc INTERNAL
// @mfunc Cancella il record corrente
//
// @rdesc Ritorna se il record e' stato eliminato
bool TRelation_application::relation_remove()
// @comm Se la maschera e' in MODE_MOD non e' possibile cancellare il record e viene
// emesso un <f CHECK> di errore.
{
CHECK(_mask->edit_mode(), "You can call remove in edit mode only");
TRelation& r = *get_relation();
r.restore_status();
if (protected_record(r))
return message_box(TR("Elemento non eliminabile"));
if (_curr_transaction == TRANSACTION_DELETE || yesno_box(TR("Confermare l'eliminazione")))
{
r.restore_status();
const bool ok = remove();
if (ok || is_transaction())
set_limits();
else
{
const int err = r.status();
if (err != NOERR) // Succede nei remove con richiesta di conferma all'utente
return error_box(FR("Errore di cancellazione %d"), err);
}
}
return TRUE;
}
bool TRelation_application::protected_record(TRelation &r)
{
if (user_can_write(&r))
return protected_record(r.curr());
return TRUE;
}
bool TRelation_application::remove()
{
int err = get_relation()->remove();
if (err == NOERR)
{
const int maxf = _mask->fields();
for (int i = 0; i < maxf; i++)
{
const TMask_field& mf = _mask->fld(i);
if (mf.is_sheet())
{
TSheet_field& f = (TSheet_field&)mf;
if (f.record()&& !f.external_record())
err |= f.record()->remove();
}
}
}
if (err == NOERR)
{
_mask->set_mode(NO_MODE);
mask2mail(*_mask);
}
return err == NOERR;
}
bool TRelation_application::firm_change_enabled() const
{
bool ok = TApplication::firm_change_enabled();
ok &= (_mask == NULL || _mask->query_mode()) && _lnflag == 0;
return ok;
}
void TRelation_application::main_loop()
{
KEY k;
do {
// ciclo delle transazioni
_recins = -1;
// imposta la maschera in query mode
query_mode();
_mask->open_modal();
// Provoca l'autopremimento per il messaggio di LINK
if (_lnflag)
{
if (_trans_counter < _ntransactions)
{
// la transazione <20> sul .ini : la imposta nelle variabili _curr_transaction...
if (load_transaction())
{
// la transazione necessita di autopremimento
_autodelete = _curr_transaction == TRANSACTION_DELETE;
if (_curr_transaction == TRANSACTION_INSERT )
_mask->send_key(K_CTRL+'N', 0);
else
_mask->send_key(K_AUTO_ENTER, 0);
}
}
else // la transazione non <20> sul .ini
{
_mask->send_key(K_AUTO_ENTER, 0);
}
}
if (is_transaction())
ini2query_mask();
do
{
const bool change = firm_change_enabled();
// Dis/abilita cambio ditta
enable_menu_item(M_FILE_NEW, change);
if (_mask->edit_mode())
{
if (_autodelete)
{
const int pos = _mask->id2pos(DLG_DELREC);
if (pos >= 0 && _mask->fld(pos).active())
_mask->send_key(K_CTRL+'E', 0);
else
error_box(TR("Elemento non eliminabile."));
_autodelete = FALSE;
}
}
k = _mask->run();
switch (k)
{
case K_ESC:
if (save(TRUE))
query_mode();
if (_lnflag)
k = K_QUIT;
break;
case K_QUIT:
if (!save(TRUE))
k = K_ENTER;
break;
case K_ENTER:
if (_lnflag && _curr_transaction != TRANSACTION_RUN)
{
if (find(1))
{
if (_curr_transaction == TRANSACTION_INSERT)
_curr_transaction = TRANSACTION_MODIFY;
modify_mode();
}
else
{
if (_curr_transaction == TRANSACTION_MODIFY)
_curr_transaction = TRANSACTION_INSERT;
insert_mode();
}
}
else
{
if (find(0))
modify_mode();
else
insert_mode();
}
if (_curr_trans_mode == TM_AUTOMATIC)
_mask->send_key(K_CTRL+'R', 0);
break;
case K_SAVE:
if (save(FALSE))
{
if (_autoins_caller.not_empty() || is_transaction())
{
k = K_QUIT;
}
else
{
if (save_and_new())
{
if (_mask->insert_mode())
insert_mode();
else
query_mode();
}
else
modify_mode();
}
}
break;
case K_INS:
if (_mask->query_mode() || save(TRUE))
{
const bool trovato = _mask->query_mode() && test_key(1, FALSE) && find(1);
if (trovato)
{
if (is_transaction())
_curr_transaction=TRANSACTION_MODIFY;
else
warning_box(TR("Elemento gia' presente"));
modify_mode();
}
else
insert_mode();
}
if (_curr_trans_mode == TM_AUTOMATIC)
_mask->send_key(K_CTRL+'R', 0);
break;
case K_DEL:
if (_mask->query_mode())
{
delete_mode();
}
else
{
if (relation_remove())
{
query_mode();
if (_autoins_caller.not_empty() || is_transaction())
{
if (_lnflag) _recins = 0;
k = K_QUIT;
}
}
}
break;
case K_F9:
if (_mask->query_mode() || save(TRUE))
search_mode();
break;
default:
if (save(TRUE))
{
setkey();
int err = ~NOERR;
switch (k)
{
case K_HOME:
err = file().readat(_first, _testandlock);
break;
case K_NEXT:
err = file().reread();
if (has_filtered_cursor())
{
TCursor& cur = get_filtered_cursor();
cur.curr() = file().curr();
cur.read();
++cur;
file().curr() = cur.curr();
err = get_relation()->read(_isequal, _testandlock);
}
else
err = file().next(_testandlock);
break;
case K_PREV:
file().reread();
if (has_filtered_cursor())
{
TCursor& cur = get_filtered_cursor();
cur.curr() = file().curr();
cur.read();
--cur;
file().curr() = cur.curr();
err = get_relation()->read(_isequal, _testandlock);
}
else
err = file().prev(_testandlock);
break;
case K_END:
err = file().readat(_last, _testandlock);
break;
default:
break;
}
if (err == NOERR || err == _islocked)
{
_navigating = true;
modify_mode();
_navigating = false;
}
else
query_mode();
}
break;
}
} while (k != K_QUIT);
if (_mask->is_open())
_mask->close_modal();
_mask->set_mode(NO_MODE);
if (autoins_caller().not_empty() && _recins >= 0)
{
NFCHECK("Obsolete LINK message calling convention");
TString16 num;
num.format("%ld", _recins);
TMessage msg(autoins_caller(), _lnflag ? MSG_LN : MSG_AI, num);
msg.send();
}
if (is_transaction())
{
TConfig ini(_trans_ini.row(_trans_counter), "Transaction");
ini.set("Record", _recins);
if (_recins >= 0)
{
ini.set("Result", "OK");
ini.set("Error", "0");
edit_mask2ini();
}
else
{
const int err = get_relation()->status();
ini.set("Result", err == NOERR ? "CANCEL" : "ERROR");
ini.set("Error", err);
}
}
_trans_counter++;
} while ( _trans_counter < _ntransactions);
}
bool TRelation_application::filter()
{
if (parse_command_line())
return TRUE;
TMailbox mail;
TMessage* msg = mail.next_s(MSG_FS);
if (msg)
{
_mask = get_mask(MODE_MOD);
TToken_string body(msg->body());
short id = body.get_int();
while (id > 0)
{
_search_id = id;
TEdit_field& f = (TEdit_field&)_mask->field(id);
TCursor* cur = f.browse()->cursor();
TRectype& rec = cur->curr();
rec.zero();
TString80 t;
const char* s;
while((s = body.get()) != NULL)
{
t = s;
const int u = t.find('=');
if (u < 0)
{
id = atoi(t);
break;
}
_fixed.add(t);
const short fid = atoi(t.left(u));
const TFieldref* campo = _mask->field(fid).field();
if (campo != NULL)
campo->write(t.mid(u+1), rec);
}
cur->setfilter("");
cur->setregion(rec, rec, 0x2);
if (s == NULL) id = 0;
}
}
mail.restart();
msg = mail.next_s(MSG_AI);
if (msg) _autoins_caller = msg->from();
mail.restart();
msg = mail.next_s(MSG_LN);
if (msg)
{
TToken_string body(msg->body());
const int key = body.get_int();
_autoins_caller = msg->from();
_lnflag = TRUE;
TString str, tmp;
const char* v = body.get();
for (int i = 0; v != NULL && i < _mask->fields(); i++)
{
TMask_field& f = _mask->fld(i);
if (f.active() && f.dlg() > 0 && f.in_key(key))
{
str = v;
tmp.format("%d=", f.dlg());
str.insert(tmp, 0);
_fixed.add(str);
v = body.get();
}
}
}
mail.restart();
msg = mail.next_s(MSG_ED);
if (msg)
{
TToken_string body(msg->body());
const int key = body.get_int();
_autoins_caller = msg->from();
_lnflag = 2;
TAssoc_array field_values;
const char * s;
TString t, v;
while((s = body.get()) != NULL)
{
t = s;
const int u = t.find('=');
CHECKS(u > 0, "Invalid edit message ", (const char *) body);
if (u > 0)
{
v = t.mid(u + 1);
t.cut(u);
field_values.add(t, v);
}
}
for (int i = 0; i < _mask->fields(); i++)
{
TMask_field& f = _mask->fld(i);
const TFieldref * field = f.field();
if (field && f.in_key(key))
{
TString16 field_name(field->name());
const int from = field->from();
const int to = field->to();
if (to >= 0)
field_name << "[" << (from + 1);
const TString * v = (const TString *) field_values.objptr(field_name);
TString val;
if (v == NULL && to >= 0)
{
v = (const TString *)field_values.objptr(field->name());
if (v)
val = v->sub(from, to);
}
else
if (v) val = *v;
if (v)
{
t.format("%d=", f.dlg());
val.insert(t, 0);
_fixed.add(val);
}
}
}
}
return TRUE;
}
void TRelation_application::set_link(TMask & m, const char * keyexpr)
{
CHECK(keyexpr != NULL, "Invalid expression");
TToken_string body(keyexpr);
const int key = body.get_int();
_lnflag = TRUE;
const char* v = body.get();
TString16 tmp;
const int max = m.fields();
for (int i = 0; i < max && v != NULL; i++)
{
TMask_field& f = m.fld(i);
if (f.active() && f.dlg() > 0 && f.in_key(key))
{
TString s(v);
tmp.format("%d=", f.dlg());
s.insert(tmp, 0);
_fixed.add(s);
v = body.get();
}
}
}
bool TRelation_application::parse_command_line()
{
_trans_ini.destroy();
_trans_counter=0;
_curr_transaction = "";
_curr_trans_mode = 'I';
TFilename ini;
for (int i = argc()-1; i > 0; i--)
{
ini = argv(i);
ini.upper();
if ((ini[0] == '-' || ini[0] == '/') && ini[1] == 'I')
{
ini.ltrim(2);
CHECK(!ini.blank(),"Manca l'indicazione della transazione. Il nome va indicato di seguito al -i, senza interporre spaziatura.");
if (ini.find('*')>=0)
{
// metachars:
list_files(ini, _trans_ini);
}
else
_trans_ini.add(ini);
break;
}
}
_ntransactions= _trans_ini.items();
_lnflag = _ntransactions>0;
return _lnflag != 0;
}
// il valore di ritorno indica se attivare l'"automagia" (spedizione dei tasti e
// precaricamento della maschera)
bool TRelation_application::load_transaction()
{
bool retv = FALSE;
if (_trans_counter < _ntransactions)
{
TConfig cnf(_trans_ini.row(_trans_counter), "Transaction");
_curr_transaction = cnf.get("Action");
_curr_transaction.upper();
_curr_trans_mode = toupper(cnf.get("Mode")[0]);
_curr_trans_from = cnf.get("From");
const long firm = cnf.get_long("Firm");
if (firm > 0)
{
bool ok = set_firm(firm);
if (ok)
_mask->on_firm_change();
else
error_box("La ditta %ld non esiste", firm);
}
if (_curr_transaction == TRANSACTION_RUN)
retv= FALSE; // Ho gia' finito qui: basta il cambio ditta
else
retv = TRUE; // Attiva automagia
}
return retv;
}
void TRelation_application::ini2query_mask()
{
if (is_transaction())
{
TString8 n; n.format("%d", get_relation()->lfile().num());
TConfig ini(_trans_ini.row(_trans_counter), n);
ini2mask(ini, *_mask, TRUE);
}
}
void TRelation_application::ini2insert_mask()
{
if (is_transaction())
{
TString8 n; n.format("%d", get_relation()->lfile().num());
TConfig ini(_trans_ini.row(_trans_counter), n);
ini2mask(ini, *_mask, FALSE);
}
}
void TRelation_application::ini2mask(TConfig& ini, TMask& m, bool query)
{
const TString16 defpar = ini.get_paragraph();
for (int f = m.fields()-1; f >= 0; f--)
{
TMask_field& campo = m.fld(f);
const TFieldref* fref = campo.field();
if (fref)
{
if (!query || campo.in_key(0))
{
const TString& str = fref->read(ini, defpar);
if (str.not_empty())
campo.set(str);
}
}
else
{
if (!query && campo.is_sheet())
{
TSheet_field &sheet=(TSheet_field &)campo;
ini2sheet(ini, sheet);
}
}
}
ini.set_paragraph(defpar);
}
void TRelation_application::edit_mask2ini()
{
if (_trans_ini.row(_trans_counter).not_empty())
{
TString8 head; head.format("%d", get_relation()->lfile().num());
TConfig ini(_trans_ini.row(_trans_counter), head);
mask2ini(*_mask, ini);
}
}
void TRelation_application::ini2sheet(TConfig& ini,TSheet_field &sheet)
{
if (sheet.record() != NULL)
{
const int lognum = sheet.record()->logic_num();
const TMask& sm = sheet.sheet_mask();
// scrive le righe nello sheet associato
TString16 defpar;
for (int r = 1; ;r++)
{
defpar.format("%d,%d", lognum, r);
if (ini.set_paragraph(defpar))
{
TToken_string& row = sheet.row(r-1);
for (int sf = sm.fields()-1; sf >= 0; sf--)
{
TMask_field& campo = sm.fld(sf);
const TFieldref* fref = campo.field();
if (fref)
{
const TString& str = fref->read(ini, defpar);
row.add(str, sheet.cid2index(campo.dlg()));
}
}
sheet.check_row(r-1);
}
else
break;
}
}
}
void TRelation_application::sheet2ini(TSheet_field &sheet,TConfig& ini)
{
if (sheet.record() != NULL)
{
const int lognum = sheet.record()->logic_num();
const TMask& sm = sheet.sheet_mask();
// scrive le righe degli sheet associati
TString16 defpar;
TString str;
for (int r = 1 ; r <= sheet.items(); r++)
{
defpar.format("%d,%d", lognum, r);
TMask_field* fkey;
sheet.restart_key();
while (fkey = sheet.get_key(str))
{
ini.set(str, fkey->get(), defpar);
}
TToken_string& row = sheet.row(r-1);
const char* value;
int i;
for (i = 0, value = row.get(0); value; i++, value = row.get())
{
const TMask_field& campo = sm.field(FIRST_FIELD+i);
const TFieldref* fr = campo.field();
if (fr)
{
if (value == NULL || *value == '\0')
value = " ";
// ini.set(fr->name(), value, defpar);
fr->write(ini, defpar, value);
}
}
}
for (r = sheet.items()+1; ; r++)
{
defpar.format("%d,%d", lognum, r);
if (ini.set_paragraph(defpar))
ini.remove_all();
else
break;
}
}
}
void TRelation_application::mask2ini(const TMask& m, TConfig& ini)
{
ini.set("Firm", get_firm(), "Transaction");
int year, release, tag, patch;
if (get_version_info(year, release, tag, patch))
{
TString80 ver;
ver.format("%d %d.%d-%d", year, release, tag, patch);
ini.set("Version", ver);
}
const TLocalisamfile& lfile = get_relation()->lfile();
TString16 defpar;
defpar.format("%d", lfile.num());
ini.set_paragraph(defpar);
switch (lfile.num())
{
case LF_TAB:
case LF_TABCOM:
case LF_TABGEN:
{
const TString& tabname = lfile.curr().get("COD");
ini.set("COD", tabname, defpar);
}
break;
}
for (int f = 0; f < m.fields(); f++)
{
TMask_field& campo = m.fld(f);
if (campo.shown())
{
const TFieldref* fr = campo.field();
if (fr)
{
if (campo.empty())
fr->write(ini, defpar, " ");
else
{
if (campo.class_id() == CLASS_DATE_FIELD && campo.right_justified())
{
const TDate d = campo.get();
fr->write(ini, defpar, d.string(ANSI));
}
else
fr->write(ini, defpar, campo.get());
}
}
else
if (campo.is_sheet())
{
TSheet_field &sheet=(TSheet_field &)campo;
sheet2ini(sheet,ini); // It's virtual
}
}
}
ini.set_paragraph(defpar); // Reimposta paragrafo standard
}
bool TRelation_application::mask2mail(const TMask& m)
{
TWait_cursor hourglass;
bool ok = _curr_trans_from.empty() && ::can_dispatch_transaction(get_relation()->curr());
if (ok)
{
TFilename ininame; ininame.temp();
if (ok) // Test qualunque per usare {}
{
TConfig ini(ininame, "Transaction");
const char* action = "";
char mode[2] = { TM_AUTOMATIC, '\0' };
switch (m.mode())
{
case NO_MODE:
action = TRANSACTION_DELETE;
break;
case MODE_MOD:
action = TRANSACTION_MODIFY;
break;
default:
action = TRANSACTION_INSERT;
break;
}
ini.set("Action", action);
ini.set("Mode", mode);
mask2ini(m, ini);
}
ok = ::dispatch_transaction(get_relation()->curr(), ininame);
::remove(ininame);
}
return ok;
}