#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../sqlite/sqlite3.h" #include "ba8200.h" /////////////////////////////////////////////////////////// // TRelation_node & TRelation_tree /////////////////////////////////////////////////////////// // Informazioni relative ad un nodo della relazione: class TRelation_node : public TObject { const TRelation_node* _father; // Puntatore all'eventuale padre int _logicnum; // Numero logico TString16 _name, _alias; // Nome ed Alias TString_array _join_fields; // Elenco dei campi di join public: bool description(TString& str) const; int num() const { return _logicnum; } const TString& name() const { return _name; } const TString& alias() const { return _alias; } const TString& id() const { return _alias.not_empty() ? _alias : _name; } const TRelation_node* father() const { return _father; } void set_num(int num); void set_alias(const char* alias) { _alias = alias; } TString_array& join() { return _join_fields; } TRelation_node(const TRelation_node* father, int ln, const char* alias); virtual ~TRelation_node() {} }; bool TRelation_node::description(TString& str) const { const bool ok = _logicnum >= LF_USER; if (ok) { const FileDes& fd = prefix().get_filedes(_logicnum); str = _name; if (_alias.not_empty()) str << " ALIAS " << _alias; str << " (" << fd.Des << ')'; } return ok; } void TRelation_node::set_num(int num) { _logicnum = num; const FileDes& fd = prefix().get_filedes(_logicnum); const TFilename name = fd.SysName; _name = name.name(); _name.upper(); } TRelation_node::TRelation_node(const TRelation_node* father, int ln, const char* alias) : _father(father) { set_num(ln); set_alias(alias); } // Albero di una relazione class TRelation_tree : public TObject_tree { protected: virtual bool get_description(TString& str) const; public: bool find_id(const TString& id); }; bool TRelation_tree::get_description(TString& str) const { const TRelation_node* rn = (const TRelation_node*)curr_node(); if (rn != NULL) return rn->description(str); return false; } static bool find_id_callback(TTree& tree, void* jolly, word flags) { const TRelation_node* n = (const TRelation_node*)tree.curr_node(); if (n != NULL) { const TString& id = *(const TString*)jolly; return n->id() == id; } return false; } bool TRelation_tree::find_id(const TString& id) { bool ok = goto_root(); if (ok) { scan_depth_first(find_id_callback, (void*)&id); const TRelation_node* n = (const TRelation_node*)curr_node(); ok = n != NULL && n->id() == id; } return ok; } /////////////////////////////////////////////////////////// // TTable_mask /////////////////////////////////////////////////////////// // Maschera per inserire/editare un nodo dell'albero class TTable_mask : public TAutomask { protected: virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); protected: int father_logicnum() const; const char* find_linked_field(int logicnum, const RecFieldDes& fd) const; public: int son_logicnum() const; void mask2node(TRelation_node& node); TTable_mask(); ~TTable_mask(); }; int TTable_mask::father_logicnum() const { return table2logic(get(F_FATHER)); } int TTable_mask::son_logicnum() const { return table2logic(get(F_SON)); } // Dato il numero logico di una tabella ed un campo (di un'altra tabella) // cerca il campo piu' simile all'interno del tracciato record const char* TTable_mask::find_linked_field(int logicnum, const RecFieldDes& fd) const { const RecDes& rd = prefix().get_recdes(logicnum); int nBest = -1; double dBest = 0.0; for (int i = 0; i < rd.NFields; i++) { const RecFieldDes& field = rd.Fd[i]; if (field.TypeF == fd.TypeF && field.Len == fd.Len) { const double fuzzy = xvt_str_fuzzy_compare(field.Name, fd.Name); if (fuzzy > dBest) { nBest = i; dBest = fuzzy; if (dBest == 1.0) break; } } } return nBest >= 0 ? rd.Fd[nBest].Name : ""; } bool TTable_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_SON: if (e == fe_modify) { const int logicnum = son_logicnum(); const int fathernum = father_logicnum(); TSheet_field& sheet = sfield(F_SHEET); sheet.destroy(); if (logicnum >= LF_USER && fathernum >= LF_USER) { const RecDes& rd = prefix().get_recdes(logicnum); const KeyDes& kd = rd.Ky[0]; TToken_string tok; for (int j = 0; j < kd.NkFields; j++) { const int n = kd.FieldSeq[j] % MaxFields; tok = rd.Fd[n].Name; tok.add(find_linked_field(fathernum, rd.Fd[n])); sheet.rows_array().add(tok); } } sheet.force_update(); } break; case F_SHEET: if (e == se_query_add || e == se_query_del) return false; break; case F_FLD_TO: if (e == fe_button) { const int logicnum = son_logicnum(); if (logicnum >= LF_USER) { TArray_sheet sheet(-1, -1, 24, 20, TR("Selezione"), HR("Nome@12|Lunghezza")); const RecDes& rd = prefix().get_recdes(logicnum); TToken_string row; for (int i = 0; i < rd.NFields; i++) { row = rd.Fd[i].Name; row.add(rd.Fd[i].Len); sheet.add(row); } sheet.rows_array().sort(); if (sheet.run() == K_ENTER) o.set(sheet.row(-1).get(0)); } } break; default: break; } return true; } // Trasferisce le informazioni dalla maschera ad un nodo della tabella void TTable_mask::mask2node(TRelation_node& son) { son.set_num(son_logicnum()); son.set_alias(get(F_SON_ALIAS)); if (son.father() != NULL) { TString_array& join = son.join(); join.destroy(); TSheet_field& sheet = sfield(F_SHEET); FOR_EACH_SHEET_ROW(sheet, i, row) { TToken_string* newrow = new TToken_string(50, '='); newrow->add(row->get(0)); newrow->add(row->get()); join.add(newrow); } } } TTable_mask::TTable_mask() : TAutomask("ba8200b") { FileDes dir; CGetFile(LF_DIR, &dir, _nolock, NORDIR); const int nfiles = (int)dir.EOD; TList_sheet& sht = *efield(F_SON).sheet(); TToken_string tt(80); TFilename n; for (int logic = LF_USER; logic < nfiles; logic++) { const FileDes& fd = prefix().get_filedes(logic); n = fd.SysName; n = n.name(); n.upper(); tt = n; tt.add(logic); tt.add(fd.Des); sht.rows_array().add(tt); } sht.rows_array().sort(); } TTable_mask::~TTable_mask() { } /////////////////////////////////////////////////////////// // TQuery_mask /////////////////////////////////////////////////////////// // Maschera principale della costruzione della query class TQuery_mask : public TAutomask { TRelation_tree _tree; // Albero della relazione int _curr_num; // Numero del file corrente bool _dragster; // Operazione di trascinamento in corso TFilename _curr_query;// Nome completo della query in editazione bool _is_dirty; // Maschera cambiata dall'ultimo caricamento bool _sql_dirty; // Query modificata manualmente protected: virtual void handler(WINDOW win, EVENT* ep); virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); protected: bool add_file_to_tree(); void add_field_to_sheet(const char* fld, bool update); void add_field_to_sheet(bool update = true); void add_asterisk_to_sheet(); bool ask_vars(const char* maskname, TRecordset& recset) const; TRecordset* new_recordset() const; bool edit_file_in_tree(); static int sort_fields(const TObject** r1, const TObject** r2); void fill_fields(); void enable_sql_button(); void enable_field_buttons(); void move_curr_field(int dir); void tree2sql(TString& from, TString& where); void tree2isam(TString_array& a); void sheet2sql(); void sheet2isam(); bool select_query(); bool load_tables_tree(TXmlItem& tables); bool load_fields_sheet(TXmlItem& fields); void global_reset(); bool save_tables_tree(TXmlItem& xml); bool save_fields_sheet(TXmlItem& xml); public: void edit_query(); void save_as(TRecordsetExportFormat fmt); bool load_query(); bool save_query(); bool save_if_needed(); bool delete_query(); bool get_qry_path(TFilename& path) const; TRelation_node* curr_node(); TQuery_mask(); }; // Nodo corrente dell'albero TRelation_node* TQuery_mask::curr_node() { TTree_field& tf = tfield(F_TABLES); tf.goto_selected(); TRelation_node* n = (TRelation_node*)_tree.curr_node(); return n; } // Aggiunge interattivamente un nodo all'albero bool TQuery_mask::add_file_to_tree() { TTable_mask msk; const TRelation_node* father = curr_node(); TString fatherid; if (father != NULL) { _tree.curr_id(fatherid); msk.set(F_FATHER, father->name()); msk.set(F_FATHER_ALIAS, father->alias()); } msk.disable(DLG_DELREC); bool ok = msk.run() == K_ENTER; if (ok) { TRelation_node* son = new TRelation_node(father, msk.son_logicnum(), msk.get(F_SON_ALIAS)); msk.mask2node(*son); if (fatherid.not_empty()) _tree.goto_node(fatherid); _tree.add_son(son); _tree.expand_all(); tfield(F_TABLES).win().force_update(); _curr_num = son->num(); fill_fields(); } return ok; } // Modifica interattivamente il nodo corrente dell'albero bool TQuery_mask::edit_file_in_tree() { TRelation_node* son = curr_node(); if (son == NULL) return false; TString sonid; _tree.curr_id(sonid); const TRelation_node* father = NULL; if (_tree.goto_father()) father = (const TRelation_node*)_tree.curr_node(); TTable_mask msk; if (father != NULL) { msk.set(F_FATHER, father->name()); msk.set(F_FATHER_ALIAS, father->alias()); TString_array& arr = son->join(); FOR_EACH_ARRAY_ROW(arr, i, row) { TToken_string& newrow = msk.sfield(F_SHEET).row(-1); FOR_EACH_TOKEN((*row), tok) { TToken_string str(tok, '.'); str.strip_spaces(); if (str.items() > 1) newrow.add(str.get(1)); else newrow.add(str); } } } msk.set(F_SON, son->name()); msk.set(F_SON_ALIAS, son->alias()); bool update = true; switch (msk.run()) { case K_ENTER: msk.mask2node(*son); break; case K_DEL: if (curr_node() != NULL) { if (yesno_box(TR("Eliminare anche le colonne associate della query?"))) { TSheet_field& sheet = sfield(F_SHEET); FOR_EACH_SHEET_ROW_BACK(sheet, r, row) { const TString& id = row->get(0); if (id == son->id()) sheet.destroy(r); } } _tree.goto_node(sonid); _tree.kill_node(); _tree.goto_root(); } break; default: update = false; break; } if (update) { TTree_field& tf = tfield(F_TABLES); tf.win().force_update(); _curr_num = 0; if (tf.select_current()) { const TRelation_node* curr = curr_node(); if (curr != NULL) _curr_num = curr->num(); } fill_fields(); } return update; // ???? } // Ordina per importanza i campi di un tracciato record int TQuery_mask::sort_fields(const TObject** r1, const TObject** r2) { TToken_string& s1 = (TToken_string&)**r1; TToken_string& s2 = (TToken_string&)**r2; const int w1 = s1.get_int(4); const int w2 = s2.get_int(4); int cmp = w2-w1; if (cmp == 0) cmp = strcmp(s1.get(0), s2.get(0)); return cmp; } // Riempie la lista dei campi del file corrente void TQuery_mask::fill_fields() { TSheet_field& sht = sfield(F_FIELDS); sht.destroy(); if (_curr_num >= LF_USER) { TRelation rel(_curr_num); TRelation_description reldes(rel); const RecDes& rd = prefix().get_recdes(_curr_num); for (int i = 0; i < rd.NFields; i++) { TToken_string& row = sht.row(-1); const RecFieldDes& fd = rd.Fd[i]; row = fd.Name; row.add(fd.Len); int weight = 0; for (int k = 0; k < rd.NKeys; k++) { const KeyDes& kd = rd.Ky[k]; for (int j = 0; j < kd.NkFields; j++) { const int n = kd.FieldSeq[j] % MaxFields; if (n == i) { if (weight == 0) { weight = (rd.NKeys-k)*100 + kd.NkFields-j; row.add(k+1); } else row << ' ' << (k+1); } } } row.add(reldes.get_field_description(fd.Name), 3); row.add(weight, 4); } sht.rows_array().TArray::sort(sort_fields); } sht.force_update(); enable_field_buttons(); } // Aggiunge un cmapo allo spreadsheet F_SHEET void TQuery_mask::add_field_to_sheet(const char* fld, bool update) { const TRelation_node* n = curr_node(); if (n != NULL) { TSheet_field& ff = sfield(F_SHEET); TToken_string& row = ff.row(-1); row = n->id(); row.add(fld); _is_dirty = true; if (update) { ff.force_update(); enable_sql_button(); } } } // Aggiunge il campo selezionato nello spredasheet F_FIELDS allo spreadsheet F_SHEET void TQuery_mask::add_field_to_sheet(bool update) { TSheet_field& sf = sfield(F_FIELDS); const int r = sf.selected(); if (r >= 0) { TToken_string& rowsel = sf.row(r); add_field_to_sheet(rowsel.get(0), update); } } // Aggiunge tutte le colonne allo sheet void TQuery_mask::add_asterisk_to_sheet() { const TRelation_node* n = curr_node(); if (n != NULL) { if (yesno_box("Si desisdera aggiungere tutti i campi singolarmente?")) { TSheet_field& sf = sfield(F_FIELDS); const int nLast = sf.items()-1; for (int i = 0; i <= nLast; i++) { sf.select(i); add_field_to_sheet(i == nLast); } sf.select(0); } else add_field_to_sheet("*", true); } } // Decide se attivare o meno il bottone SQL void TQuery_mask::enable_sql_button() { const bool yes = sfield(F_SHEET).items() > 0; enable(F_GENSQL, yes); } // Decide se attivare o meno i bottoni di aggiunta campi void TQuery_mask::enable_field_buttons() { const TSheet_field& sf = sfield(F_FIELDS); const bool ok = sf.items() > 0; enable(-1, ok); } void TQuery_mask::move_curr_field(int dir) { TSheet_field& s = sfield(F_SHEET); const int sel = s.selected(); if (sel >= 0 && sel < s.items()) { const int des = sel+dir; if (des >= 0 && des < s.items()) { s.rows_array().swap(sel, des); s.select(des); s.force_update(); } } } static bool sql_tree_handler(TTree& tree, void* jolly, word flags) { TRelation_node* rn = (TRelation_node*)tree.curr_node(); TPointer_array& arr = *(TPointer_array*)jolly; TToken_string& from = *(TToken_string*)arr.objptr(0); TString& where = *(TString*)arr.objptr(1); TString_array& join = rn->join(); TString str; if (from.get_pos(rn->id()) < 0) { from.add(rn->name()); if (rn->alias().not_empty()) from << " AS " << rn->alias(); FOR_EACH_ARRAY_ROW(join, i, row) { if (where.find(*row) < 0) { if (where.not_empty()) where << "AND"; where << '(' << rn->id() << '.' << row->get(0) << '='; str = row->get(); if (isalpha(str[0])) where << rn->father()->id() << '.'; where << str << ')'; } } } return false; // Don't stop search } // Riempie una stringa SQL con la relazione tra le tabelle void TQuery_mask::tree2sql(TString& from, TString& where) { if (_tree.goto_root()) { TPointer_array a; a.add(&from); a.add(&where); _tree.scan_depth_first(sql_tree_handler, &a); } } static bool isam_tree_handler(TTree& tree, void* jolly, word flags) { TRelation_node* rn = (TRelation_node*)tree.curr_node(); TString_array* a = (TString_array*)jolly; TToken_string row; if (a->items() == 0) { row << "USE " << rn->name(); } else { row << "JOIN " << rn->name(); if (rn->father()->name() != a->row(0).mid(4)) row << " TO " << rn->father()->name(); if (!rn->alias().blank()) row << " ALIAS " << rn->alias(); row << " INTO "; FOR_EACH_ARRAY_ROW(rn->join(), i, r) row << *r << ' '; } a->add(row); return false; } // Riempie una stringa ISAM con la relazione tra le tabelle void TQuery_mask::tree2isam(TString_array& a) { if (_tree.goto_root()) _tree.scan_depth_first(isam_tree_handler, &a); } inline bool tok_get_bool(TToken_string& tok, int pos) { const char* str = tok.get(pos); return str && *str > ' '; } static void add_where_clause(TString& where, const char* field, const char* cmp, const char* expr) { if (where.not_empty()) where << "AND"; where << '(' << field << cmp << expr << ')'; } // Genera una query SQL a partire dallo spreadsheet F_SHEET void TQuery_mask::sheet2sql() { const TSheet_field& sheet = sfield(F_SHEET); TToken_string select(50, ','); _tree.goto_root(); const bool multiple = _tree.has_son(); TToken_string from(50, ','), groupby(50, ','), orderby(50, ','); TString where, expr_from, expr_to; TString field; FOR_EACH_SHEET_ROW(sheet, i, row) { field = row->get(1); if (!field.blank() && !tok_get_bool(*row, 2)) // Campo valido e visibile { if (multiple) { const TString& tab = row->get(0); if (!tab.blank()) { field.insert("."); field.insert(tab); // Table name } } select.add(field); // Column name only if (tok_get_bool(*row, 3)) // Sort orderby.add(field); if (tok_get_bool(*row, 4)) // Group groupby.add(field); expr_from = row->get(5); expr_from.trim(); expr_to = row->get(6); expr_to.trim(); if (expr_from.not_empty() || expr_to.not_empty()) { if (expr_from.not_empty() && isalpha(expr_from[0])) { expr_from.insert("'"); expr_from << '\''; } if (expr_to.not_empty() && isalpha(expr_to[0])) { expr_to.insert("'"); expr_to << '\''; } if (expr_from == expr_to) { add_where_clause(where, field, "=", expr_from); } else { if (expr_from.not_empty()) add_where_clause(where, field, ">=", expr_from); if (expr_to.not_empty()) add_where_clause(where, field, "<=", expr_to); } } } } tree2sql(from, where); TString sql; sql << "SELECT " << select << '\n'; sql << "FROM " << from << '\n'; if (!where.blank()) sql << "WHERE " << where << '\n'; if (groupby.not_empty()) sql << "GROUP BY " << groupby << '\n'; if (orderby.not_empty()) sql << "ORDER BY " << orderby << '\n'; sql << ";"; set(F_SQL, sql, true); _sql_dirty = false; TEdit_field& fs = efield(F_SQL); fs.set_focusdirty(false); // Evita di scatenare eventi inutili } // Genera una query ISAM a partire dallo spreadsheet F_SHEET void TQuery_mask::sheet2isam() { TString_array rel; tree2isam(rel); TString use; FOR_EACH_ARRAY_ROW(rel, i, row) use << *row << '\n'; set(F_SQL, use, true); _sql_dirty = false; TEdit_field& fs = efield(F_SQL); fs.set_focusdirty(false); // Evita di scatenare eventi inutili } bool TQuery_mask::ask_vars(const char* maskname, TRecordset& recset) const { if (recset.variables().items() == 0) return true; TFilename fname = maskname; fname.ext("msk"); KEY key = K_QUIT; if (!fname.custom_path()) return recset.ask_variables(true); TMask m(maskname); TVariant var; for (int i = m.fields()-1; i >= 0; i--) { TMask_field& f = m.fld(i); const TFieldref* ref = f.field(); if (ref != NULL) { TString name = ref->name(); if (name[0] != '#') name.insert("#"); const TVariant& var = recset.get_var(name); if (!var.is_null()) f.set(var.as_string()); } } key = m.run(); const bool ok = key != K_QUIT && key != K_ESC; if (ok) { // Rendi visibili tutte le variabili utente al report for (int i = m.fields()-1; i >= 0; i--) { TMask_field& f = m.fld(i); const TFieldref* ref = f.field(); if (ref != NULL) { switch (f.class_id()) { case CLASS_CURRENCY_FIELD: case CLASS_REAL_FIELD: var = real(f.get()); break; case CLASS_DATE_FIELD: var = TDate(f.get()); break; default: var = f.get(); break; } TString name = ref->name(); if (name[0] != '#') name.insert("#"); recset.set_var(name, var, true); } } } return ok; } TRecordset* TQuery_mask::new_recordset() const { const TString& sql = get(F_SQL); TRecordset* rex = create_recordset(sql); if (rex != NULL) { if (!ask_vars(get(F_CODICE), *rex)) { delete rex; rex = NULL; } if (rex != NULL && rex->items() == 0) { warning_box(TR("Nessuna riga risultato")); delete rex; rex = NULL; } } return rex; } void TQuery_mask::edit_query() { TRecordset* rex = new_recordset(); if (rex != NULL) { TRecordset_sheet sht(*rex); sht.run(); delete rex; } } void TQuery_mask::save_as(TRecordsetExportFormat fmt) { TRecordset* rex = new_recordset(); if (rex == NULL) return; if (fmt == fmt_dbf) { TTable_mask tm; TList_sheet& sht = *tm.efield(F_SON).sheet(); if (sht.run() == K_ENTER) { const TString& table = tm.get(F_SON); const KEY k = yesnocancel_box(FR("Si desidera azzerare il file %s prima dell'esportazione?"), (const char*)table); if (k != K_ESC) rex->save_as(table, fmt_dbf, k == K_YES ? 0x5 : 0x3); } return; } xvt_fsys_save_dir(); TFilename path; path.tempdir(); const char* ext = "txt"; switch (fmt) { case fmt_html: ext = "html"; break; case fmt_silk: ext = "xls"; break; default: break; } FILE_SPEC fs; /* xvt_fsys_convert_str_to_dir(path, &fs.dir); strcpy(fs.name, get(F_CODICE)); strcpy(fs.type, ext); strcpy(fs.creator, "AGA"); */ if (field(F_CODICE).empty()) path.add("query"); else path.add(get(F_CODICE)); path.ext(ext); xvt_fsys_convert_str_to_fspec(path, &fs); xvt_fsys_save_dir(); const bool good = xvt_dm_post_file_save(&fs, TR("Esportazione")) == FL_OK; xvt_fsys_restore_dir(); if (good) { xvt_fsys_convert_fspec_to_str(&fs, path.get_buffer(), path.size()); if (rex->save_as(path, fmt)) xvt_sys_goto_url(path, "open"); } delete rex; } bool TQuery_mask::get_qry_path(TFilename& path) const { const TString& name = get(F_CODICE); const bool ok = name.not_empty(); if (ok) { path = name; if (!path.is_absolute_path()) { path = firm2dir(-1); path.add("custom"); if (!path.exist()) xvt_fsys_mkdir(path); path.add(name); } path.ext("qry"); } return ok; } bool TQuery_mask::select_query() { TFilename path; const bool ok = select_custom_file(path, "qry"); if (ok) { path = path.name(); path.ext(""); set(F_CODICE, path); } return ok; } static bool xml_save_tree_handler(TTree& tree, void* jolly, word flags) { TXmlItem* rel = (TXmlItem*)jolly; TRelation_node& node = (TRelation_node&)*tree.curr_node(); TXmlItem& son = rel->AddChild("table"); TString4 num; num << node.num(); son.SetAttr("Num", num); son.SetAttr("Name", node.name()); if (node.alias().not_empty()) son.SetAttr("Alias", node.alias()); if (node.father() != NULL) { son.SetAttr("Father", node.father()->id()); FOR_EACH_ARRAY_ROW(node.join(), i, row) { if (i > 0) son << "AND"; son << "(" << *row << ")"; } } return false; } bool TQuery_mask::save_tables_tree(TXmlItem& xml) { const bool ok = _tree.goto_root(); if (ok) { TXmlItem& rel = xml.AddChild("tables"); _tree.scan_depth_first(xml_save_tree_handler, &rel); } return ok; } bool TQuery_mask::save_fields_sheet(TXmlItem& xml) { TSheet_field& sf = sfield(F_SHEET); const bool ok = sf.items() > 0; if (ok) { TXmlItem& fields = xml.AddChild("fields"); FOR_EACH_SHEET_ROW(sf, i, row) { TXmlItem& field = fields.AddChild("field"); field.SetAttr("Table", row->get(0)); field.SetAttr("Name", row->get(1)); field.SetAttr("Hidden", tok_get_bool(*row,2)); field.SetAttr("Sort", tok_get_bool(*row,3)); field.SetAttr("Group", tok_get_bool(*row,4)); const char* str = row->get(5); if (str && *str > ' ') field.SetAttr("ExprFrom", str); str = row->get(6); if (str && *str > ' ') field.SetAttr("ExprTo", str); } } return ok; } bool TQuery_mask::save_query() { bool ok = _curr_query.not_empty(); if (!ok) ok = get_qry_path(_curr_query); if (!ok) return field(F_CODICE).on_key(K_ENTER); // Segnala errore char name[_MAX_FNAME]; xvt_fsys_parse_pathname (_curr_query, NULL, NULL, name, NULL, NULL); ok = *name > ' '; if (ok) { TXmlItem xml; xml.SetTag("query"); xml.SetAttr("Name", name); xml.AddChild("description") << get(F_DESCR); save_tables_tree(xml); save_fields_sheet(xml); xml.AddChild("sql") << get(F_SQL); xml.Save(_curr_query); _is_dirty = false; } return ok; } bool TQuery_mask::save_if_needed() { if (!_is_dirty || !field(DLG_SAVEREC).active()) return true; if (!yesno_box(TR("Si desidera registrare la query?"))) return false; return save_query(); } /////////////////////////////////////////////////////////// // Caricamento da file xml /////////////////////////////////////////////////////////// // Carica l'albero della relazione bool TQuery_mask::load_tables_tree(TXmlItem& tables) { for (int i = 0; i < tables.GetChildren(); i++) { const TXmlItem& table = *tables.GetChild(i); const int num = atoi(table.GetAttr("Num")); if (num >= LF_USER) { _tree.find_id(table.GetAttr("Father")); const TRelation_node* father = (const TRelation_node*)_tree.curr_node(); TRelation_node* son = new TRelation_node(father, num, table.GetAttr("Alias")); if (father != NULL) { TString expr; table.GetEnclosedText(expr); int i = expr.find('('); while (i >= 0) { const int j = expr.find(')', i+1); TToken_string* eq = new TToken_string(expr.sub(i+1, j), '='); son->join().add(eq); i = expr.find('(', j+1); } } _tree.add_son(son); } } const bool ok = _tree.goto_root(); if (ok) { const TRelation_node* rn = (TRelation_node*)_tree.curr_node(); _curr_num = rn->num(); fill_fields(); _tree.expand_all(); tfield(F_TABLES).win().force_update(); } enable_field_buttons(); return ok; } // Carica l'elenco dei campi (o colonne) bool TQuery_mask::load_fields_sheet(TXmlItem& xml) { TSheet_field& sheet = sfield(F_SHEET); TXmlItem* fields = xml.FindFirst("fields"); const bool ok = fields != NULL; if (ok) { for (int i = 0; i < fields->GetChildren(); i++) { const TXmlItem& field = *fields->GetChild(i); TToken_string& row = sheet.row(-1); row.add(field.GetAttr("Table"),0); row.add(field.GetAttr("Name"),1); row.add(field.GetBoolAttr("Hidden") ? "X" : "",2); row.add(field.GetBoolAttr("Sort") ? "X" : "",3); row.add(field.GetBoolAttr("Group") ? "X" : "",4); row.add(field.GetAttr("ExprFrom"),5); row.add(field.GetAttr("ExprTo"),6); } sheet.force_update(); } enable_sql_button(); return ok; } // Azzera tutto, ma proprio tutto void TQuery_mask::global_reset() { if (_tree.goto_root()) { _tree.kill_node(); TTree_field& tf = tfield(F_TABLES); tf.select_current(); tf.win().force_update(); } reset(); _is_dirty = _sql_dirty = false; } // Caica l'intera query bool TQuery_mask::load_query() { TFilename path; get_qry_path(path); bool ok = path.exist(); if (ok) { TXmlItem xml; ok = xml.Load(path); if (ok) { _curr_query = path; global_reset(); path = path.name(); path.ext(""); set(F_CODICE, path); const TXmlItem* desc = xml.FindFirst("description"); if (desc != NULL) { TString str; desc->GetEnclosedText(str); set(F_DESCR, str); } TXmlItem* tables = xml.FindFirst("tables"); if (tables != NULL) load_tables_tree(*tables); load_fields_sheet(xml); const TXmlItem* sql = xml.FindFirst("sql"); if (sql != NULL) { TString str; sql->GetEnclosedText(str); set(F_SQL, str, true); // Aggiorna anche stato bottoni di esportazione TEdit_field& sf = efield(F_SQL); sf.set_dirty(false); // Evita falsi allarmi di registrazione _sql_dirty = !sf.empty(); } _is_dirty = false; // Resetta definitivamente il dirty } } return ok; } bool TQuery_mask::delete_query() { TFilename path; get_qry_path(path); const bool ok = yesno_box(FR("Si desidera eliminare il file %s"), (const char*)path); if (ok) { ::remove(path); global_reset(); } return ok; } // Gestione eventi standard bool TQuery_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) { switch (o.dlg()) { case F_CODICE: if (e == fe_button) { if (select_query()) e = fe_modify; } if (e == fe_init || e == fe_modify) { save_if_needed(); load_query(); } break; case F_TABLES: if (e == fe_modify) { const TRelation_node* rn = (TRelation_node*)_tree.curr_node(); if (rn && rn->num() != _curr_num) { _curr_num = rn->num(); fill_fields(); } } break; case F_FIELDS: if (e == se_query_modify || e == se_query_add || e == se_query_del) return false; enable_field_buttons(); break; case F_ADDFILE: if (e == fe_button) add_file_to_tree(); break; case F_EDITFILE: if (e == fe_button) edit_file_in_tree(); break; case DLG_USER: if (e == fe_button) add_field_to_sheet(); break; case F_ASTERISK: if (e == fe_button) add_asterisk_to_sheet(); break; case F_GENSQL: case F_GENISAM: if (e == fe_button) { next_page(1001); bool ok = true; if (_sql_dirty && !field(F_SQL).empty()) ok = yesno_box(TR("Attenzione: la query verra' rigenerata\n" "annullando eventuali modifiche manuali.\n" "Si desidera proseguire?")); if (ok) { if (o.dlg() == F_GENSQL) sheet2sql(); else sheet2isam(); } } break; case F_EDITQUERY: if (e == fe_button) edit_query(); break; case F_SQL: if (e == fe_init || e == fe_modify) { const bool on = !o.empty(); enable(F_EXPORT_DBF, on && is_power_station()); if (e == fe_modify) { _is_dirty = true; _sql_dirty = !o.empty(); } } break; case F_EXPORT_HTML: if (e == fe_button) save_as(fmt_html); break; case F_EXPORT_EXCEL: if (e == fe_button) save_as(fmt_silk); break; case F_EXPORT_TXT: if (e == fe_button) save_as(fmt_text); break; case F_EXPORT_CAMPO: if (e == fe_button) save_as(fmt_campo); break; case F_EXPORT_DBF: if (e == fe_button) save_as(fmt_dbf); break; case F_SHEET: enable_sql_button(); break; case F_MOVEUP: if (e == fe_button) move_curr_field(-1); break; case F_MOVEDN: if (e == fe_button) move_curr_field(+1); break; case DLG_NEWREC: if (e == fe_button) { save_if_needed(); global_reset(); next_page(1000); } break; case DLG_FINDREC: if (e == fe_button) send_key(K_F9, F_CODICE); case DLG_SAVEREC: if (e == fe_button) { get_qry_path(_curr_query); save_query(); next_page(1000); } break; case DLG_DELREC: if (e == fe_button && jolly == 0) // Elimina della Toolbar { delete_query(); next_page(1000); return false; // Do not exit! } break; case DLG_QUIT: save_if_needed(); break; default: break; } return true; } // Gestione eventi di trascinamento void TQuery_mask::handler(WINDOW wnd, EVENT* ep) { switch (ep->type) { case E_MOUSE_DOWN: if (ep->v.mouse.button == 0 && wnd == page_win(0)) { const TSheet_field& sf = sfield(F_FIELDS); RCT rct; sf.get_rect(rct); _dragster = xvt_rect_has_point(&rct, ep->v.mouse.where) != 0; } else _dragster = false; if (_dragster) { XinCursor hand = xi_get_pref(XI_PREF_HAND_CURSOR_RID); xvt_win_set_cursor(wnd, (CURSOR)hand); } else xvt_win_set_cursor(wnd, CURSOR_ARROW); break; case E_MOUSE_UP: if (ep->v.mouse.button == 0 && _dragster) { TSheet_field& ff = sfield(F_SHEET); RCT rct; ff.get_rect(rct); if (xvt_rect_has_point(&rct, ep->v.mouse.where)) add_field_to_sheet(); xvt_win_set_cursor(wnd, CURSOR_ARROW); } _dragster = false; break; default: break; } TAutomask::handler(wnd, ep); } TQuery_mask::TQuery_mask() : TAutomask("ba8200a"), _curr_num(0), _is_dirty(false) { RCT rcts, rctf, rctt; TSheet_field& sheet = sfield(F_SHEET); sheet.get_rect(rcts); // Allarga a dritta lo spreadsheet coi noi dei campi TSheet_field& fields = sfield(F_FIELDS); fields.get_rect(rctf); rctf.right = rcts.right; fields.set_rect(rctf); // Allarga a mancina l'albero della relazione TTree_field& trf = tfield(F_TABLES); trf.get_rect(rctt); rctt.top -= 4; rctt.left = rcts.left+4; rctt.right -= 32; // Toglie scrollbar rctt.bottom = rctf.bottom - 32; trf.set_rect(rctt); trf.set_tree(&_tree); // Associa l'albero al campo della maschera } /////////////////////////////////////////////////////////// // TSQL_recordset_app /////////////////////////////////////////////////////////// class TSQL_recordset_app : public TSkeleton_application { TQuery_mask* _msk; public: virtual bool create(); virtual void main_loop(); virtual bool destroy(); }; bool TSQL_recordset_app::create() { if (!has_module(RSAUT)) return error_box(TR("Modulo non autorizzato")); _msk = new TQuery_mask; xvt_sys_sleep(500); // Lasciamo il tempo di leggere il titolo if (argc() > 2) { _msk->set(F_CODICE, argv(2)); _msk->load_query(); if (argc() > 3) { switch (argv(3)[0]) { case 'H': _msk->save_as(fmt_html); break; case 'T': _msk->save_as(fmt_text); break; case 'X': _msk->save_as(fmt_silk); break; case 'C': _msk->save_as(fmt_campo); break; default : _msk->save_as(fmt_html); break; } } else _msk->edit_query(); return FALSE; } return TSkeleton_application::create(); } void TSQL_recordset_app::main_loop() { if (argc() > 2) { _msk->set(F_CODICE, argv(2)); // Carico la query da riga di comando _msk->disable(DLG_SAVEREC); // Non permetto modifiche di alcun genere _msk->disable(DLG_NEWREC); _msk->disable(DLG_DELREC); _msk->disable(DLG_FINDREC); } _msk->run(); } bool TSQL_recordset_app::destroy() { if (_msk != NULL) { delete _msk; _msk = NULL; } return true; } int ba8200(int argc, char* argv[]) { TSQL_recordset_app app; app.run(argc, argv, TR("Query Generator")); return 0; }