// $Id: relapp.cpp,v 1.63 1995-07-19 09:57:17 guy Exp $ #include #include #include #include #include #include #if XVT_OS == XVT_OS_WIN #include #endif /////////////////////////////////////////////////////////// // Array delle chiavi della maschera di ricerca /////////////////////////////////////////////////////////// class TChiave : public TObject { enum { MAX = 16 }; int _pos[MAX]; byte _num; public: void add(int p); int pos(byte n) const { return (n >= _num) ? -1 : _pos[n]; } byte items() const { return _num; } TChiave() : _num(0) {} virtual ~TChiave() {} }; void TChiave::add(int p) { CHECKD(_num < MAX, "Too many fields in key : field n.", p); _pos[_num++] = p; } class TKey_array : public TArray { const TMask* _mask; public: TKey_array(const TMask* m); virtual ~TKey_array() {} TChiave& key(byte k); }; TChiave& TKey_array::key(byte k) { k--; TChiave* chiave = (TChiave*)objptr(k); if (chiave == NULL) { chiave = new TChiave; add(chiave, k); } return *chiave; } TKey_array::TKey_array(const TMask* m) : _mask(m) { const byte keys = m->num_keys(); for (int f = 0; f < m->fields(); f++) { TMask_field& c = m->fld(f); if (c.in_key(0)) for (byte k = 1; k <= keys; k++) if (c.in_key(k)) key(k).add(f); } } /////////////////////////////////////////////////////////// // TRelation_application /////////////////////////////////////////////////////////// TRelation_application::TRelation_application() : _mask(NULL), _maskeys(NULL), _search_id(-1), _lnflag(FALSE) {} TRelation_application::~TRelation_application() { if (_maskeys) delete _maskeys; } void TRelation_application::setkey() { if (has_filtered_cursor()) { TEdit_field* f = (TEdit_field*) get_search_field(); TCursor* cur = f->browse()->cursor(); cur->setkey(); return; } file().setkey(1); } // what - meaning // 0 - nop // 1 - first // 2 - last // 3 - both void TRelation_application::set_limits(byte what) { if (has_filtered_cursor()) { TEdit_field* f = (TEdit_field*)get_search_field(); CHECK(f, "Manca il campo di ricerca"); TBrowse* b = f->browse(); TCursor* cur = b ? 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(); _maskeys = new TKey_array(_mask); 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() { if (_maskeys) delete _maskeys; user_destroy(); return TApplication::destroy(); } void TRelation_application::set_fixed() { TString256 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 (byte k = 1; k <= _maskeys->items(); k++) { const TChiave& chiave = _maskeys->key(k); for (int i = 0; i < chiave.items(); i++) { const int num = chiave.pos(i); TMask_field& c = _mask->fld(num); if (c.enabled_default()) { if (k == 1) c.enable(keyon); if (c.has_query()) ((TEdit_field&)c).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; } bool TRelation_application::autonum(TMask* m, bool rec) { 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.get().empty()) f.set(val); if (rec) 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(); } void TRelation_application::query_mode(bool pre_ins) { 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(); if (_maskeys) delete _maskeys; _maskeys = new TKey_array(_mask); set_limits(); } _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(); delete _maskeys; _maskeys = new TKey_array(_mask); } } 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(); delete _maskeys; _maskeys = new TKey_array(_mask); } set_mode(MODE_MOD); err = read(*_mask); if (err != NOERR) { query_mode(); return FALSE; } get_relation()->save_status(); init_modify_mode(*_mask); return TRUE; } TMask_field* TRelation_application::get_search_field() const { if (_search_id > 0) return &_mask->field(_search_id); const TChiave& k = _maskeys->key(1); for (int i = 0; i < k.items(); i++) { TMask_field* f = &_mask->fld(k.pos(i)); if (f->required()) return f; } return NULL; } bool TRelation_application::search_mode() { if (_mask->mode() != MODE_QUERY) query_mode(); TMask_field* prima = get_search_field(); while (prima) { if (prima->on_key(K_F9)) { if (find(1)) return modify_mode(); } TMask_field* dopo = &_mask->fld(_mask->focus_field()); prima = (dopo == prima) ? NULL : dopo; } return FALSE; } bool TRelation_application::test_key(byte k, bool err) { const TChiave& chiave = _maskeys->key(k); bool onereq = FALSE, onefill = FALSE; for (int i = 0; i < chiave.items(); i++) { const int num = chiave.pos(i); TMask_field& c = _mask->fld(num); if (c.required()) { onereq = TRUE; if (c.get().empty()) { if (err) { _mask->first_focus(-c.dlg()); error_box("Manca un valore indispensabile per la ricerca"); } return FALSE; } } else /* if (k == 1 && !onereq && !onefill && c.get().not_empty()) */ if (!onereq && !onefill && c.is_edit() && c.get().not_empty()) onefill = TRUE; } if (k == 1 && !onereq && !onefill) { if (err) error_box("Manca un valore indispensabile per la ricerca"); return FALSE; } return onefill || onereq; } // Guy: doesn't change fields bool TRelation_application::find(byte k) { const byte numkeys = _maskeys->items(); if (k == 0) { for (k = 1; k <= numkeys && !test_key(k, FALSE); k++); if (k > numkeys) return test_key(1, TRUE); } file().setkey(k); file().zero(); const TChiave& chiave = _maskeys->key(k); for (int i = 0; i < chiave.items(); i++) { const TMask_field& c = _mask->fld(chiave.pos(i)); c.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(_mask->field(dirty).warning()); if (w.empty()) w = "Campo inconsistente"; k = yesno_box("%s: annullare?", (const char *) w) ? K_ESC : K_NO; if (k == K_NO) _mask->first_focus(-_mask->field(dirty).dlg()); } 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_ESC; } if (annulla) { if (!_mask->check_fields()) // Exit with ESC didn't check values { _mask->first_focus(-_mask->fld(_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(); } bool TRelation_application::relation_remove() { 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(); if (ok) ok = curr_mask().query_mode(); 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)) 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 (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(); } } } 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(); } } }