#include #include #include #include #include #define STRICT #define XVT_INCL_NATIVE #include #if XVT_OS == XVT_OS_WIN #include #endif /////////////////////////////////////////////////////////// // TRelation_application /////////////////////////////////////////////////////////// TRelation_application::TRelation_application() : _mask(NULL), _search_id(-1), _lnflag(FALSE) { } TRelation_application::~TRelation_application() { } void TRelation_application::setkey() { if (has_filtered_cursor()) { TEdit_field& f = get_search_field(); TCursor* cur = f.browse()->cursor(); cur->setkey(); return; } file().setkey(1); } // @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(); f.browse()->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()) _first = 1; else { file().first(); _first = file().recno(); } } if (what & 0x2) { if (file().empty()) _last = 1; else { file().last(); _last = file().recno(); } } } bool TRelation_application::create() { TApplication::create(); const bool ok = user_create(); if (ok) { write_enable(); _mask = get_mask(MODE_QUERY); filter(); set_limits(); dispatch_e_menu(BAR_ITEM(1)); } return ok; } bool TRelation_application::menu(MENU_TAG m) { if (m == BAR_ITEM(1)) return main_loop(); return TRUE; } bool TRelation_application::destroy() { user_destroy(); return TApplication::destroy(); } void TRelation_application::set_fixed() { TString s; for (const char* f = _fixed.get(0); f && *f; f = _fixed.get()) { s = f; const int u = s.find('='); const int id = atoi(s.left(u)); const char* val = s.mid(u+1); if (*val) _mask->set(id, val); _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(bool all) { const int mode = _mask->mode(); int pos; if (all) { pos = _mask->id2pos(DLG_SAVEREC); if (pos >= 0) _mask->fld(pos).enable(mode != MODE_QUERY); pos = _mask->id2pos(DLG_DELREC); if (pos >= 0) { bool enabdel = mode == MODE_MOD; if (enabdel) { TRelation& r = *get_relation(); const TRecnotype oldpos = r.lfile().recno(); enabdel = !protected_record(r.curr()); if (r.lfile().recno() != oldpos) r.lfile().readat(oldpos); } _mask->fld(pos).enable(enabdel); } } enable_query(); } 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); if (mode == _mode) { set_toolbar(FALSE); // Fast buttons update } else { set_toolbar(TRUE); // Full buttons update _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 | Il campo chiave non e' vuoto // @flag FALSE | Non e' riuscii ad autonumerare il campo chiave bool TRelation_application::autonum( TMask* m, // @parm Maschera a cui applicare l'autonumerazione bool rec) // @parm Indica se registrare il record corrente { TToken_string k(get_next_key()); 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, "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("Il documento 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(); if (pre_ins) { set_mode(NO_MODE); init_query_insert_mode(*_mask); } else { set_mode(MODE_QUERY); init_query_mode(*_mask); } } void TRelation_application::insert_mode() { if (_mask->query_mode()) { if (test_key(1, FALSE) == FALSE) { if (!autonum(_mask, FALSE)) { query_insert_mode(); 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 { if (!autonum(_mask, FALSE)) { query_insert_mode(); return; } } set_mode(MODE_INS); get_relation()->zero(); // Azzera tutta la relazione! init_insert_mode(*_mask); } bool TRelation_application::modify_mode() { int err = get_relation()->read(_isequal, _testandlock); if (err != NOERR) { if (err == _islocked) message_box("I dati sono gia' in uso ad 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(); set_mode(MODE_MOD); err = read(*_mask); if (err != NOERR) { query_mode(); return FALSE; } get_relation()->save_status(); init_modify_mode(*_mask); return TRUE; } TEdit_field& TRelation_application::get_search_field() const { short id = _search_id; if (id <= 0) { const int max = _mask->fields(); for (int i = 0; i < max; i++) { const TMask_field& f = _mask->fld(i); if (f.in_key(1) && f.required()) { 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(); } TMask_field* dopo = &_mask->focus_field(); prima = (dopo == prima || !dopo->is_edit()) ? NULL : (TEdit_field*)dopo; } return FALSE; } // @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( byte 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()) { onereq = TRUE; if (e->empty()) { if (err) { error_box("Manca un valore indispensabile per la ricerca"); _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("Manca un valore indispensabile per la ricerca"); return FALSE; } return onefill || onereq; } bool TRelation_application::find(byte 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 err = NOERR; const int mode = _mask->mode(); if (check_dirty) { const int dirty = _mask->dirty(); const char* ms = (mode == MODE_MOD) ? "le modifiche" : "i dati inseriti"; if (mode == MODE_QUERY) { const bool cont = !dirty || yesno_box("Annullare %s?", ms); 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() > TRUE; KEY k; if (errore) { if (annulla) { TString w(80); if (_mask->field(dirty).is_edit()) w = _mask->efield(dirty).get_warning(); if (w.empty()) w = "Campo inconsistente"; w << ": si desidera "; switch (last) { case K_ESC: w << "annullare?"; break; case K_QUIT: w << "uscire?"; break; default: w << "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("Registrare %s?", ms); 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; begin_wait(); 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); } end_wait(); switch(err) { case NOERR: _recins = get_relation()->lfile().recno(); break; case _isreinsert: warning_box("Esiste gia' un documento con la stessa chiave"); break; default: error_box("Impossibile registrare i dati: errore %d", err); break; } return err == NOERR; } int TRelation_application::read(TMask& m) { const TRelation &r = *get_relation(); m.autoload(r); return NOERR; } int TRelation_application::write(const TMask& m) { TRelation &r = *get_relation(); m.autosave(r); r.write(); return r.status(); } int TRelation_application::rewrite(const TMask& m) { TRelation& r = *get_relation(); m.autosave(r); r.rewrite(); return r.status(); } // @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 di errore. { CHECK(_mask->mode() == MODE_MOD, "You can call remove in MODE_MOD only"); TRelation *r = get_relation(); r->restore_status(); if (protected_record(r->curr())) return message_box("Registrazione non eliminabile"); if (yesno_box("Confermare l'eliminazione")) { r->restore_status(); const bool ok = remove(); if (ok) set_limits(); else return error_box("Errore di cancellazione %d", r->status()); } return TRUE; } bool TRelation_application::remove() { const int err = get_relation()->remove(); 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; } bool TRelation_application::main_loop() { _recins = -1; query_mode(); _mask->open_modal(); KEY k; // Provoca l'autopremimento per il messaggio di LINK if (_lnflag) _mask->send_key(K_AUTO_ENTER, 0); do { const bool change = firm_change_enabled(); // Dis/abilita cambio ditta enable_menu_item(M_FILE_NEW, change); // Dis/abilita cambio parametri enable_menu_item(M_FILE_REVERT, change); 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 (find(0)) modify_mode(); else insert_mode(); break; case K_SAVE: if (save(FALSE)) { if (_autoins_caller.not_empty()) { 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) { modify_mode(); warning_box("Documento gia' presente"); } else insert_mode(); } break; case K_DEL: if (relation_remove()) query_mode(); if (_autoins_caller.not_empty()) { 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(); err = file().next(_testandlock); break; case K_PREV: err = file().reread(); err = file().prev(_testandlock); break; case K_END: err = file().readat(_last, _testandlock); break; default: break; } if (err == NOERR || err == _islocked) modify_mode(); 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) { TMessage msg(autoins_caller(), _lnflag ? MSG_LN : MSG_AI, format("%ld", _recins)); msg.send(); } return k != K_QUIT; } bool TRelation_application::filter() { 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); 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; const char* v = body.get(); TString80 s; 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)) { s = v; _fixed.add(format("%d=%s", f.dlg(), (const char*)s)); 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 = TRUE; TAssoc_array field_values; const char * s; TString80 t; while((s = body.get()) != NULL) { t = s; const int u = t.find('='); CHECKS(u > 0, "Invalid edit message ", (const char *) body); if (u > 0) { const TString 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); if (v) _fixed.add(format("%d=%s", f.dlg(), (const char*)*v)); } } } 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(); 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)) { const TString s(v); _fixed.add(format("%d=%s", f.dlg(), (const char*) s)); v = body.get(); } } }