From ccde3b0440ccc4f75c7d0454e2811e404441a08a Mon Sep 17 00:00:00 2001 From: guy Date: Fri, 7 May 2004 10:25:51 +0000 Subject: [PATCH] Patch level : 2.1 nopatch Files correlati : Ricompilazione Demo : [ ] Commento : Spostate da BA ad INCLUDE tutte le nuove classi delle Query e dei Report git-svn-id: svn://10.65.10.50/trunk@12062 c028cbd2-c16b-5b4b-a496-9718f37d4682 --- include/alex.cpp | 1287 ++++++++++++++++++++++++ include/alex.h | 74 ++ include/form.cpp | 50 +- include/maskfld.cpp | 9 +- include/recset.cpp | 2072 +++++++++++++++++++++++++++++++++++++++ include/recset.h | 239 +++++ include/relapp.cpp | 8 +- include/report.cpp | 2265 +++++++++++++++++++++++++++++++++++++++++++ include/report.h | 462 +++++++++ include/reprint.cpp | 917 ++++++++++++++++++ include/reprint.h | 100 ++ include/sheet.cpp | 9 +- include/window.h | 2 +- 13 files changed, 7455 insertions(+), 39 deletions(-) create mode 100755 include/alex.cpp create mode 100755 include/alex.h create mode 100755 include/recset.cpp create mode 100755 include/recset.h create mode 100755 include/report.cpp create mode 100755 include/report.h create mode 100755 include/reprint.cpp create mode 100755 include/reprint.h diff --git a/include/alex.cpp b/include/alex.cpp new file mode 100755 index 000000000..18f0cd1f1 --- /dev/null +++ b/include/alex.cpp @@ -0,0 +1,1287 @@ +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +TVariant& TVariant_stack::peek(int depth) +{ + const int sp = _sp-depth-1; + return sp >= 0 ? (TVariant&)_var[sp] : NULL_VARIANT; +} + +bool TVariant_stack::drop() +{ + if (_sp > 0) + { + _sp--; + return true; + } + return false; +} + +TVariant& TVariant_stack::pop() +{ + TVariant& var = peek(0); + drop(); + return var; +} + +void TVariant_stack::roll(int depth) +{ + const int sp = _sp-depth-1; + if (sp >= 0) + { + TObject* var = _var.remove(sp, true); + _var.insert(var, _sp-1); + } +} + +void TVariant_stack::push(const TVariant& var) +{ + if (_var.objptr(_sp) == NULL) + _var.add(var, _sp); + else + (TVariant&)_var[_sp] = var; + _sp++; +} + +void TVariant_stack::push(long n) +{ + const TVariant var(n); + push(var); +} + +void TVariant_stack::push(const real& n) +{ + const TVariant var(n); + push(var); +} + +void TVariant_stack::push(const char* str) +{ + const TVariant var(str); + push(var); +} + +void TVariant_stack::reset() +{ + _sp = 0; +} + +bool TVariant_stack::overflow() const +{ + return _sp > 2048; +} + +/////////////////////////////////////////////////////////// +// TAVM_op +/////////////////////////////////////////////////////////// + +enum AVM_opcode +{ + avm_nop, + avm_add, avm_and, + avm_begin, + avm_call_word, avm_cold, + avm_cmp_eq, avm_cmp_gt, avm_cmp_gteq, avm_cmp_lt, avm_cmp_lteq, avm_cmp_noteq, + avm_cmp_emptyeq, avm_cmp_nulleq, avm_cmp_zeroeq, + avm_div, avm_divide, avm_do, avm_dot, avm_drop, avm_dup, + avm_else, + avm_fetch, avm_forget, + avm_i, avm_if, avm_include, + avm_j, + avm_loop, + avm_mod, avm_mon, avm_mul, + avm_negate, avm_null, + avm_or, avm_over, + avm_perform, avm_pick, avm_plus_loop, avm_plus_store, avm_push, + avm_repeat, avm_rdrop, avm_rpeek, avm_rpush, avm_roll, avm_rot, + avm_strlen, avm_strmid, avm_strtok_fetch, avm_strtok_add, + avm_store, avm_sp, avm_sub, avm_swap, + avm_then, + avm_until, avm_usrword, + avm_variable, + avm_warm, avm_while, + avm_zzz +}; + +const char* AVM_TOKENS[avm_zzz+1] = +{ + "$NOP$", + "+", "AND", + "BEGIN", + "$CALL_WORD$", "COLD", + "=", ">", ">=", "<", "<=", "<>", + "EMPTY=", "NULL=", "0=", + "DIV", "/", "DO", ".", "DROP", "DUP", + "ELSE", + "@", "FORGET", + "I", "IF", "INCLUDE", + "J", + "LOOP", + "MOD", "MON", "*", + "NEGATE", "NULL", + "OR", "OVER", + "PERFORM", "PICK", "+LOOP", "+!", "$PUSH$", + "REPEAT", "R>", "R@", ">R", "ROLL", "ROT", + "STRLEN", "STRMID", "STRTOK@", "STRTOK+", + "!", "SP", "-", "SWAP", + "THEN", + "UNTIL", "$USR$", + "VARIABLE", + "WARM", "WHILE" +}; + +enum TBreakpointType { brk_none = 0x0, brk_user = 0x1, brk_auto = 0x2 }; + +class TAVM_op : public TObject +{ + AVM_opcode _op; + TVariant _var; + int _break_pointer; + +public: + const TVariant& var() const { return _var; } + TVariant& var() { return _var; } + AVM_opcode op() const { return _op; } + bool has_break() const { return _break_pointer != brk_none; } + bool has_auto_break() const { return (_break_pointer & brk_auto) != 0; } + void set_user_break(bool on); + void set_auto_break(bool on); + + TAVM_op(AVM_opcode o, const TString& str); + TAVM_op(AVM_opcode o, const real& num); + TAVM_op(AVM_opcode o, const long num); + TAVM_op(const TAVM_op& op); + TAVM_op(AVM_opcode o); +}; + +void TAVM_op::set_user_break(bool on) +{ + _break_pointer = on ? brk_user : brk_none; +} + +void TAVM_op::set_auto_break(bool on) +{ + if (on) + _break_pointer |= brk_auto; + else + _break_pointer &= ~brk_auto; +} + + +TAVM_op::TAVM_op(AVM_opcode o, const TString& str) + : _op(o), _var(str), _break_pointer(0) +{ } + +TAVM_op::TAVM_op(AVM_opcode o, const real& num) + : _op(o), _var(num), _break_pointer(0) +{ } + +TAVM_op::TAVM_op(AVM_opcode o, const long num) + : _op(o), _var(num), _break_pointer(0) +{ } + +TAVM_op::TAVM_op(AVM_opcode o) + : _op(o), _break_pointer(0) +{ } + +TAVM_op::TAVM_op(const TAVM_op& op) : _op(op._op), _var(op._var), _break_pointer(0) +{ +} + +/////////////////////////////////////////////////////////// +// TAVM_monitor +/////////////////////////////////////////////////////////// + +class TAVM_list_window : public TField_window +{ + TBytecode* _bc; + int _ip; + const TString_array* _user_words; + +protected: + virtual void update(); + virtual void handler(WINDOW win, EVENT* ep); + +public: + void set_bytecode(const TBytecode* bc, int ip, const TString_array& uw); + TAVM_list_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner); +}; + +void TAVM_list_window::handler(WINDOW win, EVENT* ep) +{ + if (ep->type == E_MOUSE_DOWN) + { + const TPoint pt = dev2log(ep->v.mouse.where); + if (pt.x <= 5) + { + TAVM_op& op = *(TAVM_op*)_bc->objptr(pt.y-1); + op.set_user_break(ep->v.mouse.button == 0); + force_update(); + } + } + TField_window::handler(win, ep); +} + +void TAVM_list_window::update() +{ + clear(NORMAL_BACK_COLOR); + if (_bc != NULL) + { + autoscroll(false); + set_brush(DISABLED_BACK_COLOR); + bar(0, 0, columns()+1, 1); + bar(0, 0, 5, rows()+1); + set_brush(NORMAL_BACK_COLOR); + printat(0, 0, _bc->name()); + autoscroll(true); + + TString str; + int tab = 6; + + const int last = min(_bc->items(), rows()); + for (int i = 0; i < last; i++) + { + + const int y = i+1; + if (_ip == i) + { + set_brush(FOCUS_BACK_COLOR); + bar(0, y, 80, y+1); + set_brush(NORMAL_BACK_COLOR); + } + printat(0, y, "%04d", i); + const TAVM_op& op = *(const TAVM_op*)_bc->objptr(i); + const AVM_opcode co = op.op(); + const TVariant& var = op.var(); + + if (op.has_break()) + printat(4, y, "<"); + + if (co == avm_else || co == avm_then || + co == avm_loop || co == avm_plus_loop || + co == avm_repeat || co == avm_until || co == avm_while) + tab -= 2; + if (co == avm_push) + { + if (var.is_string() && var.as_string()[0] != '#') + str.cut(0) << '"' << var.as_string() << '"'; + else + str = var.as_string(); + } else + if (co == avm_call_word) + { + str = var.as_string(); + } else + if (co == avm_usrword) + { + str = _user_words->row(var.as_int()); + str << " (#" << var.as_int() << ')'; + } + else + { + str = AVM_TOKENS[co]; + if (!var.is_null()) + str << " (" << var.as_string() << ')'; + } + printat(tab, y, str); + if (co == avm_if || co == avm_else || + co == avm_do || co == avm_begin || co == avm_while) + tab += 2; + } + } +} + +void TAVM_list_window::set_bytecode(const TBytecode* bc, int ip, const TString_array& uw) +{ + _bc = (TBytecode*)bc; + _ip = ip; + _user_words = &uw; + set_scroll_max(80, _bc->items() - rows()); +} + +TAVM_list_window::TAVM_list_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner) + : TField_window(x, y, dx, dy, parent, owner), _bc(NULL) +{ + autoscroll(true); +} + +class TAVM_stack_window : public TField_window +{ + TVariant_stack* _stack; + +protected: + virtual void update(); + +public: + void set_stack(TVariant_stack& s); + TAVM_stack_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner); +}; + +void TAVM_stack_window::update() +{ + clear(NORMAL_BACK_COLOR); + if (_stack != NULL) + { + for (int i = 0; i < _stack->items(); i++) + printat(0, i, _stack->peek(i).as_string()); + } +} + +void TAVM_stack_window::set_stack(TVariant_stack& s) +{ + _stack = &s; + set_scroll_max(80, s.items() - rows()); +} + +TAVM_stack_window::TAVM_stack_window(int x, int y, int dx, int dy, WINDOW parent, TWindowed_field* owner) + : TField_window(x, y, dx, dy, parent, owner), _stack(NULL) +{ + autoscroll(true); +} + +class TAVM_list_field : public TWindowed_field +{ +protected: + virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent); + +public: + TAVM_list_field(TMask* m) : TWindowed_field(m) { } + virtual ~TAVM_list_field() { } +}; + +TField_window* TAVM_list_field::create_window(int x, int y, int dx, int dy, WINDOW parent) +{ + return new TAVM_list_window(x, y, dx, dy, parent, this); +} + +class TAVM_stack_field : public TWindowed_field +{ +protected: + virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent); + +public: + TAVM_stack_field(TMask* m) : TWindowed_field(m) { } + virtual ~TAVM_stack_field() { } +}; + +TField_window* TAVM_stack_field::create_window(int x, int y, int dx, int dy, WINDOW parent) +{ + return new TAVM_stack_window(x, y, dx, dy, parent, this); +} + +class TAVM_monitor : public TAutomask +{ +protected: + TMask_field* parse_field(TScanner& scanner); + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + virtual bool on_key(KEY k); + +public: + TAVM_list_window& monitor() { return (TAVM_list_window&)((TAVM_list_field&)field(101)).win(); } + TAVM_stack_window& stacker() { return (TAVM_stack_window&)((TAVM_list_field&)field(102)).win(); } + TAVM_stack_window& rstacker() { return (TAVM_stack_window&)((TAVM_list_field&)field(103)).win(); } + TAVM_monitor(); +}; + +bool TAVM_monitor::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + return true; +} + +bool TAVM_monitor::on_key(KEY k) +{ + switch (k) + { + case K_F10: + case K_F11: + stop_run(k); + return true; + default: break; + } + return TAutomask::on_key(k); +} + +TMask_field* TAVM_monitor::parse_field(TScanner& scanner) +{ + if (scanner.token().starts_with("LI")) + return new TAVM_list_field(this); + if (scanner.token().starts_with("ST")) + return new TAVM_stack_field(this); + return TAutomask::parse_field(scanner); +} + +TAVM_monitor::TAVM_monitor() +{ + read_mask("ba8304", 0, -1); + set_handlers(); +} + + +/////////////////////////////////////////////////////////// +// TAVM +/////////////////////////////////////////////////////////// + +class TAVM +{ + TAlex_virtual_machine* _vm; + bool _interactive; + TString _last_error; + TVariant_stack _stack, _rstack; + const TBytecode* _bc; // Current word (or command line) + int _ip; // Current instruction pointer + ostream* _outstr; + + TAssoc_array _words; + TAssoc_array _vars; + TString_array _user_words; + TAVM_monitor _mon; + +protected: + bool get_token(istream& instr, TString& str) const; + AVM_opcode token2opcode(const TString& str) const; + void log_error(const char* str); + int compare_tos_nos(); + int find_matching(const TBytecode& bytecode, AVM_opcode op1, AVM_opcode op2 = avm_nop) const; + void execute(const TAVM_op& op); + void do_call(const TString& func); + + const TString_array& get_user_words(); + int compile_user_word(const char* n); + +public: + const TString& get_last_error() const { return _last_error; } + + bool compile(istream& instr, TBytecode& bc); + bool execute(const TBytecode& bc, ostream* outstr = NULL); + void do_restart(bool cold); + bool do_include(const char* fname); + TVariant& do_fetch(const TString& name); + void do_add(); + void do_store(const TString& name); + void set_interactive(bool inter) { _interactive = inter; } + bool defined(const char* w); + + TAVM(TAlex_virtual_machine* vm); + virtual ~TAVM(); +}; + +void TAVM::log_error(const char* str) +{ + _last_error = str; + if (_interactive) + error_box(str); +#ifdef DBG + else + statbar_set_title(TASK_WIN, str); +#endif +} + +bool TAVM::get_token(istream& instr, TString& str) const +{ + str.cut(0); + instr.eatwhite(); + if (instr.eof()) + return false; + char c; + instr.get(c); + str << c; + if (c == '"') + { + char* buf = str.get_buffer()+1; + const int bufsize = str.size()-1; + instr.getline(buf, bufsize, '"'); + str << '"'; + } + else + { + instr.get(c); + while (!isspace(c) && c != EOF) + { + str << c; + instr.get(c); + } + } + + return *str > ' '; +} + +#define ALEX_TOKENS 24 + + +AVM_opcode TAVM::token2opcode(const TString& str) const +{ + for (int i = 0; AVM_TOKENS[i] != NULL; i++) + { + if (str == AVM_TOKENS[i]) + return AVM_opcode(i); + } + + const TBytecode* bc =(const TBytecode*)_words.objptr(str); + if (bc != NULL) + return avm_call_word; + + return avm_nop; +} + +int TAVM::find_matching(const TBytecode& bytecode, AVM_opcode op1, AVM_opcode op2) const +{ + int i; + for (i = bytecode.last(); i >= 0; i--) + { + TAVM_op& theop = (TAVM_op&)bytecode[i]; + if ((theop.op() == op1 || theop.op() == op2) && theop.var().is_null()) + { + theop.var() = bytecode.items(); + break; + } + } + return i; +} + +const TString_array& TAVM::get_user_words() +{ + if (_user_words.items() == 0) + { + _user_words.add("***"); + _vm->get_usr_words(_user_words); + } + return _user_words; +} + +int TAVM::compile_user_word(const char* w) +{ + const TString_array& uw = get_user_words(); + const int i = uw.find(w); + return i > 0 ? i : 0; +} + +bool TAVM::compile(istream& instr, TBytecode& bytecode) +{ + TString str(256); + bytecode.destroy(); + while (get_token(instr, str)) + { + TAVM_op* op = NULL; + if (str[0] == '"') + { + str.rtrim(1); str.ltrim(1); + op = new TAVM_op(avm_push, str); + } else + if ((isdigit(str[0]) || (str[0]=='-')) && isdigit(str[str.len()-1])) + { + const real r(str); + op = new TAVM_op(avm_push, r); + } else + if (str[0] == '#') // User variable + { + op = new TAVM_op(avm_push, str); + } else + if (str == ":") // User word + { + if (get_token(instr, str)) + { + TBytecode* bc = new TBytecode; + bc->set_name(str); + _words.add(str, bc, true); + compile(instr, *bc); + op = new TAVM_op(avm_nop); + } + else + { + _last_error = "Missing word after :"; + log_error(_last_error); + return false; + } + } else + if (str == ";") + { + return true; + } else + if (str == "(") + { + TString256 str; + instr.getline(str.get_buffer(), str.size(), ')'); + op = new TAVM_op(avm_nop); + } else + if (str == "\\") + { + TString256 str; + instr.getline(str.get_buffer(), str.size()); + op = new TAVM_op(avm_nop); + } + else + { + const int oc = compile_user_word(str); + if (oc > 0) + op = new TAVM_op(avm_usrword, oc); + else + { + const AVM_opcode oc = token2opcode(str); + if (oc != avm_nop) + { + switch (oc) + { + case avm_else: + if (find_matching(bytecode, avm_if) < 0) + { + _last_error = "ELSE without matching IF"; + log_error(_last_error); + return false; + } + op = new TAVM_op(oc); + break; + case avm_then: + if (find_matching(bytecode, avm_if, avm_else) < 0) + { + _last_error = "THEN without matching IF"; + log_error(_last_error); + return false; + } + op = new TAVM_op(oc); + break; + case avm_loop: + case avm_plus_loop: + { + const do_pos = find_matching(bytecode, avm_do); + if (do_pos < 0) + { + _last_error.cut(0) << str << " without matching DO"; + log_error(_last_error); + return false; + } + op = new TAVM_op(oc, do_pos); + } + break; + case avm_repeat: + case avm_until: + { + const int begin_pos = find_matching(bytecode, avm_begin); + if (begin_pos < 0) + { + _last_error.cut(0) << str << " without matching BEGIN"; + log_error(_last_error); + return false; + } + find_matching(bytecode, avm_while); + op = new TAVM_op(oc, begin_pos); + } + break; + case avm_call_word: + { + const TBytecode* bc = (const TBytecode*)_words.objptr(str); + if (bc != NULL && bc->items() == 1) + { + const TAVM_op* inline_op = (const TAVM_op*)bc->objptr(0); + op = new TAVM_op(*inline_op); + } + else + op = new TAVM_op(oc, str); + } + break; + default: + op = new TAVM_op(oc); + break; + } + } + } + } + if (op != NULL) + { + if (op->op() != avm_nop) + bytecode.add(op); + else + delete op; + } + else + { + _last_error.cut(0) << "Unknown WORD: " << str; + log_error(_last_error); + return false; + } + } + return true; +} + +int TAVM::compare_tos_nos() +{ + const TVariant& tos = _stack.pop(); + const TVariant& nos = _stack.pop(); + return nos.compare(tos); +} + +void TAVM::do_call(const TString& func) +{ + if (_stack.overflow()) + { + log_error("Stack overflow"); + _bc = NULL; + return; + } + TBytecode* bc = (TBytecode*)_words.objptr(func); + if (bc != NULL) + { + _rstack.push(_bc->name()); + _rstack.push(_ip+1); + _ip = -1; // will be incremented! + _bc = bc; + } + else + { + _last_error = func; _last_error << " ?"; + log_error(_last_error); + _bc = NULL; + } +} + +bool TAVM::do_include(const char* fname) +{ + TFilename name = fname; + bool ok = name.custom_path(); + if (ok) + { + TBytecode bc; + ifstream inf(name); + ok = compile(inf, bc); + if (ok) + execute(bc); + } + return ok; +} + +// Mette sullo stack il valore della variabile name +TVariant& TAVM::do_fetch(const TString& name) +{ + if (name[0] == '#') + { + TVariant var; + _vm->get_usr_val(name, var); + _stack.push(var); + } + else + { + const TVariant* var = (const TVariant*)_vars.objptr(name); + if (var != NULL) + _stack.push(*var); + else + _stack.push(NULL_VARIANT); + } + return _stack.peek(); +} + +void TAVM::do_add() +{ + const TVariant& v1 = _stack.pop(); + TVariant& v0 = (TVariant&)_stack.peek(); + v0.add(v1); +} + +// Legge dallo stack il valore da asseganre alla variabile name +void TAVM::do_store(const TString& name) +{ + const TVariant& var = _stack.pop(); + if (name[0] == '#') + _vm->set_usr_val(name, var); + else + { + TVariant* v = (TVariant*)_vars.objptr(name); + if (v != NULL) + *v = var; + } +} + +bool TAVM::defined(const char* name) +{ + if (_words.objptr(name) != NULL) + return true; + + return compile_user_word(name) > 0; +} + +void TAVM::execute(const TAVM_op& op) +{ + switch(op.op()) + { + case avm_add: do_add(); break; + case avm_and: + { + const TVariant& v1 = _stack.pop(); + TVariant& v0 = (TVariant&)_stack.peek(); + const long r = v0.as_int() & v1.as_int(); + v0.set(r); + } + break; + case avm_begin: break; + case avm_call_word: do_call(op.var().as_string()); break; + case avm_cold: do_restart(true); _bc = NULL; break; + case avm_cmp_eq : _stack.push(compare_tos_nos() == 0); break; + case avm_cmp_gt : _stack.push(compare_tos_nos() > 0); break; + case avm_cmp_gteq : _stack.push(compare_tos_nos() >= 0); break; + case avm_cmp_lt : _stack.push(compare_tos_nos() < 0); break; + case avm_cmp_lteq : _stack.push(compare_tos_nos() <= 0); break; + case avm_cmp_noteq: _stack.push(compare_tos_nos() != 0); break; + case avm_cmp_emptyeq: _stack.push(_stack.pop().is_empty()); break; + case avm_cmp_nulleq:_stack.push(_stack.pop().is_null()); break; + case avm_cmp_zeroeq:_stack.push(_stack.pop().is_zero()); break; + case avm_div: + { + const long r0 = _stack.pop().as_int(); + const long r1 = _stack.pop().as_int(); + if (r0 != 0) + { + const long n = r1 / r0; + _stack.push(n); + } + else + _stack.push(NULL_VARIANT); + } + + break; + case avm_divide: + { + const real& r0 = _stack.pop().as_real(); + const real& r1 = _stack.pop().as_real(); + if (!r0.is_zero()) + { + const real n = r1 / r0; + _stack.push(n); + } + else + _stack.push(NULL_VARIANT); + } + break; + case avm_do: + { + const TVariant& start = _stack.pop(); + const TVariant& limit = _stack.pop(); + if (start.compare(limit) < 0) + { + _rstack.push(limit); + _rstack.push(start); + } + else + _ip = op.var().as_int(); + } + break; + case avm_dot: + if (_outstr != NULL) + *_outstr << _stack.pop().as_string(); + break; + case avm_drop: + if (!_stack.drop()) + { + log_error("Stack underflow"); + _bc = NULL; + } + break; + case avm_dup: _stack.push(_stack.peek()); break; + case avm_else: + _ip = op.var().as_int(); + break; + case avm_fetch: do_fetch(_stack.pop().as_string()); break; + case avm_forget: + { + const TString& name = _stack.pop().as_string(); + _vars.remove(name); + _words.remove(name); + } + break; + case avm_i: _stack.push(_rstack.peek()); break; + case avm_if: + if (_stack.pop().is_zero()) + _ip = op.var().as_int(); + break; + case avm_include: do_include(_stack.pop().as_string()); break; + case avm_j: _stack.push(_rstack.peek(2)); break; + case avm_loop: + { + TVariant& start = _rstack.pop(); + const TVariant& limit = _rstack.pop(); + start.add(TVariant(UNO)); + _stack.push(limit); + _stack.push(start); + _ip = op.var().as_int()-1; + } + break; + case avm_mod: + { + const long i0 = _stack.pop().as_int(); + const long i1 = _stack.pop().as_int(); + _stack.push(i1 % i0); + } + break; + case avm_mon: + if (!_mon.is_open()) + _mon.open_modal(); + break; + case avm_mul: + { + const TVariant& v1 = _stack.pop(); + TVariant& v0 = (TVariant&)_stack.peek(); + const real m = v0.as_real() * v1.as_real(); + v0.set(m); + } + break; + case avm_negate: + { + TVariant& tos = _stack.peek(); + tos.set(~tos.as_int()); + } + break; + case avm_null: _stack.push(NULL_VARIANT); break; + case avm_or: + { + const TVariant& tos = _stack.pop(); + TVariant& nos = (TVariant&)_stack.peek(); + const long r = nos.as_int() | tos.as_int(); + nos.set(r); + } + break; + case avm_over: _stack.push(_stack.peek(1)); break; + case avm_perform: do_call(_stack.pop().as_string()); break; + case avm_pick: _stack.push(_stack.peek(_stack.pop().as_int())); break; + case avm_plus_loop: + { + TVariant& start = _rstack.pop(); + const TVariant& limit = _rstack.pop(); + start.add(_stack.pop()); + _stack.push(limit); + _stack.push(start); + _ip = op.var().as_int()-1; + } + break; + case avm_plus_store: + { + const TString& name = _stack.pop().as_string(); + do_fetch(name); + do_add(); + do_store(name); + } + break; + case avm_push: _stack.push(op.var()); break; + case avm_repeat: _ip = op.var().as_int(); break; + case avm_rdrop: _stack.push(_rstack.pop()); break; + case avm_rpeek: _stack.push(_rstack.peek()); break; + case avm_rpush: _rstack.push(_stack.pop()); break; + case avm_roll: _stack.roll(_stack.pop().as_int()); break; + case avm_rot: _stack.roll(2); break; + case avm_store: do_store(_stack.pop().as_string()); break; + case avm_strlen: _stack.push(_stack.peek().as_string().len()); break; + case avm_strmid: + { + const int len = _stack.pop().as_int(); + const int frm = _stack.pop().as_int(); + const TString& str = _stack.peek().as_string(); + _stack.push(str.mid(frm, len)); break; + } + break; + case avm_strtok_fetch: + { + const int pos = _stack.pop().as_int(); + TToken_string str(_stack.pop().as_string()); + const char* tok = str.get(pos); + _stack.push(tok); + } + break; + case avm_strtok_add: + { + const TString& tok = _stack.pop().as_string(); + TToken_string str(_stack.pop().as_string()); + str.add(tok); + _stack.push(str); + } + break; + case avm_sp: _stack.push(_stack.items()); break; + case avm_sub : + { + const TVariant& v1 = _stack.pop(); + TVariant& v0 = (TVariant&)_stack.peek(); + v0.sub(v1); + } + break; + case avm_swap: _stack.roll(1); break; + case avm_then: break; + case avm_until: + if (_stack.pop().is_zero()) + _ip = op.var().as_int(); + break; + case avm_usrword: + { + const long usrword = op.var().as_int(); + _vm->execute_usr_word(usrword, _stack); + } + break; + case avm_variable: _vars.add(_stack.pop().as_string(), NULL_VARIANT); break; + case avm_warm: do_restart(false); _bc = NULL; break; + case avm_while: + if (_stack.pop().is_zero()) + _ip = op.var().as_int(); // Exit loop + break; + default: + _last_error << "Unimplemented op code: " << op.op() << '\n'; + log_error(_last_error); + _bc = NULL; // force exit + break; + } +} + +bool TAVM::execute(const TBytecode& cmdline, ostream* outstr) +{ + const TBytecode* old_bc = _bc; + const int old_ip = _ip; + + _stack.reset(); + _rstack.reset(); + _bc = &cmdline; + _ip = 0; + + if (outstr != NULL) + _outstr = outstr; + while (_bc != NULL) + { + while (_ip >= _bc->items()) // Fine funzione + { + if (_rstack.items() >= 2) // Controllo il return stack + { + _ip = _rstack.pop().as_int(); + const TString& str = _rstack.pop().as_string(); + if (str == cmdline.name()) + _bc = &cmdline; + else + { + _bc = (const TBytecode*)_words.objptr(str); + if (_bc == NULL) + break; + } + } + else + { + _bc = NULL; + break; // Fine esecuzione + } + } + if (_bc == NULL || _ip > _bc->items()) + break; + + TAVM_op& op = *(TAVM_op*)_bc->objptr(_ip); + if (op.has_break() && !_mon.is_open()) + { + if (op.has_auto_break()) + op.set_auto_break(false); + _mon.open_modal(); + } + + if (_mon.is_open()) // Gestione debugger + { + lock_preview_update(true); + TAVM_list_window& monitor = _mon.monitor(); + monitor.set_bytecode(_bc, _ip, _user_words); + TAVM_stack_window& stacker = _mon.stacker(); + stacker.set_stack(_stack); + TAVM_stack_window& rstacker = _mon.rstacker(); + rstacker.set_stack(_rstack); + const KEY k = _mon.run(); + switch (k) + { + case K_F11: monitor.force_update(); stacker.force_update(); rstacker.force_update(); break; + case K_F10: + if (_ip < _bc->items()-1) + { + _mon.close_modal(); + TAVM_op& op = *(TAVM_op*)_bc->objptr(_ip+1); + op.set_auto_break(true); + } + break; + case K_QUIT: abort_printing(); + case K_F5 : _mon.close_modal(); lock_preview_update(false); break; + default: break; + } + } + + execute(op); + + _ip++; + } + + if (_mon.is_open()) // Chiudi debugger + { + _mon.close_modal(); + lock_preview_update(false); + } + + //const bool ok = _bc != NULL; // Not aborted + _bc = old_bc; + _ip = old_ip; + + return true; +} + +void TAVM::do_restart(bool cold) +{ + _stack.reset(); + _rstack.reset(); + if (cold) + { + _words.destroy(); + _vars.destroy(); + do_include("alex.alx"); + } +} + +TAVM::TAVM(TAlex_virtual_machine* vm) + : _vm(vm), _outstr(NULL), _interactive(false) +{ + do_restart(true); +} + +TAVM::~TAVM() +{ +} + +/////////////////////////////////////////////////////////// +// TAlex_virtual_machine +/////////////////////////////////////////////////////////// + +TAVM& TAlex_virtual_machine::avm() +{ + if (_avm == NULL) + _avm = new TAVM(this); + return *_avm; +} + +const TString& TAlex_virtual_machine::get_last_error() const +{ + if (_avm != NULL) + return _avm->get_last_error(); + return EMPTY_STRING; +} + +bool TAlex_virtual_machine::compile(istream& instr, TBytecode& bc) +{ + return avm().compile(instr, bc); +} + +bool TAlex_virtual_machine::execute(const TBytecode& bc, ostream& outstr) +{ + return avm().execute(bc, &outstr); +} + +bool TAlex_virtual_machine::compile(const char* cmd, TBytecode& bc) +{ + istrstream instr((char*)cmd, strlen(cmd)); + return compile(instr, bc); +} + +bool TAlex_virtual_machine::execute(const TBytecode& bc, TString& outs) +{ + char* buf = outs.get_buffer(); + memset(buf, 0, outs.size()); + ostrstream outstr(buf, outs.size()); + return execute(bc, outstr); +} + +void TAlex_virtual_machine::warm_restart() // Ripartenza a caldo +{ + avm().do_restart(false); +} + +void TAlex_virtual_machine::cold_restart() // Ripartenza a freddo +{ + avm().do_restart(true); +} + +bool TAlex_virtual_machine::get_usr_val(const TString& name, TVariant& var) const +{ + if (name.starts_with("#SYSTEM.")) + { + const TFixed_string n((const char*)name+8); + if (n == "ADMINISTATOR") + { + var.set(dongle().administrator()); + return true; + } + if (n == "CLOCK") + { + const long msec = clock() / (CLOCKS_PER_SEC / 1000); + var.set(msec); + return true; + } + if (n == "FIRM") + { + var.set(prefix().get_codditta()); + return true; + } + if (n == "STUDY") + { + var.set(firm2dir(-1)); + return true; + } + if (n == "DATE") + { + const TDate oggi(TODAY); + var.set(oggi); + return true; + } + if (n == "USER") + { + var.set(user()); + return true; + } + } + return false; +} + +bool TAlex_virtual_machine::set_usr_val(const TString& name, const TVariant& var) +{ + if (name.starts_with("#SYSTEM.")) + { + const TFixed_string n((const char*)name+8); + if (n == "FIRM") + { + return prefix().set_codditta(var.as_int()); + } + } + return false; +} + +unsigned int TAlex_virtual_machine::get_usr_words(TString_array&) const +{ + return 0; +} + +bool TAlex_virtual_machine::execute_usr_word(unsigned int opcode, TVariant_stack& stack) +{ + return false; +} + +bool TAlex_virtual_machine::include(const char* fname) +{ + return avm().do_include(fname); +} + +void TAlex_virtual_machine::set_interactive(bool inter) +{ avm().set_interactive(inter); } + +bool TAlex_virtual_machine::defined(const char* name) +{ + return avm().defined(name); +} + +TAlex_virtual_machine::TAlex_virtual_machine() : _avm(NULL) +{ +} + +TAlex_virtual_machine::~TAlex_virtual_machine() +{ + if (_avm != NULL) + delete _avm; +} + diff --git a/include/alex.h b/include/alex.h new file mode 100755 index 000000000..83772d311 --- /dev/null +++ b/include/alex.h @@ -0,0 +1,74 @@ +#ifndef __ALEX_H +#define __ALEX_H + +#ifndef __RECORDSET_H +#include +#endif + +class TVariant_stack : public TObject +{ + TArray _var; + int _sp; + +public: + int items() const { return _sp; } + bool drop(); + TVariant& pop(); + TVariant& peek(int depth = 0); + void roll(int depth); + + void push(const TVariant& var); + void push(long n); + void push(const real& n); + void push(const char* str); + void reset(); + bool overflow() const; + + TVariant_stack() : _sp(0) { } +}; + +// Generic bytecode for any language + +class TBytecode : public TArray +{ + TString _name; + +public: + void set_name(const char* name) { _name = name; } + const TString& name() const { return _name; } +}; + +// ALEX = Another Language EXtension + +class TAVM; + +class TAlex_virtual_machine : public TObject +{ + TAVM* _avm; // Chesire's cat class + +protected: + TAVM& avm(); + +public: + virtual size_t get_usr_words(TString_array& names) const; + virtual bool execute_usr_word(unsigned int opcode, TVariant_stack& stack); + virtual bool get_usr_val(const TString& name, TVariant& var) const; + virtual bool set_usr_val(const TString& name, const TVariant& var); + + const TString& get_last_error() const; + bool compile(istream& instr, TBytecode& bc); + bool compile(const char* cmd, TBytecode& bc); + bool execute(const TBytecode& bc, ostream& outstr); + bool execute(const TBytecode& bc, TString& outstr); + bool include(const char* fname); + void warm_restart(); + void cold_restart(); + void set_interactive(bool inter); + bool defined(const char* name); + + TAlex_virtual_machine(); + virtual ~TAlex_virtual_machine(); +}; + +#endif + diff --git a/include/form.cpp b/include/form.cpp index e717655e9..a839cc54b 100755 --- a/include/form.cpp +++ b/include/form.cpp @@ -1,13 +1,13 @@ #define __FORM_CPP #include -#include #include #include #include #include #include #include +#include #include #include @@ -4119,19 +4119,18 @@ bool TForm::validate(TForm_item &cf, TToken_string &s) // dove: nome tabella da leggere // costante stringa o riferimento a campo della form (preceduto da '#') da usare come chiave di ricerca // identificativo del campo da leggere dalla tabella - TTable tab(s.get()); // prende il nome della tabella - tab.zero(); // vuota il record corrente della tabella - TString in(s.get()); // prende il valore o il campo da usare come codice di ricerca - if (in[0]== '#') + const TString4 cod = s.get(); // Codice tabella + valore = s.get(); // prende il valore o il campo da usare come codice di ricerca + if (valore[0]== '#') { - in.ltrim(1); - TForm_item &fi= cf.find_field(in); - in= fi.get(); + valore.ltrim(1); + TForm_item &fi= cf.find_field(valore); + valore = fi.get(); } - tab.put("CODTAB", in); // setta la chiave nella tabella - if (tab.read()== NOERR) + const TRectype& tab = cache().get(cod, valore); + if (!tab.empty()) { - const TString &fld= s.get(); // prende il nome del campo da leggere... + const TString &fld = s.get(); // prende il nome del campo da leggere... valore = tab.get(fld); cf.set(valore); } @@ -4182,8 +4181,8 @@ bool TForm::validate(TForm_item &cf, TToken_string &s) if (code== "_TODAY")// Messaggio per stampare la data di oggi { - TString16 dt(TDate(TODAY).string()); - cf.set(dt); + const TDate oggi(TODAY); + cf.set(oggi.string()); return TRUE; } @@ -4221,17 +4220,18 @@ bool TForm::validate(TForm_item &cf, TToken_string &s) // + %ANAGR (6) Anagrafica generale (indirizzo, ecc.) // + %COMUNI (113@) Comune di residenza // + %COMUNI (213@) Comune di residenza fiscale - TLocalisamfile firm(LF_NDITTE); - TLocalisamfile anag(LF_ANAG); - firm.put("CODDITTA",main_app().get_firm()); - firm.read(); - anag.put("TIPOA",firm.get("TIPOA")); - anag.put("CODANAGR",firm.get("CODANAGR")); - anag.read(); + const TRectype& firm = cache().get(LF_NDITTE, main_app().get_firm()); + + TString16 codanag; codanag.format("%c|%ld", firm.get_char("TIPOA"), firm.get_long("CODANAGR")); + const TRectype& anag = cache().get(LF_ANAG, codanag); + TString in(s.get()); - if (in[0]=='!') { + if (in[0]=='!') + { in.ltrim(1); - bool _fisc= anag.get("INDRF").not_empty(); + const bool _fisc= anag.get("INDRF").not_empty(); + if (in=="CODDITTA") + valore =firm.get("CODDITTA"); if (in=="RAGSOC") valore = anag.get("RAGSOC"); if (in=="IND") { @@ -4285,9 +4285,11 @@ bool TForm::validate(TForm_item &cf, TToken_string &s) valore = _firmrel[LF_UNLOC].get("NUMCCIAA"); valore << " del " << _firmrel[LF_UNLOC].get("DATAICCIAA"); } */ - } else { + } + else + { TFieldref fref(s.get(), 0); - valore = fref.read(anag.curr()); + valore = fref.read(anag); } cf.set(valore); return TRUE; diff --git a/include/maskfld.cpp b/include/maskfld.cpp index c948eefd1..abce6a21a 100755 --- a/include/maskfld.cpp +++ b/include/maskfld.cpp @@ -2659,8 +2659,7 @@ KEY TFile_select::run() if (good) { - const TFixed_string str(fs.name); - good = _filter.empty() || str.match(_filter); + good = _filter.empty() || xvt_str_match(fs.name, _filter, false); if (good) { TFilename path; @@ -2669,7 +2668,7 @@ KEY TFile_select::run() field().set(path); } else - field().error_box("Il nome del file non corrisponde alla maschera %s", _filter.get_buffer()); + field().error_box(TR("Il nome non corrisponde a %s"), _filter.get_buffer()); } return good ? K_ENTER : K_ESC; } @@ -2679,8 +2678,8 @@ bool TFile_select::check(CheckTime ct) const TFilename name = field().get(); if (ct != STARTING_CHECK && name.empty() && field().check_type() == CHECK_REQUIRED) - return FALSE; - bool ok = _filter.empty() || name.match(_filter); + return false; + bool ok = _filter.empty() || xvt_str_match(name, _filter, false); if (ok && field().roman()) // Must exist ok = name.exist(); return ok; diff --git a/include/recset.cpp b/include/recset.cpp new file mode 100755 index 000000000..cebe40166 --- /dev/null +++ b/include/recset.cpp @@ -0,0 +1,2072 @@ +#ifdef WIN32 +#include +#else +#include "../xvaga/incstr.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +/////////////////////////////////////////////////////////// +// TVariant +/////////////////////////////////////////////////////////// + +static const TVariant NULL_VARIANT; + +void TVariant::set_null() +{ + if (_ptr != NULL) + { + if (_type != _nullfld && _type != _longfld) + delete _ptr; + _ptr = NULL; + } + _type = _nullfld; +} + +void TVariant::set(const char* str) +{ + if (str != NULL) + { + if (_type == _alfafld) + *((TString*)_ptr) = str; + else + { + set_null(); + _type = _alfafld; + _ptr = new TString(str); + } + } + else + set_null(); +} + +void TVariant::set(const real& r) +{ + if (_type == _realfld) + *((real*)_ptr) = r; + else + { + set_null(); + _type = _realfld; + _ptr = new real(r); + } +} + +void TVariant::set(const TDate& d) +{ + if (_type == _datefld) + *((TDate*)_ptr) = d; + else + { + set_null(); + _type = _datefld; + _ptr = new TDate(d); + } +} + +void TVariant::set(const long n) +{ + if (_type != _longfld) + set_null(); + _type = _longfld; + _ptr = (void*)n; +} + +bool TVariant::is_zero() const +{ + switch (_type) + { + case _datefld: return !as_date().ok(); + case _longfld: return _ptr == NULL; + case _realfld: return as_real().is_zero(); + case _alfafld: return real::is_null(as_string()); + default: break; + } + return true; +} + +bool TVariant::is_empty() const +{ + if (_type == _alfafld) + return as_string().empty(); + return is_zero(); +} + +TDate TVariant::as_date() const +{ + if (_type == _datefld) + return *(TDate*)_ptr; + + const TDate d(as_int()); + return d; +} + +long TVariant::as_int() const +{ + long n = 0; + switch(_type) + { + case _datefld: n = as_date().date2ansi(); break; + case _longfld: n = (long)_ptr; break; + case _realfld: n = as_real().integer(); break; + case _alfafld: n = atoi(as_string()); break; + default : break; + } + return n; +} + +bool TVariant::as_bool() const +{ + bool ok = false; + if (_type == _alfafld) + ok = strchr("XY", as_string()[0]) != NULL; + else + ok = as_int() != 0; + return ok; +} + +COLOR TVariant::as_color() const +{ + const unsigned long rgb = as_int(); + unsigned char r = XVT_COLOR_GET_RED(rgb); + unsigned char g = XVT_COLOR_GET_GREEN(rgb); + unsigned char b = XVT_COLOR_GET_BLUE(rgb); + return MAKE_COLOR(r, g, b); +} + +real TVariant::as_real() const +{ + if (_type == _realfld) + return *(real*)_ptr; + switch(_type) + { + case _alfafld: return real(as_string()); break; + case _longfld: return real(as_int()); break; + default : break; + } + return ZERO; +} + +bool TVariant::as_string(TString& tmp) const +{ + tmp.cut(0); + switch(_type) + { + case _alfafld: tmp = *(TString*)_ptr; break; + case _datefld: tmp = as_date().string(); break; + case _longfld: tmp << as_int(); break; + case _realfld: tmp = as_real().string(); break; + default: break; + } + return !is_null(); +} + +const TString& TVariant::as_string() const +{ + if (_type == _alfafld) + return *(TString*)_ptr; + TString& tmp = get_tmp_string(); + as_string(tmp); + return tmp; +} + +void TVariant::convert_to(TFieldtypes ft) +{ + if (_type != ft) + { + switch (ft) + { + case _alfafld: set(as_string()); break; + case _datefld: set(as_date()); break; + case _longfld: set(as_int()); break; + case _realfld: set(as_real()); break; + default : set_null(); break; + } + } +} + +void TVariant::copy(const TVariant& var) +{ + switch (var._type) + { + case _datefld: set(var.as_date()); break; + case _longfld: set(var.as_int()); break; + case _realfld: set(var.as_real()); break; + case _alfafld: set(var.as_string()); break; + default : set_null(); break; + } +} + +int TVariant::compare(const TSortable& s) const +{ + const TVariant& var = (const TVariant&)s; + int cmp = 0; + switch (_type) + { + case _datefld: cmp = as_date() - var.as_date(); break; + case _longfld: cmp = as_int() - var.as_int(); break; + case _realfld: + { + const real n = as_real() - var.as_real(); + cmp = n.sign(); + } + break; + case _alfafld: cmp = as_string().compare(var.as_string()); break; + default : cmp = var.is_null() ? 0 : -1; + } + return cmp; +} + +TVariant& TVariant::add(const TVariant& var) +{ + switch (_type) + { + case _datefld: set(as_date() + var.as_int()); break; + case _longfld: set(as_int() + var.as_int()); break; + case _alfafld: *(TString*)_ptr << var.as_string(); break; + case _realfld: *(real*)_ptr += var.as_real(); break; + default: copy(var); break; + } + return *this; +} + +TVariant& TVariant::sub(const TVariant& var) +{ + switch (_type) + { + case _datefld: set(as_date() - var.as_int()); break; + case _longfld: + if (var.type() == _longfld) + { + set(as_int() - var.as_int()); + break; + } + // Fall down + default: + { + real n = as_real(); + n -= var.as_real(); + set(n); + } + break; + } + return *this; +} + +/////////////////////////////////////////////////////////// +// TTable name converter +/////////////////////////////////////////////////////////// + +class TTable_names : public TObject +{ + TAssoc_array _names; + TArray _ids; + long _filled; + +protected: + void fill(); + void add_file(int logic, const TString& table); + +public: + const TString& name(int logic_num); + int logic_num(const TString& name); + + TTable_names() : _filled(-1) { } +} _table_names; + +void TTable_names::add_file(int logic, const TString& table) +{ + TString8* id = new TString8; + id->format("%d", logic); + _names.add(table, id); + _ids.add(table, logic); +} + +void TTable_names::fill() +{ + if (_filled != prefix().get_codditta()) + { + FileDes dir; + CGetFile(LF_DIR, &dir, _nolock, NORDIR); + const int nfiles = (int)dir.EOD; + + 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(); + if (_names.objptr(n) == NULL) + add_file(logic, n); + } + _filled = prefix().get_codditta(); + } +} + +int TTable_names::logic_num(const TString& name) +{ + if (isdigit(name[0])) + { + int num = atoi(name); + if (name[name.len()-1] == '@') + num = -num; + return num; + } + + if (name[0] == '%' && name.len() == 4) + return LF_TABCOM; + + if (name.len() == 3) + return LF_TAB; + + TString* str = (TString*)_names.objptr(name); + if (str == NULL) + { + fill(); + str = (TString*)_names.objptr(name); + } + return str == NULL ? 0 : atoi(*str); +} + +const TString& TTable_names::name(int logic_num) +{ + TString* str = (TString*)_ids.objptr(logic_num); + if (str == NULL) + { + fill(); + str = (TString*)_ids.objptr(logic_num); + } + return str == NULL ? (const TString&)EMPTY_STRING : *str; +} + +const TString& logic2table(int logic_num) +{ return _table_names.name(logic_num); } + +int table2logic(const TString& name) +{ return _table_names.logic_num(name); } + + +/////////////////////////////////////////////////////////// +// Utility +/////////////////////////////////////////////////////////// + +static bool is_numeric(const char* str) +{ + if (str == NULL || *str == '\0' || *str == '0') + return false; // Se comincia per zero va preservato! + if (*str == '-') + { + str++; + if (*str <= ' ') + return false; + } + while (*str) + { + if (strchr("0123456789.", *str) == NULL) + return false; + str++; + } + return true; +} + +/////////////////////////////////////////////////////////// +// TRecordset +/////////////////////////////////////////////////////////// + +const TString& TRecordset::query_text() const +{ + return EMPTY_STRING; +} + +const TToken_string& TRecordset::sheet_head() const +{ + TToken_string head; + TToken_string tablefield(32, '.'); + for (unsigned int c = 0; c < columns(); c++) + { + const TRecordset_column_info& ci = column_info(c); + tablefield = ci._name; + int maxlen = 0; + FOR_EACH_TOKEN(tablefield, tok) + { + if (maxlen == 0) + head.add(tok); + else + head << '\n' << tok; + const int len = strlen(tok); + if (len > maxlen) + maxlen = len; + } + head << '@' << max(ci._width, maxlen); + if (ci._type == _wordfld || ci._type == _intfld || ci._type == _longfld || ci._type == _realfld) + head << 'R'; + } + + // Creo la stringa temporanea solo ora, altrimenti puo' essere sovrascritta! + TToken_string& h = get_tmp_string(); + h = head; + return h; +} + +bool TRecordset::save_as_html(const char* path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + ofstream out(path); + out << "" << endl; + out << "" << endl; + out << "" << endl; + out << " " << endl; + + out << " "; + for (unsigned int c = 0; c < columns(); c++) + { + const TRecordset_column_info& ci = column_info(c); + TToken_string header(ci._name, '.'); + TString str; + FOR_EACH_TOKEN(header, tok) + { + if (str.not_empty()) + str << "
"; + str << tok; + } + out << " " << endl; + } + out << " " << endl; + + TString val; + for (TRecnotype n = 0; n < items(); n++) + { + move_to(n); + pi.addstatus(1); + if (pi.iscancelled()) + break; + out << " " << endl; + for (unsigned int c = 0; c < columns(); c++) + { + const TRecordset_column_info& ci = column_info(c); + out << " "; + get(c).as_string(val); + if (!val.blank()) + { + val.rtrim(); + out << val; + } + out << " " << endl; + } + out << " " << endl; + } + + out << "

" << path << "

" << str << "
" << endl; + out << "" << endl; + out << "" << endl; + + return !pi.iscancelled(); +} + +bool TRecordset::save_as_silk(const char* path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + + ofstream out(path); + out << "ID;PWXL;N;E" << endl; + + for (unsigned int c = 0; c < columns(); c++) + { + const TRecordset_column_info& ci = column_info(c); + out << "C;Y1;X" << (c+1) << ";K\"" << ci._name << '"' << endl; + } + + TString val; + for (TRecnotype n = 0; n < items(); n++) + { + move_to(n); + pi.addstatus(1); + if (pi.iscancelled()) + break; + for (unsigned int c = 0; c < columns(); c++) + { + out << "C;Y" << (n+2) << ";X" << (c+1) << ";K\""; + get(c).as_string(val); + if (!val.blank()) + { + val.rtrim(); + val.replace('"', '\''); + out << val; + } + out << '"' << endl; + } + } + out << "E" << endl; + + return !pi.iscancelled(); +} + +bool TRecordset::save_as_text(const char* path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + + ofstream out(path); + TString val; + for (TRecnotype n = 0; n < items(); n++) + { + move_to(n); + for (unsigned int c = 0; c < columns(); c++) + { + if (c > 0) + out << '\t'; + get(c).as_string(val); + if (!val.blank()) + { + val.rtrim(); + out << val; + } + } + out << endl; + pi.addstatus(1); + if (pi.iscancelled()) + break; + } + + return !pi.iscancelled(); +} + +bool TRecordset::save_as_campo(const char* path) +{ + TProgind pi(items(), TR("Esportazione in corso..."), true, true); + ofstream out(path); + + out << "[Head]" << endl; + out << "Version=0"; + + for (unsigned int c = 0; c < columns(); c++) + { + const TRecordset_column_info& ci = column_info(c); + if ((c % 8) == 0) + out << endl << "Fields="; + else + out << '|'; + out << ci._name; + } + out << endl << endl << "[Data]" << endl; + + TString val; + for (TRecnotype n = 0; n < items(); n++) + { + move_to(n); + for (unsigned int c = 0; c < columns(); c++) + { + if (c > 0) + out << '|'; + get(c).as_string(val); + if (!val.blank()) + { + val.rtrim(); + out << val; + } + } + out << endl; + pi.addstatus(1); + if (pi.iscancelled()) + break; + } + return !pi.iscancelled(); +} + +bool TRecordset::save_as(const char* path, TRecordsetExportFormat fmt) +{ + if (fmt == fmt_unknown) + { + TString ext; + xvt_fsys_parse_pathname(path, NULL, NULL, NULL, ext.get_buffer(), NULL); + ext.lower(); + if (ext.starts_with("htm")) + fmt = fmt_html; else + if (ext == "xls" || ext == "slk") + fmt = fmt_silk; + } + bool ok = false; + switch (fmt) + { + case fmt_html : ok = save_as_html(path); break; + case fmt_silk : ok = save_as_silk(path); break; + case fmt_campo: ok = save_as_campo(path); break; + default : ok = save_as_text(path); break; + } + + return ok; +} + +int TRecordset::find_column(const char* column_name) const +{ + for (int i = columns()-1; i >= 0; i--) + { + const TRecordset_column_info& info = column_info(i); + if (info._name == column_name) + break; + } + return i; +} + +const TVariant& TRecordset::get(const char* column_name) const +{ + if (*column_name == '#') + { + if (variables().items() > 0) + { + const TVariant& var = get_var(column_name); + if (!var.is_null()) + return var; + } + column_name++; + } + + char* colon = strchr(column_name, ':'); + if (colon != NULL) + { + *colon = '\0'; + const int i = find_column(column_name); + *colon = ':'; + if (i >= 0) + { + const TString& str = get(i).as_string(); + TString subfield; subfield << (colon+1) << '='; + int s = str.find(subfield); + if (s == 0 || (s > 0 && str[s-1] < ' ')) + { + static TVariant var; + s += subfield.len(); + const int e = str.find('\n', s); + var.set(str.sub(s, e)); + return var; + } + } + } + else + { + const int i = find_column(column_name); + if (i >= 0) + return get(i); + } + + return NULL_VARIANT; +} + +const TVariant& TRecordset::get_var(const char* name) const +{ + const TVariant* var = (const TVariant*)_var.objptr(name); + return var != NULL ? *var : NULL_VARIANT; +} + +bool TRecordset::set_var(const char* name, const TVariant& var, bool create) +{ + bool ok = false; + TVariant* old = (TVariant*)_var.objptr(name); + if (old != NULL) + { + *old = var; + ok = true; + } + else + { + if (create) + { + _var.add(name, var); + _varnames.add(name); + ok = true; + } + } + return ok; +} + +bool is_var_separator(char c) +{ + if (isspace(c)) + return true; + return strchr("<=>,", c) != NULL; +} + +// Cerca le variabili nel testo SQL: +// Una variabile comincia per # ed e' composta da soli caratteri alfanumerici. +// Prima del simbolo # e dopo il nome della variabile deve esserci un separatore o blank +void TRecordset::find_and_reset_vars() +{ + _var.destroy(); + _varnames.destroy(); + + const TString& sql = query_text(); + int diesis = sql.find('#'); // cerco il primo # + for ( ; diesis > 0; diesis = sql.find('#', diesis+1)) // Cerco tutti i # + { + if (is_var_separator(sql[diesis-1])) // Controllo che ci sia un separatore prima del # + { + int i = diesis+1; + for ( ; sql[i] && (isalnum(sql[i]) || sql[i] == '_'); i++); + if (i > diesis+1) + { + const TString& name = sql.sub(diesis, i); + set_var(name, NULL_VARIANT, true); + } + } + } +} + +void TRecordset::parsed_text(TString& sql) const +{ + sql = query_text(); + const bool vars = ((TSQL_recordset*)this)->ask_variables(false); + if (vars) // Se ci sono variabili faccio le sostituzioni + { + const TString_array& names = variables(); + FOR_EACH_ARRAY_ROW(names, i, name) // Scandisco tutte le variabili + { + TVariant var = get_var(*name); + int pos = sql.find(*name); + for ( ; pos > 0; pos = sql.find(*name, pos+1)) + { + const TString& after = sql.mid(pos+name->len()); + sql.cut(pos); + TString s = var.as_string(); + if ((var.is_string() && s[0] != '\'') || var.is_null()) + { + s.insert("'"); + s << '\''; + } + sql << s << after; + } + } + } +} + +bool ask_variable(const char* name, TVariant& var) +{ + TMask m("Richiesta variabile", 1, 52, 4); + m.add_static(-1, 0, name, 1, 0); + m.add_string(101, 0, "", 1, 1, 80, "", 50); + m.add_button(DLG_OK, 0, "", -12, -1, 10, 2); + m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2); + m.set(101, var.as_string()); + const bool ok = m.run() == K_ENTER; + if (ok) + { + const TString& str = m.get(101); + if (is_numeric(str)) + var = real(str); + else + var = str; + } + return ok; +} + +bool TRecordset::ask_variables(bool all) +{ + const bool ok = variables().items() > 0; + if (ok) // Se ci sono variabili faccio le sostituzioni + { + FOR_EACH_ARRAY_ROW(_varnames, i, name) + { + TVariant var = get_var(*name); + if (var.is_null() || all) + { + ask_variable(*name, var); + if (var.is_null()) + var.set(""); // Mi serve assolutamente un valore! + + set_var(*name, var); + } + } + } + return ok; +} + +/////////////////////////////////////////////////////////// +// Utility +/////////////////////////////////////////////////////////// + +bool select_custom_file(TFilename& path, const char* ext) +{ + TFilename custom = firm2dir(-1); + custom.add("custom"); + if (!custom.exist()) + xvt_fsys_mkdir(custom); + + path = custom; + path.add("*"); + path.ext(ext); + + TString_array files; + list_files(path, files); + + TArray_sheet sheet(-1, -1, 78, 20, TR("Selezione"), TR("Nome@20|Descrizione@50")); + + TString str; + FOR_EACH_ARRAY_ROW(files, i, row) + { + + TXmlItem item; + if (item.Load(*row)) + { + TToken_string* riga = new TToken_string; + path = *row; + path = path.name(); + path.ext(""); + riga->add(path); + + const TXmlItem* desc = item.FindFirst("description"); + str = *row; + if (desc != NULL) + desc->GetEnclosedText(str); + riga->add(str); + + sheet.add(riga); + } + } + + const bool ok = sheet.run() == K_ENTER; + if (ok) + { + path = custom; + path.add(sheet.row(-1).get(0)); + path.ext(ext); + } + return ok; +} + + +/////////////////////////////////////////////////////////// +// Private interface +/////////////////////////////////////////////////////////// + +#include "../sqlite/sqlite.h" + + +class TSQLite : public TObject +{ + sqlite* _handle; + TFilename _currdb; + +protected: + TVariant& get_sql_value(const TRectype& curr, const RecFieldDes& fd, TVariant& tmp) const; + + void build_curr_path(TFilename& name) const; + void test_path(); + + bool esporta(const TRectype& rec, ostream& sql) const; + bool create_dbf_times(); + long get_dbf_time(const TString& table); + bool set_dbf_time(const TString& table, long last); + bool import(int logicnum); + +public: + sqlite* open(const char* fname = NULL); + bool exec(const char* sql, sqlite_callback callback = NULL, void* jolly = NULL, bool show_error = true); + void close(); + + bool exists(const char* table); + bool parse_select_from(const char* szSql); + + TSQLite(); + virtual ~TSQLite(); +} _TheDataBase; + +void get_sql_directory(TFilename& name) +{ + name = firm2dir(-1); + name.add("sql"); + if (!name.exist()) + make_dir(name); +} + +void TSQLite::build_curr_path(TFilename& name) const +{ + TString16 firm; firm.format("%05ldA.sql", prefix().get_codditta()); + get_sql_directory(name); + name.add(firm); +} + +sqlite* TSQLite::open(const char* fname) +{ + close(); + _currdb = fname; + char* errmsg = NULL; + _handle = sqlite_open(_currdb, 0, &errmsg); + if (errmsg != NULL) + { + error_box(errmsg); + sqlite_freemem(errmsg); + } + + create_dbf_times(); + + return _handle; +} + +void TSQLite::test_path() +{ + TFilename n; + build_curr_path(n); + if (n != _currdb) + open(n); +} + +bool TSQLite::exec(const char* sql, sqlite_callback callback, void* jolly, bool show_error) +{ + if (_handle == NULL) + test_path(); + + TWait_cursor hourglass; + char* errmsg = NULL; + const int rc = sqlite_exec(_handle, sql, callback, jolly, &errmsg); + if (errmsg != NULL) + { + if (show_error) + { + TString msg; + msg << sql; + msg.cut(128); + msg << '\n' << errmsg; + error_box(msg); + } + sqlite_freemem(errmsg); + } + return rc == SQLITE_OK; +} + +void TSQLite::close() +{ + if (_handle != NULL) + { + sqlite_close(_handle); + _handle = NULL; + } +} + +const char* const DBF_TIMES_TABLE = "DBF_TIMES"; + +bool TSQLite::create_dbf_times() +{ + bool ok = exists(DBF_TIMES_TABLE); + if (!ok) + { + TString sql; + sql << "CREATE TABLE " << DBF_TIMES_TABLE << " (name TEXT,time NUMERIC);\n" + << "CREATE UNIQUE INDEX " << DBF_TIMES_TABLE << "_1 ON " << DBF_TIMES_TABLE << " (name);"; + ok = exec(sql); + } + return ok; +} + +bool TSQLite::set_dbf_time(const TString& table, long last) +{ + TString sql; + sql << "REPLACE INTO " << DBF_TIMES_TABLE << " VALUES(" + << '\'' << table << "','" << last << "');"; + return exec(sql); +} + +static int dbf_time_callback(void* jolly, int argc, char** argv, char** columns) +{ + long& last = *(long*)jolly; + last = atol(argv[0]); + return SQLITE_OK; +} + +long TSQLite::get_dbf_time(const TString& table) +{ + TString sql; + sql << "SELECT time FROM " << DBF_TIMES_TABLE << " WHERE name='" << table << "';"; + long last = 0; + exec(sql, dbf_time_callback, &last); + return last; +} + +TVariant& TSQLite::get_sql_value(const TRectype& curr, const RecFieldDes& fd, TVariant& tmp) const +{ + switch (fd.TypeF) + { + case _realfld : tmp.set(curr.get_real(fd.Name)); break; + case _intfld : + case _longfld : + case _wordfld : + case _intzerofld : + case _longzerofld: tmp.set(curr.get_long(fd.Name)); break; + case _datefld : + { + const TDate date = curr.get_date(fd.Name); + tmp.set(date.date2ansi()); + } + break; + case _boolfld : tmp.set(curr.get_bool(fd.Name)); break; + case _memofld: + { + TString memo = curr.get(fd.Name); + memo.replace('\n', char(0xB6)); // Simbolo di paragrafo + tmp.set(memo); + } + break; + default : tmp.set(curr.get(fd.Name)); break; + } + return tmp; +} + +static int exists_callback(void *jolly, int argc, char **argv, char **azColName) +{ + bool& yes = *(bool*)jolly; + yes = argc > 0; + return SQLITE_OK; +} + +bool TSQLite::exists(const char* table) +{ + TString sql; + sql << "SELECT name FROM sqlite_master WHERE (type='table')AND(name='" << table << "');"; + bool yes = false; + exec(sql, exists_callback, &yes, false); + return yes; +} + +bool TSQLite::esporta(const TRectype& rec, ostream& sql) const +{ + const RecDes& rd = *rec.rec_des(); + TVariant tmp; + for (int i = 0; i < rd.NFields; i++) + { + if (i > 0) sql << '\t'; + get_sql_value(rec, rd.Fd[i], tmp); + sql << tmp.as_string(); + } + sql << '\n'; + return true; +} + +bool TSQLite::import(int logicnum) +{ + const TString& table = logic2table(logicnum); + + long last = get_dbf_time(table); + if (logicnum >= LF_USER) // Dummy test + { + TLocalisamfile file(logicnum); + if (!file.is_changed_since(last)) + return true; + } + + const RecDes& rd = prefix().get_recdes(logicnum); + + TString sql; + if (exists(table)) + { + // Drop old table + sql.cut(0) << "DROP TABLE "<< table << ';'; + exec(sql); + } + + // Create new table + sql.cut(0) << "CREATE TABLE "<< table << "\n("; + for (int i = 0; i < rd.NFields; i++) + { + if (i > 0) sql << ','; + sql << rd.Fd[i].Name << ' '; + switch (rd.Fd[i].TypeF) + { + case _alfafld: sql << "TEXT"; break; + case _memofld: sql << "BLOB"; break; + case _datefld: sql << "DATE"; break; + default : sql << "NUMERIC"; break; + } + } + sql << ");"; + if (!exec(sql)) + return false; + + // Creazione indici + for (int index = 0; index < rd.NKeys; index++) + { + sql.cut(0) << "CREATE INDEX " << table << '_' << (index+1) << " ON "<< table << "\n("; + const KeyDes& kd = rd.Ky[index]; + for (int k = 0; k < kd.NkFields; k++) + { + if (k > 0) sql << ','; + const int ndx = kd.FieldSeq[k] % MaxFields; + sql << rd.Fd[ndx].Name; + } + sql << ");"; + exec(sql); + } + + TRelation rel(logicnum); + TCursor cur(&rel); + const TRecnotype items = cur.items(); + cur.freeze(); + const TRectype& curr = rel.curr(); + + TString msg; + msg << TR("Esportazione tabella") << ' ' << table; + msg << ": " << items << ' ' << TR("righe"); + TProgind pi(items, msg, true, true); + + TFilename tmp; tmp.tempdir(); tmp.add("sql.txt"); + ofstream txt(tmp, ios::binary); + + for (cur = 0; cur.pos() < items; ++cur) + { + esporta(curr, txt); + pi.addstatus(1); + if (pi.iscancelled()) + break; + } + + txt.close(); + + msg << '\n' << TR("Importazione tabella") << ' ' << table; + msg << ": " << items << ' ' << TR("righe"); + pi.set_text(msg); + + sql.cut(0) << "COPY " << table << " FROM '" << tmp << "';"; + if (exec(sql)) + set_dbf_time(table, last); // Aggiorna ora di ultima modifica + + ::remove(tmp); + + return true; +} + +bool TSQLite::parse_select_from(const char* szSql) +{ + test_path(); + + TString sql(szSql); + sql.trim(); sql.upper(); + if (!sql.starts_with("SELECT")) + return false; + + const int from = sql.find("FROM"); + if (from < 0) + return false; + + const int where = sql.find("WHERE", from); + TToken_string tables(sql.sub(from+5, where), ','); + TString table; + FOR_EACH_TOKEN(tables, tok) + { + table = tok; + table.trim(); + for (int i = 0; table[i]; i++) + { + if (table[i] <= ' ' || table[i] == ';') + { table.cut(i); break; } + } + const int logicnum = table2logic(table); + if (logicnum > 0) + import(logicnum); + } + + return true; +} + +TSQLite::TSQLite() : _handle(NULL) +{ } + +TSQLite::~TSQLite() +{ + close(); +} + +/////////////////////////////////////////////////////////// +// TSQL_recordset +/////////////////////////////////////////////////////////// + +void TSQL_recordset::reset() +{ + _items = 0; + _pagesize = 512; + _page.destroy(); + _column.destroy(); +} + +int TSQL_recordset::on_get_items(int argc, char** values, char** columns) +{ + if (_column.items() == 0) + { + for (int i = 0; i < argc; i++) + { + TRecordset_column_info* info = new TRecordset_column_info; + info->_name = columns[i]; + info->_width = 1; + info->_type = _alfafld; + + const char* fldtype = columns[argc+i]; + if (fldtype != NULL) + { + if (xvt_str_compare_ignoring_case(fldtype, "DATE") == 0) + { + info->_type = _datefld; + info->_width = 10; + } else + if (xvt_str_compare_ignoring_case(fldtype, "NUMERIC") == 0) + { + info->_type = _realfld; + } else + if (xvt_str_compare_ignoring_case(fldtype, "BLOB") == 0) + { + info->_type = _memofld; + info->_width = 50; + } + } + _column.add(info); + } + } + if (_items < _pagesize) + { + for (int i = 0; i < argc; i++) if (values[i] && *values[i]) + { + TRecordset_column_info& info = (TRecordset_column_info&)_column[i]; + if (info._type == _alfafld || info._type == _realfld) + { + const int len = strlen(values[i]); + if (len > info._width) + info._width = len; + } + } + } + + _items++; + return SQLITE_OK; +} + +static int query_get_items(void* jolly, int argc, char** values, char** columns) +{ + TSQL_recordset* q = (TSQL_recordset*)jolly; + return q->on_get_items(argc, values, columns); +} + +bool TSQL_recordset::ask_variables(bool all) +{ + const bool ok = TRecordset::ask_variables(all); + if (ok) + _page.destroy(); + return ok; +} + +TRecnotype TSQL_recordset::items() const +{ + if (_items == 0) + { + TString sql; parsed_text(sql); + TPerformance_profiler prof("SQL query"); + _TheDataBase.exec("PRAGMA show_datatypes = ON;", NULL, NULL); + _TheDataBase.exec(sql, query_get_items, (TSQL_recordset*)this); + _TheDataBase.exec("PRAGMA show_datatypes = OFF;", NULL, NULL); + } + return _items; +} + +unsigned int TSQL_recordset::columns() const +{ + if (_column.items() == 0) + items(); + return _column.items(); +} + +const TRecordset_column_info& TSQL_recordset::column_info(unsigned int c) const +{ + return (const TRecordset_column_info&)_column[c]; +} + +// Funzione chiamata per riempire la pagina corrente delle righe della query +int TSQL_recordset::on_get_rows(int argc, char** values, char** columns) +{ + TArray* a = new TArray; + for (int c = 0; c < argc; c++) + { + TVariant* var = new TVariant; + switch (column_info(c)._type) + { + case _alfafld: + var->set(values[c]); + break; + case _memofld: + { + TFixed_string memo(values[c]); + memo.replace(char(0xB6), '\n'); + var->set(memo); + } + break; + case _datefld: + var->set(TDate(values[c])); + break; + default: + var->set(real(values[c])); + break; + } + a->add(var, c); + } + _page.add(a); + return SQLITE_OK; +} + +static int query_get_rows(void* jolly, int argc, char** values, char** columns) +{ + TSQL_recordset* rs = (TSQL_recordset*)jolly; + return rs->on_get_rows(argc, values, columns); +} + +bool TSQL_recordset::move_to(TRecnotype n) +{ + _current_row = n; + if (n < 0 || n >= items()) + { + _page.destroy(); // Forza rilettura la prossiva volta + _first_row = 0; + return false; + } + + if (n < _first_row || n >= _first_row+_page.items()) + { + TString sql; parsed_text(sql); + if (sql.find("LIMIT ") < 0) + { + const int semicolon = sql.rfind(';'); + if (semicolon >= 0) + sql.cut(semicolon); + sql.trim(); + _page.destroy(); + if (n >= _pagesize) + _first_row = n-_pagesize/8; // Prendo qualche riga dalla pagina precedente, per velocizzare il pagina su + else + _first_row = n; + sql << "\nLIMIT " << _pagesize << " OFFSET " << _first_row << ';'; + } + _TheDataBase.exec(sql, query_get_rows, this); + } + + return true; +} + +const TArray* TSQL_recordset::row(TRecnotype n) +{ + const TArray* a = NULL; + if (move_to(n)) + a = (const TArray*)_page.objptr(n-_first_row); + return a; +} + +const TVariant& TSQL_recordset::get(unsigned int c) const +{ + const TArray* a = (const TArray*)_page.objptr(_current_row-_first_row); + if (a != NULL) + { + const TVariant* s = (const TVariant*)a->objptr(c); + if (s != NULL) + return *s; + } + return NULL_VARIANT; +} + +void TSQL_recordset::set(const char* sql) +{ + reset(); + _sql = sql; + if (_sql.find("SELECT") >= 0 || _sql.find("select") >= 0) + { + _TheDataBase.parse_select_from(_sql); + find_and_reset_vars(); + } +} + +TSQL_recordset::TSQL_recordset(const char* sql) +{ + set(sql); +} + +/////////////////////////////////////////////////////////// +// TCursor_parser +/////////////////////////////////////////////////////////// + +class TCursor_parser +{ + istream& _instr; + TArray& _column; + + TString _pushed; + TString _token; + + TRelation* _relation; + TCursor* _cursor; + +protected: + const TString& pop(); + void push(); + void add_column_info(const char* table, const TRectype& rec); + + void parse_sortexpr(TToken_string& se); + void parse_filter(TToken_string& filter); + void parse_region(TRectype& rec); + void parse_join_param(TRelation* rel, const TString& j, int to); + void parse_join(); + void parse_sortedjoin(); + +public: + TRelation* get_relation() { return _relation; } + TCursor* get_cursor() { return _cursor; } + + TCursor_parser(istream& instr, TArray& column); +}; + +const TString& TCursor_parser::pop() +{ + if (_pushed.not_empty()) + { + _token = _pushed; + _pushed.cut(0); + } + else + { + _token.cut(0); + + _instr.eatwhite(); + if (_instr.eof()) + return _token; + + char c; + _instr.get(c); + while (!isspace(c) && c != EOF) + { + _token << c; + _instr.get(c); + } + } + return _token; +} + +void TCursor_parser::push() +{ + CHECK(_pushed.empty(), "Repushing?"); + _pushed = _token; +} + +void TCursor_parser::add_column_info(const char* table, const TRectype& rec) +{ + for (int i = 0; i < rec.items(); i++) + { + TRecordset_column_info* info = new TRecordset_column_info; + const char* name = rec.fieldname(i); + info->_name << table << '.' << name; + info->_type = rec.type(name); + switch (info->_type) + { + case _datefld: info->_width = 10; break; + case _memofld: info->_width = 50; break; + default : info->_width = rec.length(name); break; + } + _column.add(info); + } +} + +void TCursor_parser::parse_sortexpr(TToken_string& se) +{ + const char sep = se.separator(); + se.separator(' '); + _instr.getline(se.get_buffer(), se.size()); + se.strip_d_spaces(); + se.replace(' ', sep); + se.separator(sep); +} + +void TCursor_parser::parse_filter(TToken_string& filter) +{ + const TString& str = pop(); + while (str.find('=') > 0) + { + filter.add(str); + pop(); + } + push(); +} + +void TCursor_parser::parse_region(TRectype& rec) +{ + TString16 field; + TString value; + while (true) + { + const TString& ass = pop(); + const int equal = ass.find('='); + if (equal > 0) + { + field = ass.left(equal); + value = ass.mid(equal+1); + if (value[0] == '"' || value[0] == '\'') + { + value.rtrim(1); + value.ltrim(1); + } + rec.put(field, value); + } + else + break; + } + push(); +} + + +void TCursor_parser::parse_join_param(TRelation* rel, const TString& j, int to) +{ + int key = 1; + const TString& tok = pop(); + if (tok.starts_with("KE")) + { + pop(); + key = atoi(tok); + } + else + push(); + + int alias = 0; + pop(); + if (tok.starts_with("AL")) + { + pop(); + alias = atoi(tok); + } + else + push(); + + TToken_string exp(80); + pop(); + if (tok == "INTO") + { + parse_filter(exp); + } + if (exp.empty()) + yesnofatal_box("JOIN senza espressioni INTO"); + + const int logicnum = table2logic(j); + if (logicnum != LF_TAB && logicnum != LF_TABCOM) + rel->add(logicnum, exp, key, to, alias); // join file + else + rel->add(j, exp, key, to, alias); // join table + + TString16 tabname; + if (alias > 0) + tabname << alias << '@'; + else + tabname = j; + const TRectype& rec = rel->curr(logicnum); + add_column_info(tabname, rec); +} + +void TCursor_parser::parse_join() +{ + const TString j = pop(); // File or table + + int to = 0; + const TString& tok = pop(); + if (tok == "TO") // TO keyword + { + pop(); + to = table2logic(tok); + } + else + push(); + + parse_join_param(_relation, j, to); +} + +void TCursor_parser::parse_sortedjoin() +{ + TToken_string filter,sortexp; + const TString j = pop(); // File or table + const TString& tok = pop(); + if (tok == "BY" ) + { + parse_sortexpr(sortexp); + } + else + push(); + + pop(); + if (tok.starts_with("FI") || tok.starts_with("SE")) + { + parse_filter(filter); + } + else + push(); + + TRelation* sortrel = new TRelation(table2logic(j)); + while (true) + { + pop(); + if (tok.empty() || tok.starts_with("JO")) + { + push(); + break; + } + + if (tok.starts_with("US")) // USING keyword + { + const TString subj = pop(); // File or table + parse_join_param(sortrel, subj, table2logic(j)); + } + } + + int to = 0; + pop(); + if (tok == "TO") // TO keyword + { + pop(); + to = table2logic(tok); + } + else + push(); + + int key = 1; + pop(); + if (tok.starts_with("KE")) + { + pop(); + key = atoi(tok); + } + else + push(); + + int alias = 0; + pop(); + if (tok.starts_with("AL")) + { + pop(); + alias = atoi(tok); + } + else + push(); + + TToken_string exp(80); + if (pop() == "INTO") + { + pop(); + while (tok.find('=') > 0) + { + exp.add(tok); + pop(); + } + } + push(); + + TSortedfile *sf= new TSortedfile(atoi(j),sortrel,sortexp,filter,key); + _relation->add((TLocalisamfile *)sf, exp, key, to, alias, false); // join table + + TString16 tabname = j; + if (alias > 0) + tabname.cut(0) << alias << '@'; + add_column_info(tabname, sf->curr()); +} + +TCursor_parser::TCursor_parser(istream& instr, TArray& col) + : _instr(instr), _column(col), _relation(NULL), _cursor(NULL) +{ + _column.destroy(); + const TString& tok = pop(); + if (!tok.starts_with("US")) + push(); + + pop(); + if (tok.blank()) + return; + + const int logicnum = table2logic(tok); + const char* tab = NULL; + + if (logicnum != LF_TAB && logicnum != LF_TABCOM) + _relation = new TRelation(logicnum); + else + _relation = new TRelation(tok); + add_column_info(tok, _relation->curr()); + + int key = 1; // key number + pop(); + if (tok.starts_with("KE")) + { + pop(); + key = atoi(tok); + } + else + push(); + + pop(); + if (tok.starts_with("BY")) // "sort BY": user-defined sort + { + TToken_string ordexpr(256); + parse_sortexpr(ordexpr); + _cursor = new TSorted_cursor(_relation, ordexpr,"", key); + } + else + push(); + + pop(); + if (tok.starts_with("FI") || tok.starts_with("SE")) + { + TToken_string filter; + parse_filter(filter); + if (_cursor == NULL) + _cursor = new TCursor(_relation, filter, key); + else + _cursor->setfilter(filter); + } + else + push(); + + if (_cursor == NULL) + _cursor = new TCursor(_relation, "", key); + + TRectype rec_start(_relation->curr()); + TRectype rec_stop(_relation->curr()); + + pop(); + if (tok.starts_with("FR")) + parse_region(rec_start); + else + push(); + pop(); + if (tok.starts_with("TO")) + parse_region(rec_stop); + else + push(); + if (!rec_start.empty() || !rec_stop.empty()) + _cursor->setregion(rec_start, rec_stop, 0x2); + + while (true) + { + pop(); + if (tok.starts_with("JO")) + parse_join(); else + if (tok.starts_with("SO")) + parse_sortedjoin(); + else + break; + } + push(); + + if (_relation->items() == 0) // Non ci sono anche tabelle collegate + { + FOR_EACH_ARRAY_ITEM(_column, i, obj) + { + TRecordset_column_info* info = (TRecordset_column_info*)obj; + const int arrow = info->_name.find('.'); + if (arrow > 0) + info->_name = info->_name.mid(arrow+1); + } + } +} + +/////////////////////////////////////////////////////////// +// TISAM_recordset +/////////////////////////////////////////////////////////// + +TVariant& TISAM_recordset::get_tmp_var() const +{ + static TArray _page; // Variants to be returned by get + static int _next_var = 0; // Index of next variant to be returned + + if (_next_var >= 32) + _next_var = 0; + TVariant* var = (TVariant*)_page.objptr(_next_var); + if (var == NULL) + { + var = new TVariant; + _page.add(var, _next_var); + } + _next_var++; + return *var; +} + +const TVariant& TISAM_recordset::get(int logic, const char* fldname) const +{ + TString80 name = fldname; + TString16 subfield; + int from = 1, to = 0; + + const int open_bracket = name.find('['); + if (open_bracket > 0) + { + sscanf((const char*)name + open_bracket, "[%d,%d]", &from, &to); + name.cut(open_bracket); + } + + const int colon = name.find(':'); + if (colon > 0) + { + subfield = name.mid(colon+1); + name.cut(colon); + } + + const TRectype& rec = _relation->curr(logic); + const TFieldtypes ft = rec.type(name); + + if (ft == _nullfld) + { + if (logic == LF_DOC) // Proviamo la magia + { + subfield = name; + name = "G1"; + } + else + return NULL_VARIANT; + } + + TVariant& var = get_tmp_var(); + switch (ft) + { + case _datefld: var.set(rec.get_date(name)); break; + case _realfld: var.set(rec.get_real(name)); break; + case _intfld : + case _longfld: + case _wordfld: var.set(rec.get_long(name)); break; + default : var.set(rec.get(name)); break; + } + + if (subfield.not_empty()) + { + subfield << '='; + const TString& str = var.as_string(); + int s = str.find(subfield); + if (s == 0 || (s > 0 && str[s-1] < ' ')) + { + s += subfield.len(); + const int e = str.find('\n', s); + var.set(str.sub(s, e)); + } + } + + if (to >= from) + var.set(var.as_string().sub(from-1, to)); + + return var; +} + +const TVariant& TISAM_recordset::get(size_t c) const +{ + const TRecordset_column_info* info = (const TRecordset_column_info*)_column.objptr(c); + if (info != NULL) + { + int logic = 0; + const char* field = info->_name; + const int dot = info->_name.find('.'); + if (dot > 0) + { + logic = table2logic(info->_name.left(dot)); + field += dot+1; + } + return get(logic, field); + } + return NULL_VARIANT; +} + +const TVariant& TISAM_recordset::get(const char* name) const +{ + const TFixed_string fldname(name); + + int table_end = fldname.find('.'); + int field_start = table_end+1; + if (table_end < 0) + { + table_end = fldname.find('-'); + if (table_end > 0) + field_start = table_end+2; + } + int logic = 0; + const char* field = name; + if (table_end > 0) + { + logic = table2logic(fldname.left(table_end)); + field += field_start; + } + return get(logic, field); +} + +const TRecordset_column_info& TISAM_recordset::column_info(size_t i) const +{ + return (const TRecordset_column_info&)_column[i]; +} + +TRecnotype TISAM_recordset::current_row() const +{ + return _cursor != NULL ? _cursor->pos() : -1; +} + +bool TISAM_recordset::ask_variables(bool all) +{ + bool ok = TRecordset::ask_variables(all); + if (ok) + reset(); + return ok; +} + +TRecnotype TISAM_recordset::items() const +{ + if (_cursor == NULL) + { + TString use; parsed_text(use); + TPerformance_profiler prof("ISAM query"); + TISAM_recordset* my = (TISAM_recordset*)this; + istrstream instr(use.get_buffer(), use.len()); + TCursor_parser parser(instr, my->_column); + + my->_relation = parser.get_relation(); + my->_cursor = parser.get_cursor(); + + if (_cursor != NULL) + { + _cursor->items(); + _cursor->freeze(); + } + } + + return _cursor != NULL ? _cursor->items() : 0; +} + +unsigned int TISAM_recordset::columns() const +{ + if (_cursor == NULL) + items(); + return _column.items(); +} + + +bool TISAM_recordset::move_to(TRecnotype pos) +{ + bool ok = _cursor != NULL; + if (ok) + { + *_cursor = pos; + ok = pos >= 0 && pos < items(); + } + return ok; +} + +void TISAM_recordset::reset() +{ + _column.destroy(); + if (_relation != NULL) + { + delete _relation; + _relation = NULL; + } + if (_cursor != NULL) + { + delete _cursor; + _cursor = NULL; + } +} + +void TISAM_recordset::set(const char* use) +{ + reset(); + _use = use; + find_and_reset_vars(); +} + +TISAM_recordset::TISAM_recordset(const char* use) + : _relation(NULL), _cursor(NULL) +{ + set(use); +} + +TISAM_recordset::~TISAM_recordset() +{ + reset(); +} + +/////////////////////////////////////////////////////////// +// TRecordset_sheet +/////////////////////////////////////////////////////////// + +void TRecordset_sheet::get_row(long r, TToken_string& row) +{ + row.separator('\t'); + row.cut(0); + if (_query.move_to(r)) + { + TString str; + for (unsigned int c = 0; c < _query.columns(); c++) + { + _query.get(c).as_string(str); + row.add(str); + } + } +} + +long TRecordset_sheet::get_items() const +{ + return _query.items(); +} + +TRecordset_sheet::TRecordset_sheet(TRecordset& query) + : TSheet(-1, -1, -2, -4, "Query", query.sheet_head()), _query(query) +{ +} + +/////////////////////////////////////////////////////////// +// TPerformance_profiler +/////////////////////////////////////////////////////////// + +TPerformance_profiler::TPerformance_profiler(const char* desc) + : _desc(desc) +{ +#ifdef DBG + _start = clock(); + + TString80 msg; + msg << "Profiling " << desc << "..."; + statbar_set_title(TASK_WIN, msg); + + while (true) + { + const clock_t clk = clock(); + if (clk != _start) + { + _start = clk; + break; + } + } +#endif +} + +void TPerformance_profiler::show() const +{ +#ifdef DBG + const double s = double(clock() - _start) / CLOCKS_PER_SEC; + + int hour = 0, min = 0; + int sec = int(s); + const int cent = int((s - sec)*100); + + if (sec >= 3600) + { + hour = sec / 3600; + sec -= hour * 3600; + } + if (sec >= 60) + { + min = sec / 60; + sec -= min * 60; + } + + TString80 msg = _desc; + msg.format("%s %02d:%02d:%02d.%02d", (const char*)_desc, hour, min, sec, cent); + statbar_set_title(TASK_WIN, msg); +#endif +} + +TPerformance_profiler::~TPerformance_profiler() +{ + show(); +} diff --git a/include/recset.h b/include/recset.h new file mode 100755 index 000000000..4b9c1ab45 --- /dev/null +++ b/include/recset.h @@ -0,0 +1,239 @@ +#ifndef __RECORDSET_H +#define __RECORDSET_H + +#ifndef __ASSOC_H +#include +#endif + +#ifndef __RECTYPES_H +#include +#endif + +#ifndef __SHEET_H +#include +#endif + +struct TRecordset_column_info : public TObject +{ + TString _name; // Table.Column + int _width; + TFieldtypes _type; +}; + +/////////////////////////////////////////////////////////// +// TVariant +/////////////////////////////////////////////////////////// + +class TVariant : public TSortable +{ + TFieldtypes _type; + void* _ptr; + +protected: + virtual TObject* dup() const { return new TVariant(*this); } + void copy(const TVariant& var); + +public: + TFieldtypes type() const { return _type; } // Internal use only + bool is_string() const { return _type == _alfafld; } + bool is_empty() const; + bool is_null() const { return _type == _nullfld; } + bool is_zero() const; + void set_null(); + + void set(const char* str); + void set(const real& r); + void set(const long n); + void set(const TDate& d); + + TVariant& operator=(const TVariant& var) { copy(var); return *this; } + TVariant& operator=(const char* str) { set(str); return *this; } + TVariant& operator=(const real& r) { set(r); return *this; } + TVariant& operator=(const long n) { set(n); return *this; } + TVariant& operator=(const TDate& d) { set(d); return *this; } + + const TString& as_string() const; + bool as_string(TString& str) const; + real as_real() const; + long as_int() const; + TDate as_date() const; + bool as_bool() const; + COLOR as_color() const; + + void convert_to(TFieldtypes ft); + + virtual int compare(const TSortable& s) const; + TVariant& add(const TVariant& var); + TVariant& sub(const TVariant& var); + + TVariant() : _type(_nullfld), _ptr(NULL) { } + TVariant(const char* str) : _type(_alfafld), _ptr(new TString(str)) { } + TVariant(const real& num) : _type(_realfld), _ptr(new real(num)) { }; + TVariant(const TDate& d) : _type(_datefld), _ptr(new TDate(d)) { }; + TVariant(long num) : _type(_longfld), _ptr((void*)num) { }; + TVariant(bool ok) : _type(_longfld), _ptr((void*)ok) { }; + TVariant(const TVariant& var) : _type(_nullfld), _ptr(NULL) { copy(var); } + virtual ~TVariant() { set_null(); } +}; + +extern const TVariant NULL_VARIANT; + +/////////////////////////////////////////////////////////// +// TRecordset +/////////////////////////////////////////////////////////// + +enum TRecordsetExportFormat { fmt_unknown, fmt_html, fmt_text, fmt_silk, fmt_campo }; + +class TRecordset : public TObject +{ + TAssoc_array _var; + TString_array _varnames; + +protected: + bool save_as_html(const char* path); + bool save_as_silk(const char* path); + bool save_as_text(const char* path); + bool save_as_campo(const char* path); + + void find_and_reset_vars(); + void parsed_text(TString& sql) const; + +public: // Absolutely needed methods + virtual TRecnotype items() const pure; + virtual bool move_to(TRecnotype pos) pure; + virtual TRecnotype current_row() const pure; + + virtual bool move_first() { return move_to(0); } + virtual bool move_prev() { return move_to(current_row()-1); } + virtual bool move_next() { return move_to(current_row()+1); } + virtual bool move_last() { return move_to(items()-1); } + virtual bool bof() const { return current_row() <= 0; } + virtual bool eof() const { return current_row() >= items(); } + + virtual unsigned int columns() const pure; + virtual const TRecordset_column_info& column_info(unsigned int column) const pure; + virtual const TVariant& get(unsigned int column) const pure; + + virtual const TString_array& variables() const { return _varnames; } + virtual const TVariant& get_var(const char* name) const; + virtual bool set_var(const char* name, const TVariant& var, bool create = false); + virtual bool ask_variables(bool all); + + virtual const TString& query_text() const; + virtual int find_column(const char* column_name) const; + virtual const TVariant& get(const char* column_name) const; + virtual const TToken_string& sheet_head() const; + + virtual bool save_as(const char* path, TRecordsetExportFormat fmt = fmt_unknown); +}; + +/////////////////////////////////////////////////////////// +// TSQL_recordset +/////////////////////////////////////////////////////////// + +class TSQL_recordset : public TRecordset +{ + TString _sql; + + TRecnotype _first_row, _pagesize, _items, _current_row; + TArray _column; + TArray _page; + +protected: + void reset(); + void parsed_sql_text(TString& sql) const; + +public: // TRecordset + virtual TRecnotype items() const; + virtual bool move_to(TRecnotype pos); + virtual TRecnotype current_row() const { return _current_row; } + virtual unsigned int columns() const; + virtual const TRecordset_column_info& column_info(unsigned int c) const; + virtual const TVariant& get(unsigned int column) const; + virtual bool ask_variables(bool all); + const TString& query_text() const { return _sql; } + +public: + void set(const char* sql); + + // Internal use only + virtual int on_get_items(int argc, char** values, char** columns); + virtual int on_get_rows(int argc, char** values, char** columns); + const TArray* row(TRecnotype n); + + TSQL_recordset(const char* sql); + virtual ~TSQL_recordset() { } +}; + +/////////////////////////////////////////////////////////// +// TISAM_recordset +/////////////////////////////////////////////////////////// + +class TISAM_recordset : public TRecordset +{ + TString _use; + + TRelation* _relation; + TCursor* _cursor; + TArray _column; // Column infos + +protected: + void reset(); + TVariant& get_tmp_var() const; + const TVariant& get(int logic, const char* field) const; + +public: + void set(const char* use); + virtual TRecnotype items() const; + virtual bool move_to(TRecnotype pos); + virtual TRecnotype current_row() const; + virtual unsigned int columns() const; + virtual const TRecordset_column_info& column_info(unsigned int c) const; + virtual const TVariant& get(unsigned int column) const; + virtual const TVariant& get(const char* column_name) const; + virtual bool ask_variables(bool all); + virtual const TString& query_text() const { return _use; } + + + TISAM_recordset(const char* use); + virtual ~TISAM_recordset(); +}; + +/////////////////////////////////////////////////////////// +// TRecordset_sheet +/////////////////////////////////////////////////////////// + +class TRecordset_sheet : public TSheet +{ + TRecordset& _query; + +protected: + virtual void get_row(long r, TToken_string& row); + virtual long get_items() const; + +public: + TRecordset_sheet(TRecordset& sql); +}; + +/////////////////////////////////////////////////////////// +// Utility +/////////////////////////////////////////////////////////// + +class TPerformance_profiler : public TObject +{ + TString _desc; + clock_t _start; + +public: + void show() const; + + TPerformance_profiler(const char* desc = ""); + ~TPerformance_profiler(); +}; + +bool select_custom_file(TFilename& path, const char* ext); +const TString& logic2table(int logic_num); +int table2logic(const TString& name); + +#endif + diff --git a/include/relapp.cpp b/include/relapp.cpp index 93545bf4f..53da952db 100755 --- a/include/relapp.cpp +++ b/include/relapp.cpp @@ -327,13 +327,13 @@ int TRelation_application::set_mode(int mode) switch(mode) { case MODE_QUERY: - t = "Ricerca"; break; + t = TR("Ricerca"); break; case MODE_MOD: - t = "Modifica"; break; + t = TR("Modifica"); break; case NO_MODE: - t = "Ricerca/Inserimento"; break; + t = TR("Ricerca/Inserimento"); break; case MODE_INS: - t = "Inserimento"; break; + t = TR("Inserimento"); break; default: break; } diff --git a/include/report.cpp b/include/report.cpp new file mode 100755 index 000000000..514cd454f --- /dev/null +++ b/include/report.cpp @@ -0,0 +1,2265 @@ +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////// +// TReport_font +/////////////////////////////////////////////////////////// + +XVT_FNTID TReport_font::get_xvt_font(const TWindow& win) const +{ + if (win.win() != _win_mapped) + { + int nSize = _size; + + if (win.win() != 883) // Non e' una stampa! + { + const int cpi = 120 / DEFAULT_FONT_SIZE; + const PNT pnt0 = win.log2dev(0,0); + const PNT pnt1 = win.log2dev(cpi*100,100); + const int ppi = pnt1.h - pnt0.h; + const TString emme(cpi, 'M'); + + int mi = 0, me = 0, ma = 2*(pnt1.v - pnt0.v); + int best = 0, best_error = 0; + while (mi <= ma) + { + me = (mi+ma)/2; + + XVT_FNTID fontid = xvt_font_create(); + xvt_font_set_family(fontid, "Courier New"); + xvt_font_set_size(fontid, me); + xvt_dwin_set_font(win.win(), fontid); + const int width = xvt_dwin_get_text_width(win.win(), emme, -1); + const int error = abs(width - ppi); + xvt_font_destroy(fontid); + if (best == 0 || error < best_error) + { + best = me; + best_error = error; + if (error == 0) + break; + } + if (width > ppi) + ma = me-1; + else + mi = me+1; + } + nSize = cpi * best / _cpi; + } + + XVT_FNTID fontid = xvt_font_create(); + xvt_font_set_family(fontid, (char*)(const char*)_name); + xvt_font_set_size(fontid, nSize); + xvt_font_set_style(fontid, _style); + + TReport_font& myself = *(TReport_font*)this; + myself.unmap(); + myself._fontid = fontid; + myself._win_mapped = win.win(); + xvt_dwin_get_font_metrics(_win_mapped, &myself._leading, &myself._ascent, &myself._descent); + } + return _fontid; +} + +void TReport_font::unmap() +{ + if (_fontid != NULL) + { + xvt_font_destroy(_fontid); + _fontid = NULL; + } + _win_mapped = NULL_WIN; +} + +void TReport_font::create(const char* name, int size, XVT_FONT_STYLE_MASK style) +{ + _name = name; + _size = size; + _style = style; + _cpi = 120 / _size; + unmap(); +} + +void TReport_font::copy(const TReport_font& font) +{ + create(font.name(), font.size(), font.style()); +} + +int TReport_font::compare(const TSortable& s) const +{ + const TReport_font& f = (const TReport_font&)s; + int cmp = _name.compare(f.name(), -1, true); + if (cmp == 0) + { + cmp = _size - f.size(); + if (cmp == 0) + cmp = _style - f.style(); + } + return cmp; +} + +void TReport_font::save(TXmlItem& item) const +{ + TXmlItem& font = item.AddChild("font"); + font.SetAttr("face", _name); + font.SetAttr("size", _size); + if (_style & XVT_FS_BOLD) + font.SetAttr("bold", "1"); + if (_style & XVT_FS_ITALIC) + font.SetAttr("italic", "1"); + if (_style & XVT_FS_UNDERLINE) + font.SetAttr("underline", "1"); +} + +bool TReport_font::load(const TXmlItem& item) +{ + const TXmlItem* font = item.FindFirstChild("font"); + if (font != NULL) + { + const TString& name = font->GetAttr("face"); + const int size = font->GetIntAttr("size", 10); + XVT_FONT_STYLE_MASK style = 0; + if (font->GetIntAttr("bold")) + style |= XVT_FS_BOLD; + if (font->GetIntAttr("italic")) + style |= XVT_FS_ITALIC; + if (font->GetIntAttr("underline")) + style |= XVT_FS_UNDERLINE; + create(name, size, style); + } + return font != NULL; +} + +TReport_font::TReport_font() : _fontid(NULL), _win_mapped(NULL_WIN) +{ + create("Courier New", DEFAULT_FONT_SIZE, XVT_FS_NONE); +} + +TReport_font::TReport_font(const TReport_font& f) : _fontid(NULL), _win_mapped(NULL_WIN) +{ copy(f); } + +TReport_font::~TReport_font() +{ + unmap(); +} + +/////////////////////////////////////////////////////////// +// TReport_expr +/////////////////////////////////////////////////////////// + +class TReport_expr : public TExpression +{ + TReport& _report; + TVariant _var; + +protected: + virtual int parse_user_func(const char* name, int nparms) const; + virtual void evaluate_user_func(int index, int nparms, TEval_stack& stack, TTypeexp type) const; + bool is_numeric(const char* str) const; + +public: + TReport_expr(TReport& rep, const char* exp); + const TVariant& as_variant(TFieldtypes ft); +}; + +int TReport_expr::parse_user_func(const char* name, int nparms) const +{ + return -1; +} + +void TReport_expr::evaluate_user_func(int index, int nparms, TEval_stack& stack, TTypeexp type) const +{ +} + +bool TReport_expr::is_numeric(const char* str) const +{ + if (str == NULL || *str == '\0' || *str == '0') + return false; // Se comincia per zero va preservato! + if (*str == '-') + { + str++; + if (*str <= ' ') + return false; + } + while (*str) + { + if (strchr("0123456789.", *str) == NULL) + return false; + str++; + } + return true; +} + +const TVariant& TReport_expr::as_variant(TFieldtypes ft) +{ + set_type(ft == _alfafld || ft == _nullfld ? _strexpr : _numexpr); + const TString& str = as_string(); + switch (ft) + { + case _datefld: _var.set(TDate(str)); break; + case _longfld: _var.set(atol(str)); break; + case _realfld: _var.set(real(str)); break; + default : _var.set(str); break; + }; + return _var; +} + +TReport_expr::TReport_expr(TReport& rep, const char* exp) + : _report(rep) +{ + set(exp, _strexpr); +} + + +TObject* TReport_expr_cache::key2obj(const char* key) +{ + return new TReport_expr(*_report, key); +} + +TReport_expr& TReport_expr_cache::operator[](const char* key) +{ return *(TReport_expr*)objptr(key); } + + +TObject* TReport_image_cache::key2obj(const char* key) +{ + TImage* img = NULL; + + TFilename pathname = key; + if (pathname.custom_path()) + img = new TImage(pathname); + + return img; +} + +TImage* TReport_image_cache::image(const TString& key) +{ + return (TImage*)objptr(key); +} + +TReport_image_cache::TReport_image_cache() : TCache(7) +{ +} + +/////////////////////////////////////////////////////////// +// Utility +/////////////////////////////////////////////////////////// + +void advanced_draw_rect(TWindow& win, const RCT& r, int border, COLOR fore, COLOR back) +{ + const bool has_pen = border > 0; + const bool has_brush = color_distance(back, COLOR_WHITE) != 0; + if (has_pen || has_brush) + { + if (has_pen) + win.set_pen(fore, border, PAT_SOLID); + else + win.hide_pen(); + if (has_brush) + win.set_brush(back, PAT_SOLID); + else + win.hide_brush(); + xvt_dwin_draw_rect(win.win(), (RCT*)&r); + } +} + +void advanced_draw_justified_text(TWindow& win, const char* text, short x, short y, short dx) +{ + TString txt(text); txt.rtrim(); + int spaces = 0; + for (int s = 0; txt[s]; s++) + if (isspace(txt[s])) spaces++; + const int tw = xvt_dwin_get_text_width(win.win(), txt, -1); + if (tw < dx && spaces > 0) + { + txt << ' '; // Aggiunge spazio finale + const double kspc = double(dx - tw) / spaces; + int start = 0; + double kx = x; + for (int i = 0; txt[i]; i++) + { + if (isspace(txt[i])) + { + const bool last_word = txt[i+1] == '\0'; + const TString& parola = txt.sub(start, i + (last_word ? 0 : 1)); + const int lw = xvt_dwin_get_text_width(win.win(), parola, -1); + if (last_word) // ultima parola + kx = x+dx-lw; + xvt_dwin_draw_text(win.win(), int(kx+0.5), y, parola, -1); + kx += lw + kspc; + start = i+1; + } + } + } + else + xvt_dwin_draw_text(win.win(), x, y, text, -1); +} + +void advanced_draw_text(TWindow& win, const char* text, const RCT& r, + char halign, char valign) +{ + const short dx = r.right-r.left; + const short dy = r.bottom-r.top; + short x = r.left; + short y = r.bottom; + + if (halign != 'L') + { + const int tw = xvt_dwin_get_text_width(win.win(), text, -1); + switch (halign) + { + case 'C': x += (dx - tw)/2; break; + case 'R': x = r.right-tw; break; + default : break; + } + } + + // Text Height + int leading, ascent, descent; + xvt_dwin_get_font_metrics(win.win(), &leading, &ascent, &descent); + switch (valign) + { + case 'C': y -= (dy - ascent)/2; break; + case 'T': y = r.top + leading + ascent; break; + default : y -= descent; break; + } + + if (halign == 'J') + advanced_draw_justified_text(win, text, x, y, dx); + else + xvt_dwin_draw_text(win.win(), x, y, text, -1); +} + +static void set_num_attr(TXmlItem& item, const char* attr, long num, short def = 0) +{ + if (num != def) + { + const real n = num / CENTO; + item.SetAttr(attr, n.string()); + } +} + +static void set_col_attr(TXmlItem& item, const char* attr, COLOR col, COLOR def = COLOR_BLACK) +{ + if (color_distance(col, def) != 0) + { + TString16 str; + str.format("#%06X", col & 0xFFFFFF); + item.SetAttr(attr, str); + } +} + +static short get_num_attr(const TXmlItem& item, const char* attr, short def = 0) +{ + const TString& str = item.GetAttr(attr); + if (str.not_empty()) + { + real n = str; n *=CENTO ; + def = (short)n.integer(); + } + return def; +} + +static COLOR get_col_attr(const TXmlItem& item, const char* attr, COLOR col) +{ + const TString& str = item.GetAttr(attr); + if (str[0] == '#') + sscanf(str, "#%X", &col); + return col; +} + +static char get_chr_attr(const TXmlItem& item, const char* attr, char c) +{ + const TString& str = item.GetAttr(attr); + if (str[0] > ' ') + c = toupper(str[0]); + return c; +} + +/////////////////////////////////////////////////////////// +// TReport_section +/////////////////////////////////////////////////////////// + +TReport_section* TReport_section::father_section() const +{ + if (level() <= 0) + { + if (type() != 'B') + return _report.find_section('B', 0); + } + else + return _report.find_section('B', type() == 'B' ? 0 : 1); + return NULL; +} + +const TReport_font& TReport_section::font() const +{ + const TReport_font* f = _font; + if (f == NULL) + { + TReport_section* father = father_section(); + if (father == NULL) + f = &_report.font(); + else + f = &father->font(); + } + return *f; +} + +void TReport_section::set_font(const TReport_font& f) +{ + if (_font != NULL) + { + delete _font; + _font = NULL; + } + if (font() != f) + _font = new TReport_font(f); +} + +const TString& TReport_section::prescript() const +{ return _prescript.get(); } + +void TReport_section::set_prescript(const char* src) +{ + _prescript.set(src); + TString desc; desc << type() << level() << " PRESCRIPT"; + _prescript.set_description(desc); +} + +const TString& TReport_section::postscript() const +{ return _postscript.get(); } + +void TReport_section::set_postscript(const char* src) +{ + _postscript.set(src); + TString desc; desc << type() << level() << " POSTSCRIPT"; + _postscript.set_description(desc); +} + +void TReport_section::unmap_font() +{ + if (_font != NULL) + _font->unmap(); + for (int i = last(); i >= 0; i--) + field(i).unmap_font(); +} + +int TReport_section::add(TObject* obj) +{ + TReport_field* rf = (TReport_field*)obj; + rf->set_section(this); + return TArray::add(obj); +} + +int TReport_section::add(TObject& obj) +{ + TReport_field& rf = (TReport_field&)obj; + rf.set_section(this); + return TArray::add(obj); +} + +int TReport_section::find_field_pos(int id) +{ + for (int i = items()-1; i >= 0; i--) + { + if (field(i).id() == id) + break; + } + return i; +} + +TReport_field* TReport_section::find_field(int id) +{ + const int pos = find_field_pos(id); + if (pos >= 0) + return &field(pos); + return NULL; +} + +TPoint TReport_section::compute_size() const +{ + if (hidden()) + return TPoint(0,0); + + TPoint s = _size; + if (_size.x <= 0 || _size.y <= 0) // Calcolo automatico necessario + { + for (int i = 0; i < items(); i++) + { + const TReport_field& rf = field(i); + if (rf.shown()) + { + const TRectangle r = rf.get_draw_rect(); + if (_size.x <= 0 && r.right() > s.x) // Richiesto calcolo larghezza + s.x = r.right(); + if (_size.y <= 0 && r.bottom() > s.y) // Richiesto calcolo altezza + s.y = r.bottom(); + } + } + if ((s.x % 100) != 0) // Arrotonda alla colonna successiva + s.x = (s.x / 100 + 1) * 100; + if ((s.y % 100) != 0) // Arrotonda alla riga successiva + s.y = (s.y / 100 + 1) * 100; + } + return s; +} + +bool TReport_section::compute_rect(TRectangle& rct) const +{ + rct.set(TPoint(0, 0), compute_size()); + return !rct.is_empty(); +} + +void TReport_section::draw(TWindow& win, TReport_draw_mode rdm) const +{ + if (shown() || rdm == rdm_edit) + { + for (int i = 0; i < items(); i++) + { + const TReport_field& f = field(i); + f.draw(win, rdm); + } + } +} + +bool TReport_section::load_fields() +{ + const bool ok = active() || shown(); + if (ok) + { + for (int i = 0; i < items(); i++) + { + TReport_field& f = field(i); + f.load_field(); + } + } + return ok; +} + +bool TReport_section::execute_prescript() +{ + bool ok = true; + if (active()) + { + TString80 str; + if (items() > 0) + report().set_curr_field(&field(0)); + else + report().set_curr_field(NULL); + ok = _prescript.execute(report(), str); + } + for (int i = 0; i < items(); i++) + { + TReport_field& f = field(i); + f.execute_prescript(); + } + return ok; +} + +bool TReport_section::execute_postscript() +{ + bool ok = true; + for (int i = 0; i < items(); i++) + { + TReport_field& f = field(i); + f.execute_postscript(); + } + if (active()) + { + TString80 str; + ok = _postscript.execute(report(), str); + } + return ok; +} + +void TReport_section::save(TXmlItem& root) const +{ + TXmlItem& item = root.AddChild("section"); + char* tipo = NULL; + switch (type()) + { + case 'H': tipo = "Head"; break; + case 'F': tipo = "Foot"; break; + default : tipo = "Body"; break; + } + item.SetAttr("type", tipo); + item.SetAttr("level", level()); + set_num_attr(item, "x", pos().x); + set_num_attr(item, "y", pos().y); + set_num_attr(item, "width", width()); + set_num_attr(item, "height", height()); + item.SetAttr("hidden", _hidden); + item.SetAttr("deactivated", _deactivated); + item.SetAttr("hidden_if_needed", hidden_if_needed()); + item.SetAttr("pagebreak", _page_break); + item.SetAttr("keep_with_next", keep_with_next()); + if (grouped_by().not_empty()) + item.AddChild("groupby") << grouped_by(); + if (has_font()) + font().save(item); + _prescript.save(item, "prescript"); + _postscript.save(item, "postscript"); + + for (int i = 0; i < items(); i++) + { + const TReport_field& rf = field(i); + rf.save(item); + } +} + +void TReport_section::load(const TXmlItem& sec) +{ + _pos.x = get_num_attr(sec, "x"); + _pos.y = get_num_attr(sec, "y"); + set_width(get_num_attr(sec, "width")); + set_height(get_num_attr(sec, "height")); + force_page_break(sec.GetBoolAttr("pagebreak")); + keep_with_next(sec.GetBoolAttr("keep_with_next")); + hide_if_needed(sec.GetBoolAttr("hidden_if_needed")); + show(!sec.GetBoolAttr("hidden")); + activate(!sec.GetBoolAttr("deactivated")); + + TReport_font font; + if (font.load(sec)) + set_font(font); + + if (level() > 1) + { + const TXmlItem* gb = sec.FindFirstChild("groupby"); + if (gb != NULL) + { + TString str; + gb->GetEnclosedText(str); + group_by(str); + } + } + + for (int j = 0; j < sec.GetChildren(); j++) + { + const TXmlItem& fld = *sec.GetChild(j); + if (fld.GetTag() == "field") + { + TReport_field* rf = new TReport_field(this); + if (rf->load(fld)) + add(rf); + else + delete rf; + } + } + + _prescript.load(sec, "prescript"); + _postscript.load(sec, "postscript"); +} + +TReport_section::TReport_section(TReport& r, char t, int l) + : _report(r), _type(t), _level(l), _font(NULL), + _size(0,0), _pos(0,0), _page_break(false), _hidden_if_needed(false), + _hidden(false), _deactivated(false) +{ } + +TReport_section::~TReport_section() +{ + if (_font) + delete _font; +} + +/////////////////////////////////////////////////////////// +// TReport_script +/////////////////////////////////////////////////////////// + +TString& TReport_script::translate_message(TReport& rep) const +{ + TToken_string source(_src, '\n'); + TToken_string line(256, '|'); + TToken_string args(256, ','); + TString cmd; + TString alex, empty_alex; + FOR_EACH_TOKEN(source, srcrow) + { + line = srcrow; + if (!line.starts_with("MESSAGE ")) + continue; + line.ltrim(8); line.trim(); + const bool msg_empty = line.starts_with("EMPTY "); + if (msg_empty) + { + line.ltrim(6); + line.trim(); + } + FOR_EACH_TOKEN(line, tok) + { + const TFixed_string msg(tok); + const int comma = msg.find(','); + if (comma > 0) + { + cmd = msg.left(comma); + args = msg.mid(comma+1); + if (args[0] != '#') // Controlla se c'e' bisogno di un # all'inizio + { + char type; + int level, id; + if (rep.parse_field(args.get(0), type, level, id) >= 2) + args.insert("#"); + } + } + else + { + cmd = msg; + args.cut(0); + } + + if (cmd[0] != '_') + cmd.insert("_"); + cmd.insert("MESSAGE"); + if (rep.defined(cmd)) + { + TString& alx = msg_empty ? empty_alex : alex; + for (int i = args.items()-1; i >= 0; i--) + alx << args.get(i) << ' '; + alx << cmd << ' '; + } + else + { + cmd << " ?"; + error_box(cmd); + } + } + } + TString& src = get_tmp_string(); + if (!empty_alex.blank()) + { + src = "#THIS @ IF\n"; + src << alex; + src << "\nELSE\n"; + src << empty_alex; + src << "\nTHEN"; + } + else + src = alex; + + return src; +} + + +void TReport_script::set(const char* source) +{ + if (_src != source) + { + destroy(); + _src = source; + } +} + +void TReport_script::copy(const TReport_script& rs) +{ + set(rs.get()); +} + +bool TReport_script::compile(TReport& rep) +{ + if (_bc == NULL) + _bc = new TBytecode; + + bool good = true; + if (_src.starts_with("MESSAGE ")) + good = rep.compile(translate_message(rep), *_bc); + else + good = rep.compile(_src, *_bc); + _bc->set_name(_desc); + + return good; +} + +bool TReport_script::execute(TReport& rep, TString& output) +{ + bool good = true; + if (ok()) + { + if (_bc == NULL) + good = compile(rep); + if (good) + good = rep.execute(*_bc, output); + } + return good; +} + +bool TReport_script::execute(TReport_field& rf) +{ + bool good = true; + if (ok()) + { + TString str; + TReport& rep = rf.section().report(); + rep.set_curr_field(&rf); + good = execute(rep, str); + } + return good; +} + +void TReport_script::destroy() +{ + if (_bc != NULL) + { + delete _bc; + _bc = NULL; + } + _src.cut(0); +} + +void TReport_script::save(TXmlItem& root, const char* tag) const +{ + if (ok()) + { + TXmlItem& script = root.AddChild(tag); + script.SetAttr("description", _desc); + script << _src; + } +} + +bool TReport_script::load(const TXmlItem& root, const char* tag) +{ + destroy(); + TXmlItem* script = root.FindFirstChild(tag); + if (script != NULL) + { + _desc = script->GetAttr("description"); + script->GetEnclosedText(_src); + } + return ok(); +} + +TReport_script::TReport_script() : _bc(NULL) +{ } + +TReport_script::~TReport_script() +{ destroy(); } + +/////////////////////////////////////////////////////////// +// TReport_field +/////////////////////////////////////////////////////////// + +void TReport_field::set_pos(long x, long y) +{ + _rct.x = x; + _rct.y = y; +} + +void TReport_field::set_size(long w, long h) +{ + _rct.set_width(w); + _rct.set_height(h); +} + +void TReport_field::offset(const TPoint& pt) +{ + _rct.x += pt.x; + _rct.y += pt.y; +} + +void TReport_field::set_draw_pos(long x, long y) +{ + _rct_draw.x = x; + _rct_draw.y = y; +} + +void TReport_field::set_draw_size(long w, long h) +{ + _rct_draw.set_width(w); + _rct_draw.set_height(h); +} + +void TReport_field::compute_draw_rect() const +{ + TRectangle& rct = ((TReport_field*)this)->_rct_draw; + rct = get_rect(); + if (type() == 'S') + { + if (rct.width() <= 0) + { + TString text = formatted_text(); text.rtrim(); + TToken_string str(text, '\n'); + int maxlen = 1; + FOR_EACH_TOKEN(str, line) + { + const int len = strlen(line); + if (len > maxlen) + maxlen = len; + } + rct.set_width(maxlen * 100); + } + if (rct.height() <= 0) + { + if (rct.width() >= 100) + { + TString text = formatted_text(); text.rtrim(); + TParagraph_string str(text, rct.width()/100); + rct.set_height(str.items() * 100); + } + if (rct.height() <= 0) + rct.set_height(100); + } + } +} + +const TReport_font& TReport_field::font() const +{ + return _font != NULL ? *_font : _section->font(); +} + +void TReport_field::set_font(const TReport_font& f) +{ + if (_font != NULL) + { + delete _font; + _font = NULL; + } + if (f != font()) + _font = new TReport_font(f); +} + +void TReport_field::unmap_font() +{ + if (_font != NULL) + _font->unmap(); +} + +const TString& TReport_field::prescript() const +{ return _prescript.get(); } + +void TReport_field::set_prescript(const char* src) +{ + TString desc; desc << section().type() << section().level() << '.' << id() << " PRESCRIPT"; + _prescript.set_description(desc); + _prescript.set(src); +} + +const TString& TReport_field::postscript() const +{ return _postscript.get(); } + +void TReport_field::set_postscript(const char* src) +{ + TString desc; desc << section().type() << section().level() << '.' << id() << " POSTSCRIPT"; + _postscript.set_description(desc); + _postscript.set(src); +} + +void TReport_field::copy(const TReport_field& rf) +{ + _section = rf._section; + _id = rf.id(); + _type = rf.type(); + _rct = rf._rct; + _fgcolor = rf._fgcolor; _bgcolor = rf._bgcolor; + _border = rf._border; + _halign = rf._halign; _valign = rf._valign; + _picture = rf._picture; _field = rf._field; + _hidden = rf.hidden(); + _deactivated = rf.deactivated(); + _hide_zeroes = rf._hide_zeroes; + _selected = false; + _prescript = rf._prescript; + _postscript = rf._postscript; + _list = rf._list; + + set_font(rf.font()); +} + +const char* TReport_field::type_name() const +{ + const char* n = NULL; + switch (_type) + { + case 'A': n = "Array"; break; + case 'D': n = "Data"; break; + case 'E': n = "Ellisse"; break; + case 'I': n = "Immagine"; break; + case 'L': n = "Linea"; break; + case 'N': n = "Numero"; break; + case 'P': n = "Prezzo"; break; + case 'R': n = "Rettangolo"; break; + case 'S': n = "Stringa"; break; + case 'T': n = "Testo"; break; + case 'V': n = "Valuta"; break; + default : break; + } + return n; +} + +TFieldtypes TReport_field::var_type() const +{ + TFieldtypes ft = _nullfld; + switch (_type) + { + case 'D': ft = _datefld; break; + case 'P': // Prezzo + case 'V': // Valuta + case 'N': ft = _realfld; break; + case 'I': + case 'A': + case 'S': ft = _alfafld; break; + default : ft = _nullfld; break; + } + return ft; +} + +void TReport_field::set(const char* str) +{ + _var = str; + _var.convert_to(var_type()); +} + +void TReport_field::set(const TVariant& var) +{ + _var = var; + _var.convert_to(var_type()); +} + +bool TReport_field::load_field() +{ + const bool ok = !_field.blank(); + if (ok) + { + const TFieldtypes ft = var_type(); + if (ft != _nullfld) + { + if (_field.starts_with("SELECT ")) + { + TSQL_recordset qry(_field); + TString str; + for (int i = 0; i < qry.items(); i++) + { + if (i > 0) str << '\n'; + for (unsigned int j = 0; j < qry.columns(); j++) + { + if (j > 0) str << ' '; + str << qry.get(j).as_string(); + } + } + _var.set(str); + _var.convert_to(ft); + } + else + section().report().evaluate(_field, _var, ft); + } + else + _var.set_null(); + } + compute_draw_rect(); + + return ok; +} + +bool TReport_field::execute_prescript() +{ + bool ok = true; + if (active()) + ok = _prescript.execute(*this); + return ok; +} + +bool TReport_field::execute_postscript() +{ + return deactivated() || _postscript.execute(*this); +} + +void TReport_field::draw_rect(TWindow& win) const +{ + RCT r; win.log2dev(get_rect(), r); + advanced_draw_rect(win, r, border(), fore_color(), back_color()); +} + +COLOR TReport_field::link_color() const +{ + return COLOR_BLUE; +} + +void TReport_field::draw_text(TWindow& win, const char* text, TReport_draw_mode rdm) const +{ + const TRectangle& rct = get_draw_rect(); + RCT r; win.log2dev(rct, r); + advanced_draw_rect(win, r, border(), fore_color(), back_color()); + + if (rdm == rdm_print_preview && link().not_empty()) + { + XVT_FNTID lnkfont = xvt_font_create(); + xvt_font_copy(lnkfont, font().get_xvt_font(win), XVT_FA_ALL); + xvt_font_set_style(lnkfont, XVT_FS_UNDERLINE); + xvt_dwin_set_font(win.win(), lnkfont); + win.set_color(link_color(), back_color()); + } + else + { + xvt_dwin_set_font(win.win(), font().get_xvt_font(win)); + win.set_color(fore_color(), back_color()); + } + + if (rct.height() > 100) + { + const unsigned columns = rct.width() / 100; + int rows = rct.height() / 100; + TParagraph_string str(text, columns); + if (str.items() < rows) + rows = str.items(); + + int ybase = rct.y; + switch (_valign) + { + case 'C': ybase += (rct.height() - rows*100) / 2; break; + case 'B': ybase += rct.height() - rows*100; break; + default : break; + } + char halign = _halign; + char valign = 'T'; + for (int row = 0; row < rows; row++) + { + TRectangle rctline = rct; + rctline.y = ybase + 100*row; + rctline.set_height(100); + win.log2dev(rctline, r); + const char* line = str.get(); + if (halign == 'J' && (row == rows-1 || strlen(line) < columns/2)) + halign = 'L'; + advanced_draw_text(win, line, r, halign, valign); + } + } + else + advanced_draw_text(win, text, r, _halign, _valign); +} + +void TReport_field::get_currency(TCurrency& cur) const +{ + if (_codval.not_empty()) + { + TVariant val; + section().report().evaluate(_codval, val, _alfafld); + cur.force_value(val.as_string()); + } + cur.set_price(_type == 'P'); + cur.set_num(_var.as_real()); +} + +const TString& TReport_field::formatted_text() const +{ + if (_hide_zeroes && _var.is_zero()) + return EMPTY_STRING; + + switch (_type) + { + case 'A': + { + const TString& val = _var.as_string(); + TString_array list; get_list(list); + int i; + for (i = list.last(); i > 0; i--) + if (val == list.row(i).get(0)) + break; + if (i >= 0) + { + TString& fmt = get_tmp_string(); + TToken_string& tok = list.row(i); + tok.get(1, fmt); + return fmt; + } + } + break; + case 'D': + { + const TDate d = _var.as_date(); + TString& tmp = get_tmp_string(); + if (_picture.not_empty()) + { + if (_picture.find('#') >= 0) + { + TString8 str; str.format("%02d%02d%04d", d.day(), d.month(), d.year()); + tmp.picture(_picture, str); + } + else + tmp << _picture << ' ' << d; + } + else + tmp = d.string(_rct.width() >= 1000 ? full : brief); + return tmp; + } + break; + case 'N': + { + const real n = _var.as_real(); + TString& tmp = get_tmp_string(); + if (_picture.not_empty()) + tmp = n.string(_picture); + else + tmp = n.stringa(); + return tmp; + } + break; + case 'P': + case 'V': + { + TString& tmp = get_tmp_string(); + TCurrency cur; get_currency(cur); + tmp = cur.string(true); + return tmp; + } + break; + default: + return _var.as_string(); + } + return EMPTY_STRING; +} + +void TReport_field::draw(TWindow& win, TReport_draw_mode rdm) const +{ + if (rdm != rdm_edit && (hidden() || deactivated())) + return; + + if (rdm == rdm_edit) + compute_draw_rect(); + + PAT_STYLE back_pattern = PAT_HOLLOW; + if (rdm == rdm_edit) + { + if (hidden() || deactivated()) + { + if (hidden() && deactivated()) + back_pattern = PAT_DIAGCROSS; + else + back_pattern = hidden() ? PAT_FDIAG : PAT_BDIAG; + } + } + + switch (_type) + { + case 'E': + { + const bool has_pen = border() > 0; + const bool has_brush = color_distance(back_color(), COLOR_WHITE) != 0; + if (has_pen) + win.set_pen(fore_color(), border(), PAT_SOLID); + else + win.hide_pen(); + if (has_brush) + win.set_brush(back_color(), PAT_SOLID); + else + win.hide_brush(); + RCT r; win.log2dev(get_rect(), r); + xvt_dwin_draw_oval(win.win(), &r); + + if (rdm == rdm_edit && back_pattern != PAT_HOLLOW) + { + win.set_brush(COLOR_GRAY, back_pattern); + xvt_dwin_draw_oval(win.win(), &r); + back_pattern = PAT_HOLLOW; + } + } + break; + case 'I': + { + RCT r; win.log2dev(get_rect(), r); + TReport& rep = section().report(); + if (rdm == rdm_edit) + { + TVariant var; + section().report().evaluate(_field, var, _alfafld); + const TString& name = var.as_string(); + const TImage* img = rep.image(name); + if (img != NULL && img->ok()) + img->draw(win.win(), r); + } + else + { + const TString& name = get().as_string(); + const TImage* img = rep.image(name); + if (img != NULL && img->ok()) + img->draw(win.win(), r); + } + draw_rect(win); + } + break; + case 'L': + { + win.set_pen(fore_color(), border()); + const TRectangle& r = get_rect(); + win.line((short)r.left(), (short)r.top(), (short)r.right(), (short)r.bottom()); + back_pattern = PAT_HOLLOW; + } + break; + case 'R': draw_rect(win); break; + case 'T': draw_text(win, _picture, rdm); break; + default : + if (rdm == rdm_edit) + { + if (border() <= 0) // Rendi piu' visibile il bordo dei campi che non ce l'hanno + { + RCT r; win.log2dev(get_draw_rect(), r); + advanced_draw_rect(win, r, 1, COLOR_LTGRAY, COLOR_WHITE); + } + if (id() > 0) + { + TString16 str; str << id(); + draw_text(win, str, rdm); + } + else + draw_text(win, _field, rdm); + } + else // Real printing + { + const TString& str = formatted_text(); + if (!str.blank()) + draw_text(win, str, rdm); + } + break; + } + + if (rdm == rdm_edit) + { + RCT r; win.log2dev(get_rect(), r); + if (back_pattern != PAT_HOLLOW) + { + win.set_pen(COLOR_LTGRAY); + win.set_brush(COLOR_LTGRAY, back_pattern); + xvt_dwin_draw_rect(win.win(), &r); + } + if (selected()) + { + const int k = 4; + RCT s = r; s.right = s.left+k; s.bottom = s.top+k; + advanced_draw_rect(win, s, 0, COLOR_WHITE, COLOR_LTGRAY); + s = r; s.left = s.right-k; s.top = s.bottom-k; + advanced_draw_rect(win, s, 0, COLOR_WHITE, COLOR_LTGRAY); + } + } +} + +void TReport_field::set_groups(const TString& groups) +{ + _groups.reset(); + _groups.set(groups); +} + +const TString& TReport_field::groups() const +{ + TString& str = get_tmp_string(); + str << _groups; + return str; +} + +bool TReport_field::in_group(int group) const +{ + if (group <= 0) + return _groups.first_one() > 0; + return _groups[group]; +} + +void TReport_field::save(TXmlItem& root) const +{ + TXmlItem& fld = root.AddChild("field"); + fld.SetAttr("type", type_name()); + + const TRectangle& rct = get_rect(); + fld.SetAttr("id", _id); + set_num_attr(fld, "x", rct.left()); + set_num_attr(fld, "y", rct.top()); + set_num_attr(fld, "width", rct.width()); + set_num_attr(fld, "height", rct.height(), 100); + fld.SetAttr("hidden", _hidden); + fld.SetAttr("deactivated", _deactivated); + fld.SetAttr("zeroes_hidden", _hide_zeroes); + set_col_attr(fld, "bg_color", back_color(), COLOR_WHITE); + set_col_attr(fld, "fg_color", fore_color(), COLOR_BLACK); + if (has_font()) + font().save(fld); + if (in_group(0)) + fld.AddChild("groups") << groups(); + + switch (horizontal_alignment()) + { + case 'C': fld.SetAttr("align", "center"); break; + case 'R': fld.SetAttr("align", "right"); break; + case 'J': fld.SetAttr("align", "justify"); break; + default : break; + }; + switch (vertical_alignment()) + { + case 'B': fld.SetAttr("valign", "bottom"); break; + case 'C': fld.SetAttr("valign", "center"); break; + default : break; + }; + fld.SetAttr("border", border()); + fld.SetAttr("text", picture()); + fld.SetAttr("codval", codval()); + fld.SetAttr("link", link()); + if (field().not_empty()) + fld.AddChild("source") << field(); + _prescript.save(fld, "prescript"); + _postscript.save(fld, "postscript"); + + if (_type == 'A') + { + TString_array arr; get_list(arr); + TXmlItem& list = fld.AddChild("list"); + FOR_EACH_ARRAY_ROW(arr, i, row) + { + TXmlItem& li = list.AddChild("li"); + li.SetAttr("Code", row->get(0)); + li << row->get(); + } + } +} + + +bool TReport_field::load(const TXmlItem& fld) +{ + const TString& t = fld.GetAttr("type"); + set_type(t[0]); + + set_id(fld.GetIntAttr("id")); + set_column(get_num_attr(fld, "x")); + set_row(get_num_attr(fld, "y")); + set_width(get_num_attr(fld, "width")); + set_height(get_num_attr(fld, "height", 100)); + _rct_draw = _rct; + show(!fld.GetBoolAttr("hidden")); + activate(!fld.GetBoolAttr("deactivated")); + hide_zeroes(fld.GetBoolAttr("zeroes_hidden")); + set_border(fld.GetIntAttr("border")); + set_back_color(get_col_attr(fld, "bg_color", COLOR_WHITE)); + set_fore_color(get_col_attr(fld, "fg_color", COLOR_BLACK)); + set_horizontal_alignment(get_chr_attr(fld, "align", 'L')); + set_vertical_alignment(get_chr_attr(fld, "valign", 'T')); + set_picture(fld.GetAttr("text")); + set_codval(fld.GetAttr("codval")); + set_link(fld.GetAttr("link")); + TXmlItem* src = fld.FindFirstChild("source"); + if (src != NULL) + src->GetEnclosedText(_field); + TReport_font font; + if (font.load(fld)) + set_font(font); + + TXmlItem* grp = fld.FindFirstChild("groups"); + if (grp != NULL) + { + TString str; grp->GetEnclosedText(str); + set_groups(str); + } + + _prescript.load(fld, "prescript"); + _postscript.load(fld, "postscript"); + + _list.cut(0); + if (_type == 'A') + { + TXmlItem* list = fld.FindFirstChild("list"); + TToken_string tok; + TString str; + for (int i = 0; i < list->GetChildren(); i++) + { + const TXmlItem* li = list->GetChild(i); + tok = li->GetAttr("Code"); + tok.add(li->GetEnclosedText(str)); + _list.add(tok); + } + } + + return true; +} + +void TReport_field::get_list(TString_array& list) const +{ + list.destroy(); + TToken_string& toklst = (TToken_string&)_list; + FOR_EACH_TOKEN(toklst, tok) + list.add(tok); +} + +void TReport_field::set_list(const TString_array& list) +{ + _list.cut(0); + FOR_EACH_ARRAY_ROW(list, i, row) + _list.add(*row); +} + +int TReport_field::compare(const TSortable& s) const +{ + const TReport_field& rf = (TReport_field&)s; + + int cmp = _rct.y - rf._rct.y; + if (cmp == 0) + { + cmp = _rct.x - rf._rct.x; + if (cmp == 0) + cmp = _id - rf._id; + } + return cmp; +} + +TReport_field::TReport_field(TReport_section* sec) + : _section(sec), _id(0), _type('T'), + _font(NULL), _halign('L'), _valign('T'), + _selected(false), _hidden(false), _deactivated(false), _hide_zeroes(false), + _border(0), _fgcolor(COLOR_BLACK), _bgcolor(COLOR_WHITE), _list(32, '\n') +{ + set_pos(0,0); + set_size(1600,100); + _rct_draw = _rct; +} + +TReport_field::TReport_field(const TReport_field& rf) : _font(NULL) +{ + copy(rf); +} + +TReport_field::~TReport_field() +{ + if (_font != NULL) + delete _font; +} + +/////////////////////////////////////////////////////////// +// TReport_link +/////////////////////////////////////////////////////////// + +void TReport_link::set(const char* field, const TVariant& var) +{ + _fields.add(field, var, true); +} + +const TVariant& TReport_link::get(const char* field) const +{ + const TVariant* var = (const TVariant*)_fields.objptr(field); + return var != NULL ? *var : NULL_VARIANT; +} + +void TReport_link::add_rect(const RCT& rct) +{ + // Non memorizzo tutti i rettangoli del link, ma la loro unione + if (xvt_rect_is_empty(&_rct)) + _rct = rct; + else + { + if (rct.left < _rct.left) _rct.left = rct.left; + if (rct.top < _rct.top) _rct.top = rct.top; + if (rct.right > _rct.right) _rct.right = rct.right; + if (rct.bottom > _rct.bottom) _rct.bottom = rct.bottom; + } +} + +int TReport_link::hit_test(const PNT& p) const +{ + if (p.v < _rct.top) + return -1; + if (p.v > _rct.bottom) + return +1; + if (p.h < _rct.left) + return -1; + if (p.h > _rct.right) + return +1; + return 0; +} + +int TReport_link::compare(const TSortable& s) const +{ + const TReport_link& lnk = (const TReport_link&)s; + int cmp = _rct.top - lnk._rct.top; + if (cmp == 0) + cmp = _rct.left - lnk._rct.left; + return cmp; +} + +TReport_link::TReport_link(const char* table) + : _table(table) +{ + xvt_rect_set_empty(&_rct); +} + +/////////////////////////////////////////////////////////// +// TReport +/////////////////////////////////////////////////////////// + +void TReport::build_section_key(char type, int level, TString& key) const +{ key.format("%c%d", type, level); } + +TReport_section* TReport::find_section(char type, int level) const +{ + TString4 key; build_section_key(type, level, key); + TReport_section* sec = (TReport_section*)_sections.objptr(key); + return sec; +} + +bool TReport::kill_section(char type, int level) +{ + TString4 key; build_section_key(type, level, key); + const bool ok = _sections.remove(key); + if (ok && level > 1 && type != 'B') + { + // Cancello anche testa/coda corrispondente + build_section_key(type == 'H' ? 'F' : 'H', level, key); + _sections.remove(key); + } + return ok; +} + +int TReport::find_max_level(char type) const +{ + int lev = 1; + + char type1 = type; + char type2 = ' '; + if (type == 'H' || type == 'F') + { type1 = 'H'; type2 = 'F'; } // Non puo' esistere footer di gruppo senza header + + TAssoc_array& ass = (TAssoc_array&)_sections; + FOR_EACH_ASSOC_OBJECT(ass, h, k, o) if (k[0] == type1 || k[0] == type2) + { + const int l = atoi(k+1); + if (l > lev) + lev = l; + } + return lev; +} + +bool TReport::set_recordset(TRecordset* rs) +{ + if (_recordset != NULL) + { + delete _recordset; + _recordset = NULL; + } + _recordset = rs; + return _recordset != NULL; +} + + +bool TReport::set_recordset(const TString& sql) +{ + bool ok = false; + if (sql.compare("SELECT ", 7, true) == 0) + ok = set_recordset(new TSQL_recordset(sql)); + else + ok = set_recordset(new TISAM_recordset(sql)); + return ok; +} + +TReport_section& TReport::section(char type, int level) +{ + TReport_section* sec = find_section(type, level); + if (sec == NULL) + { + sec = new TReport_section(*this, type, level); + TString4 key; build_section_key(type, level, key); + _sections.add(key, sec); + } + return *sec; +} + +// Parsa un riferimento a campo, gruppo o sezione +// #B0 -> 2 B 0 +// #101 -> 3 B 1 101 +// #B1.101@ -> 4 B 1 101 +int TReport::parse_field(const char* code, char& type, int& level, int& id) const +{ + if (code[0] == '#') + code++; + + if (isdigit(code[0]) || strncmp(code, "THIS", 4) == 0) // Niente sezione davanti + { + TReport_field* rf = curr_field(); + if (rf != NULL) + { + type = rf->section().type(); + level = rf->section().level(); + } + id = atoi(code); + } + else + { + type = code[0]; + if (type != 'H' && type != 'B' && type != 'F') // Non comincia con un codice sezione + return 0; + + level = atoi((const char*)code + 1); + TReport_section* sec = find_section(type, level); + if (sec == NULL) + return 1; + + const int dot = code[2] == '.' ? 2 : (code[3] == '.' ? 3 : -1); + if (dot <= 0) + return 2; + id = atoi((const char*)code + dot + 1); + } + + return strchr(code, '@') != NULL ? 4 : 3; +} + +TReport_field* TReport::field(const TString& code) +{ + char type = ' '; + int level = -1, id = 0; + const int k = parse_field(code, type, level, id); + + TReport_field* rf = NULL; + if (k == 3) + { + if (id > 0) + { + TReport_section* sec = find_section(type, level); + if (sec != NULL) + rf = sec->find_field(id); + } + else + rf = curr_field(); + } + return rf; +} + +bool TReport::evaluate(const char* expr, TVariant& var, TFieldtypes force_type) +{ + TReport_expr& e = _expressions[expr]; + + // Caso semplice nonche' standard + if (e.numvar() == 1) + { + const TFixed_string name(e.varname(0)); + if (name == expr) + { + if (get_usr_val(name, var)) + { + if (force_type != _nullfld) + var.convert_to(force_type); + return true; + } + } + } + + for (int i = 0; i < e.numvar(); i++) + { + const TFixed_string name(e.varname(i)); + const bool ok = get_usr_val(name, var); + if (!ok) + var = name; + if (var.is_string()) + e.setvar(i, var.as_string()); + else + e.setvar(i, var.as_real()); + } + + const TFieldtypes ft = force_type != _nullfld ? force_type : var.type(); + var = e.as_variant(ft); + + return true; +} + +void TReport::destroy() +{ + _sections.destroy(); + _description.cut(0); + if (_recordset != NULL) + { + delete _recordset; + _recordset = NULL; + } +} + +void TReport::load_sections(const TXmlItem& xml) +{ + for (int i = 0; i < xml.GetChildren(); i++) + { + const TXmlItem& sec = *xml.GetChild(i); + if (sec.GetTag() != "section") + continue; + + const char type = sec.GetAttr("type")[0]; + const int level = sec.GetIntAttr("level"); + TReport_section& rs = section(type, level); + rs.load(sec); + } +} + +bool TReport::load(const char* fname) +{ + destroy(); + TXmlItem xml; + + _path = fname; + bool ok = xml.Load(_path); + if (ok) + { + _lpi = xml.GetIntAttr("lpi", 6); + _font.load(xml); + + const TXmlItem* desc = xml.FindFirstChild("description"); + if (desc != NULL) + desc->GetEnclosedText(_description); + + if (xml.FindFirstChild("section") != NULL) + load_sections(xml); + + const TXmlItem* sql = xml.FindFirstChild("sql"); + if (sql != NULL) + { + TString str; + sql->GetEnclosedText(str); + set_recordset(str); + } + + _include = xml.GetAttr("libraries"); + FOR_EACH_TOKEN(_include, lib) + { + TFilename libname = lib; + libname.trim(); + if (libname.find('.') < 0) + libname.ext("alx"); + include(libname); + } + + _prescript.load(xml, "prescript"); + _postscript.load(xml, "postscript"); + } + return ok; +} + +bool TReport::save(const char* fname) const +{ + char name[_MAX_FNAME]; + xvt_fsys_parse_pathname (fname, NULL, NULL, name, NULL, NULL); + bool ok = *name > ' '; + if (ok) + { + TXmlItem xml; + xml.SetTag("report"); + xml.SetAttr("name", name); + xml.SetAttr("lpi", lpi()); + xml.AddChild("description") << _description; + xml.SetAttr("libraries", _include); + _font.save(xml); + + const char* sectype = "HBF"; + for (int j = 0; j < 3; j++) + { + const int ml = find_max_level(sectype[j]); + for (int i = 0; i <= ml; i++) + { + TReport_section& rs = ((TReport*)this)->section(sectype[j], i); + rs.save(xml); + } + } + + if (_recordset != NULL) + xml.AddChild("sql") << _recordset->query_text(); + + _prescript.save(xml, "prescript"); + _postscript.save(xml, "postscript"); + + xml.Save(fname); + } + return ok; +} + +void TReport::unmap_font() +{ + _font.unmap(); + FOR_EACH_ASSOC_OBJECT(_sections, h, k, o) + { + TReport_section& sec = *(TReport_section*)o; + sec.unmap_font(); + } +} + +const TString& TReport::prescript() const +{ return _prescript.get(); } + +void TReport::set_prescript(const char* src) +{ + _prescript.set(src); +} + +const TString& TReport::postscript() const +{ return _postscript.get(); } + +void TReport::set_postscript(const char* src) +{ + _postscript.set(src); +} + +bool TReport::execute_prescript() +{ + bool ok = true; + + warm_restart(); + if (_prescript.ok()) + { + TString80 str; + ok = _prescript.execute(*this, str); + if (recordset() != NULL) + recordset()->ask_variables(false); + } + else + { + // Script dei poveri: lancia la maschera associata al report + TFilename msk = _path; msk.ext("msk"); + if (msk.exist()) + { + const KEY key = run_form(msk.name()); + ok = key != K_ESC && key != K_QUIT; + } + else + { + // Script dei poverissimi: chiede le eventuali variabili + if (recordset() != NULL) + recordset()->ask_variables(true); + } + } + + return ok; +} + +bool TReport::execute_postscript() +{ + TString80 str; + return _postscript.execute(*this, str); +} + +bool TReport::get_report_field(const TString& name, TVariant& var) const +{ + bool found = false; + const char* str = name; + if (name[0] == '#') + { + if (name.starts_with("#REPORT.")) + { + str += 8; + found = true; + } + else + str++; + } + const TFixed_string n(str); + + if (n == "PAGE") + { + var = curr_page(); + return true; + } + TReport_field* fld = ((TReport*)this)->field(n); + if (fld != NULL) + { + var = fld->get(); + return true; + } + + return found; +} + +bool TReport::get_record_field(const TString& name, TVariant& var) const +{ + bool found = false; + if (_recordset != NULL) + { + const char* str = name; + if (name[0] == '#') + { + if (name.starts_with("#RECORD.")) + { + str += 8; + found = true; + } + else + str++; + } + var = _recordset->get(str); + if (!var.is_null()) + return true; + } + + return found; +} + + +bool TReport::get_usr_val(const TString& name, TVariant& var) const +{ + if (get_report_field(name, var)) + return true; + + if (get_record_field(name, var)) + return true; + + return TAlex_virtual_machine::get_usr_val(name, var); +} + +bool TReport::set_usr_val(const TString& name, const TVariant& var) +{ + const char* str = name; + if (name[0] == '#') + { + if (name.starts_with("#REPORT.")) + str += 8; + else + str++; + } + const TFixed_string n(str); + TReport_field* fld = field(n); + if (fld != NULL) + { + fld->set(var); + return true; + } + + return TAlex_virtual_machine::set_usr_val(name, var); +} + +size_t TReport::get_usr_words(TString_array& words) const +{ + const char* const name[] = + { + "***", "DISABLE", "ENABLE", "GET_POS", + "GET_SIZE", "HIDE", "RUN_FORM", "SET_BACK_COLOR", "SET_FORE_COLOR", + "SET_POS", "SET_SIZE", "SHOW", NULL + }; + size_t i; + for (i = 0; name[i] != NULL; i++) + words.add(name[i]); + return i; +} + +static void do_show(TReport_field& rf, void* jolly) +{ rf.show(jolly != NULL); } + +static void do_enable(TReport_field& rf, void* jolly) +{ rf.activate(jolly != NULL); } + +static void do_set_pos(TReport_field& rf, void* jolly) +{ + const TPoint& pt = *(const TPoint*)jolly; + rf.set_draw_pos(pt.x, pt.y); +} + +static void do_set_size(TReport_field& rf, void* jolly) +{ + const TPoint& pt = *(const TPoint*)jolly; + rf.set_draw_size(pt.x, pt.y); +} + +static void do_set_back_color(TReport_field& rf, void* jolly) +{ rf.set_back_color((COLOR)jolly); } + +static void do_set_fore_color(TReport_field& rf, void* jolly) +{ rf.set_fore_color((COLOR)jolly); } + +bool TReport::do_message(const TVariant& var, FLDMSG_FUNC msg, void* jolly) +{ + char type; + int level, id; + const int k = parse_field(var.as_string(), type, level, id); + switch (k) + { + case 2: // E' una sezione + { + TReport_section& sec = section(type, level); + if (msg == do_show) + sec.show(jolly != 0); else + if (msg == do_enable) + sec.activate(jolly != 0); + } + break; + case 3: // E' un campo singolo + { + TReport_field* rf = id <= 0 ? curr_field() : section(type, level).find_field(id); + if (rf != NULL) + msg(*rf, jolly); + } + break; + case 4: // E' un gruppo + { + TReport_section& sec = section(type, level); + for (int i = 0; i < sec.items(); i++) + { + TReport_field& rf = sec.field(i); + if (rf.in_group(id)) + msg(rf, jolly); + } + } + break; + default: + break; + } + return true; +} + +KEY TReport::run_form(const TString& maskname) +{ + TFilename fname = maskname; fname.ext("msk"); + KEY key = K_QUIT; + if (fname.custom_path()) + { + 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("#"); + if (get_usr_val(name, var)) + f.set(var.as_string()); + } + } + key = m.run(); + if (key != K_QUIT && key != K_ESC) + { + TRecordset* rset = recordset(); + if (rset != NULL && rset->variables().items() == 0) + rset = NULL; + + // 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("#"); + set_usr_val(name, var); + if (rset != NULL) + rset->set_var(name, var); + } + } + } + } + return key; +} + +bool TReport::execute_usr_word(unsigned int opcode, TVariant_stack& stack) +{ + switch (opcode) + { + case 1: // Placeholder + break; + case 2: // DISABLE + do_message(stack.pop(), do_enable, NULL); + break; + case 3: // ENABLE + do_message(stack.pop(), do_enable, (void*)1); + break; + case 4: // GET_POS + { + const TReport_field* fld = field(stack.pop().as_string()); + real x, y; + if (fld != NULL) + { + const TRectangle& r = fld->get_draw_rect(); + x = r.x / CENTO; y = r.y / CENTO; + } + stack.push(x); stack.push(y); + } + break; + case 5: // GET_SIZE + { + const TReport_field* fld = field(stack.pop().as_string()); + real w, h; + if (fld != NULL) + { + const TRectangle& r = fld->get_draw_rect(); + w = r.width() / CENTO; h = r.height() / CENTO; + } + stack.push(w); stack.push(h); + } + break; + case 6: // HIDE + do_message(stack.pop(), do_show, NULL); + break; + case 7: // RUN_FORM + { + const TString& msk = stack.pop().as_string(); + const KEY key = run_form(msk); + stack.push(key); + } + break; + case 8: // SET_BACK_COLOR + { + const COLOR rgb = stack.pop().as_color(); + do_message(stack.pop(), do_set_back_color, (void*)rgb); + } + break; + case 9: // SET_FORE_COLOR + { + const COLOR rgb = stack.pop().as_color(); + do_message(stack.pop(), do_set_fore_color, (void*)rgb); + } + break; + case 10: // SET_POS + { + const TVariant& fld = stack.pop(); + const real y = stack.pop().as_real() * CENTO; + const real x = stack.pop().as_real() * CENTO; + const TPoint pt(x.integer(), y.integer()); + do_message(fld, do_set_pos, (void*)&pt); + } + break; + case 11: // SET_SIZE + { + const TVariant& fld = stack.pop(); + const real h = stack.pop().as_real() * CENTO; + const real w = stack.pop().as_real() * CENTO; + const TPoint sz(w.integer(), h.integer()); + do_message(fld, do_set_size, (void*)&sz); + } + break; + case 12: // SHOW + do_message(stack.pop(), do_show, (void*)1); + break; + default: + return false; + } + return true; +} + +bool TReport::on_link(const TReport_link& lnk) +{ + const TString& table = lnk.table(); + const int logicnum = table2logic(table); + if (logicnum >= LF_USER) + { + TRectype rec(logicnum); + TAssoc_array& fields = lnk.fields(); + FOR_EACH_ASSOC_OBJECT(fields, h, k, o) + { + const TVariant* var = (const TVariant*)o; + rec.put(k, var->as_string()); + } + return rec.edit(); + } + + return false; +} + +TReport::TReport() : _lpi(6), _recordset(NULL), _curr_field(NULL), _include(15, ',') +{ + _expressions.set_report(this); + + include("report.alx"); // Include base libraries + + _prescript.set_description("PRESCRIPT"); + _postscript.set_description("POSTSCRIPT"); +} + +TReport::~TReport() +{ + destroy(); +} + diff --git a/include/report.h b/include/report.h new file mode 100755 index 000000000..bc74d98b3 --- /dev/null +++ b/include/report.h @@ -0,0 +1,462 @@ +#ifndef __REPORT_H +#define __REPORT_H + +#ifndef __IMAGE_H +#include +#endif + +#ifndef __WINDOW_H +#include +#endif + +#ifndef __XML_H +#include +#endif + +#ifndef __RECORDSET_H +#include +#endif + +#ifndef __ALEX_H +#include +#endif + +/////////////////////////////////////////////////////////// +// TReport_font +/////////////////////////////////////////////////////////// + +class TReport_font : public TSortable +{ + enum { DEFAULT_FONT_SIZE = 10 }; + + TString _name; + int _size, _cpi; + XVT_FONT_STYLE_MASK _style; + + WINDOW _win_mapped; + XVT_FNTID _fontid; + int _leading, _ascent, _descent; + +protected: + virtual int compare(const TSortable& s) const; + void copy(const TReport_font& f); + +public: + const TString& name() const { return _name; } + int size() const { return _size; } + XVT_FONT_STYLE_MASK style() const { return _style; } + int cpi() const { return _cpi; } + XVT_FNTID get_xvt_font(const TWindow& win) const; + void unmap(); + + int leading() const { return _leading; } + int ascent() const { return _ascent; } + int descent() const { return _descent; } + + void save(TXmlItem& root) const; + bool load(const TXmlItem& root); + + void create(const char* name, int size, XVT_FONT_STYLE_MASK style); + TReport_font& operator=(const TReport_font& f) { copy(f); return *this; } + TReport_font(); + TReport_font(const TReport_font& f); + virtual ~TReport_font(); +}; + +class TReport; +class TReport_field; + +/////////////////////////////////////////////////////////// +// Cache varie +/////////////////////////////////////////////////////////// + +class TReport_expr; + +class TReport_expr_cache : public TCache +{ + TReport* _report; + +protected: + virtual TObject* key2obj(const char* key); + +public: + TReport_expr& operator[](const char* key); + + void set_report(TReport* rep) { _report = rep; } + TReport_expr_cache() : _report(NULL) { } +}; + +class TReport_image_cache : public TCache +{ +protected: + virtual TObject* key2obj(const char* key); + +public: + TImage* image(const TString& name); + TReport_image_cache(); +}; + +/////////////////////////////////////////////////////////// +// TReport_script +/////////////////////////////////////////////////////////// + +class TReport_script : public TObject +{ + TBytecode* _bc; // Chesire's cat class + TString _src, _desc; + +protected: + void destroy(); + TString& translate_message(TReport& report) const; + void copy(const TReport_script& rs); + +public: + virtual bool ok() const { return !_src.blank(); } + void set(const char* source); + const TString& get() const { return _src; } + void set_description(const char* d) { _desc = d; } + + bool compile(TReport& report); + bool execute(TReport& report, TString& output); + bool execute(TReport_field& rf); + + void save(TXmlItem& root, const char* tag) const; + bool load(const TXmlItem& root, const char* tag); + + TReport_script& operator =(const TReport_script& rs) { copy(rs); return *this; } + TReport_script(); + TReport_script(const TReport_script& rs) { copy(rs); } + virtual ~TReport_script(); +}; + +/////////////////////////////////////////////////////////// +// TReport_section +/////////////////////////////////////////////////////////// + +enum TReport_draw_mode { rdm_edit, rdm_print, rdm_print_preview, rdm_spooler, rdm_textonly }; + +class TReport_section : public TArray +{ + TReport& _report; + + char _type; // Head,Body,Tail + int _level; // 0,1,2,... + TPoint _pos; // Posizione assoluta in centesimi, default (0,0) + TPoint _size; // Dimensioni in centesimi, default (0,0) + TString _groupby; + bool _page_break, _hidden_if_needed, _keep_with_next; + bool _hidden, _deactivated; + TReport_script _prescript, _postscript; + + TReport_font* _font; + +protected: + virtual const char* class_name() const { return "ReportSection"; } + TReport_section* father_section() const; + +public: + virtual int add(TObject* obj); + virtual int add(TObject& obj); + TReport_field& field(int i) { return *(TReport_field*)objptr(i); } + const TReport_field& field(int i) const { return *(TReport_field*)objptr(i); } + int find_field_pos(int id); + TReport_field* find_field(int id); + TReport& report() { return _report; } + + char type() const { return _type; } + int level() const { return _level; } + + int width() const { return _size.x; } + int height() const { return _size.y; } + void set_width(short w) { _size.x = w; } + void set_height(short h) { _size.y = h; } + const TPoint& size() const { return _size; } + TPoint compute_size() const; + bool compute_rect(TRectangle& rct) const; + const TPoint& pos() const { return _pos; } + void set_pos(const TPoint& p) { _pos = p; } + + bool page_break() const { return _page_break; } + void force_page_break(bool pb) { _page_break = pb; } + + const TString& grouped_by() const { return _groupby; } + void group_by(const char* gb) { _groupby = gb; } + + bool hidden_if_needed() const { return _hidden_if_needed; } + void hide_if_needed(bool h) { _hidden_if_needed = h; } + bool keep_with_next() const { return _keep_with_next; } + void keep_with_next(bool k) { _keep_with_next = k; } + bool hidden() const { return _hidden; } + bool shown() const { return !hidden(); } + void show(bool on) { _hidden = !on; } + void hide() { show(false); } + bool deactivated() const { return _deactivated; } + bool active() const { return !deactivated(); } + void activate(bool on) { _deactivated = !on; } + void deactivate() { activate(false); } + + const TString& prescript() const; + void set_prescript(const char* src); + const TString& postscript() const; + void set_postscript(const char* src); + + bool has_font() const { return _font != NULL; } + const TReport_font& font() const; + void set_font(const TReport_font& f); + void unmap_font(); + + bool load_fields(); + bool execute_prescript(); + bool execute_postscript(); + void draw(TWindow& win, TReport_draw_mode mode) const; + + void save(TXmlItem& report) const; + void load(const TXmlItem& sec); + + TReport_section(TReport& r, char t, int l); + virtual ~TReport_section(); +}; + +class TReport_field : public TSortable +{ + TReport_section* _section; + int _id; + char _type; // Text, String, Numeric, Price, Valuta, Date, Line, Rectangle, Image + TRectangle _rct, _rct_draw; // In centesimi + COLOR _fgcolor, _bgcolor; + short _border; + char _halign, _valign; + TBit_array _groups; + TString _picture, _field, _codval, _link; + TVariant _var; + TReport_script _prescript, _postscript; + TToken_string _list; // Elementi di un campo lista + + TReport_font* _font; + bool _hidden, _deactivated, _hide_zeroes, _selected; + +protected: + virtual const char* class_name() const { return "ReportField"; } + virtual int compare(const TSortable& s) const; + void copy(const TReport_field& rf); + TFieldtypes var_type() const; + const TString& formatted_text() const; + void get_currency(TCurrency& cur) const; + void compute_draw_rect() const; + +public: + virtual TObject* dup() const { return new TReport_field(*this); } + TReport_field& operator=(const TReport_field& rf) { copy(rf); return *this; } + + TReport_section& section() const { return *_section; } + void set_section(TReport_section* sec) { _section = sec; } + + bool has_font() const { return _font != NULL; } + const TReport_font& font() const; + void set_font(const TReport_font& f); + void unmap_font(); + + const TString& picture() const { return _picture; } + void set_picture(const char* str) { _picture = str; } + + const TString& field() const { return _field; } + void set_field(const char* str) { _field = str; } + + const TVariant& get() const { return _var; } + void set(const char* str); + void set(const TVariant& var); + + bool load_field(); + bool execute_prescript(); + bool execute_postscript(); + + void get_list(TString_array& list) const; + void set_list(const TString_array& list); + + int id() const { return _id; } + void set_id(int id) { _id = id; } + char type() const { return _type; } + const char* type_name() const; + void set_type(char t) { _type = t; } + void set_pos(long x, long y); + void set_row(long y) { _rct.y = y; } + void set_column(long x) { _rct.x = x; } + void set_size(long w, long h); + void set_width(long dx) { _rct.set_width(dx); } + void set_height(long dy) { _rct.set_height(dy); } + const TRectangle& get_rect() const { return _rct; } + + void set_draw_pos(long x, long y); + void set_draw_size(long x, long y); + const TRectangle& get_draw_rect() const { return _rct_draw; } + + bool hidden() const { return _hidden; } + bool shown() const { return !hidden(); } + void show(bool on) { _hidden = !on; } + void hide() { show(false); } + bool deactivated() const { return _deactivated; } + bool active() const { return !deactivated(); } + void activate(bool on) { _deactivated = !on; } + void deactivate() { activate(false); } + bool zeroes_hidden() const { return _hide_zeroes; } + void hide_zeroes(bool hz) { _hide_zeroes = hz; } + + void set_groups(const TString& groups); + const TString& groups() const; + bool in_group(int group) const; + void set_codval(const char* cod) { _codval = cod; } + const TString& codval() const { return _codval; } + void set_link(const char* l) { _link = l; } + const TString& link() const { return _link; } // TABLE.FIELD + + void set_fore_color(COLOR c) { _fgcolor = c; } + COLOR fore_color() const { return _fgcolor; } + void set_back_color(COLOR c) { _bgcolor = c; } + COLOR back_color() const { return _bgcolor; } + COLOR link_color() const; + void set_border(short b) { _border = b; } + short border() const { return _border; } + void set_horizontal_alignment(char a) { _halign = a; } + char horizontal_alignment() const { return _halign; } + void set_vertical_alignment(char a) { _valign = a; } + char vertical_alignment() const { return _valign; } + + const TString& prescript() const; + void set_prescript(const char* src); + const TString& postscript() const; + void set_postscript(const char* src); + + void select(bool ok = true) { _selected = ok; } + bool selected() const { return _selected; } + void offset(const TPoint& pt); + + virtual void draw_rect(TWindow& win) const; + virtual void draw_text(TWindow& win, const char* text, TReport_draw_mode mode) const; + virtual void draw(TWindow& win, TReport_draw_mode mode) const; + + void save(TXmlItem& root) const; + bool load(const TXmlItem& root); + + TReport_field(TReport_section* sec); + TReport_field(const TReport_field& rf); + virtual ~TReport_field(); +}; + +class TReport_link : public TSortable +{ + TString _table; + TAssoc_array _fields; + RCT _rct; + +protected: + virtual int compare(const TSortable& s) const; + +public: + const TString& table() const { return _table; } + void set(const char* field, const TVariant& value); + const TVariant& get(const char* field) const; + void add_rect(const RCT& rct); // Aggiunge un rettangolo al link + int hit_test(const PNT& p) const; + + TAssoc_array& fields() const { return (TAssoc_array&)_fields; } + + TReport_link(const char* table); + virtual ~TReport_link() { } +}; + +// Internal usage only +typedef void (*FLDMSG_FUNC)(TReport_field& rf, void* jolly); + +class TReport : public TAlex_virtual_machine +{ + TAssoc_array _sections; + TFilename _path; + TString _description; + TReport_font _font; + int _lpi; // Lines per inch + TToken_string _include; + TReport_script _prescript, _postscript; + TRecordset* _recordset; + TReport_expr_cache _expressions; + TReport_image_cache _images; + word _curr_page; + TReport_field* _curr_field; + +protected: + virtual const char* class_name() const { return "Report"; } + virtual size_t get_usr_words(TString_array& words) const; + virtual bool execute_usr_word(unsigned int opcode, TVariant_stack& stack); + virtual bool get_usr_val(const TString& name, TVariant& var) const; + virtual bool set_usr_val(const TString& name, const TVariant& var); + KEY run_form(const TString& msk); + + bool do_message(const TVariant& var, FLDMSG_FUNC msg, void* jolly); + + void build_section_key(char type, int level, TString& key) const; + short get_num_attr(const TXmlItem& item, const char* attr, short def = 0) const; + COLOR get_col_attr(const TXmlItem& item, const char* attr, COLOR defcol = COLOR_BLACK) const; + void load_sections(const TXmlItem& xml); + void save_section(const TReport_section& rs, TXmlItem& item) const; + + bool get_report_field(const TString& name, TVariant& var) const; + bool get_record_field(const TString& name, TVariant& var) const; + +public: + TReport_section* find_section(char type, int level) const; + TReport_section& section(char type, int level); + bool kill_section(char type, int level); + int find_max_level(char type) const; + + virtual bool on_link(const TReport_link& link); + + const TReport_font& font() const { return _font; } + void set_font(const TReport_font& f) { _font = f; } + void unmap_font(); + + int cpi() const { return _font.cpi(); } + int lpi() const { return _lpi; } + void set_lpi(int lpi) { _lpi= lpi; } + + bool set_recordset(const TString& sql); + bool set_recordset(TRecordset* sql); + TRecordset* recordset() const { return _recordset; } + bool evaluate_atom(const char* atom, TVariant& var); + bool evaluate(const char* expr, TVariant& var, TFieldtypes force_type); + + const TString& prescript() const; + void set_prescript(const char* src); + const TString& postscript() const; + void set_postscript(const char* src); + bool execute_prescript(); + bool execute_postscript(); + + void set_libraries(const char* inc) { _include = inc; } + TToken_string& get_libraries() { return _include; } + + void set_description(const char* d) { _description = d; } + const TString& description() const { return _description; } + TImage* image(const TString& name) { return _images.image(name); } + + const TFilename& filename() const { return _path; } + bool save(const char* fname) const; + bool load(const char* fname); + + // Used by TReport_printer + void set_curr_page(word p) { _curr_page = p; } + word curr_page() const { return _curr_page; } + void set_curr_field(TReport_field* fld) { _curr_field = fld; } + TReport_field* curr_field() const { return _curr_field; } + + int parse_field(const char* code, char& type, int& level, int& id) const; + TReport_field* field(const TString& code); + + void destroy(); + TReport(); + virtual ~TReport(); +}; + +void advanced_draw_rect(TWindow& win, const RCT& r, int border, COLOR fore, COLOR back); +void advanced_draw_text(TWindow& win, const char* text, const RCT& r, + char halign, char valign); + +#endif diff --git a/include/reprint.cpp b/include/reprint.cpp new file mode 100755 index 000000000..7acf549d5 --- /dev/null +++ b/include/reprint.cpp @@ -0,0 +1,917 @@ +#include +#include +#include +#include +#include +#include + +#include + +static bool _print_aborted = false; + +/////////////////////////////////////////////////////////// +// TPrint_preview_window +/////////////////////////////////////////////////////////// + +class TPrint_preview_window : public TField_window +{ + TPage_printer* _printer; + word _page, _last; + int _zoom; + static bool _locked; + +protected: + void page_select(); + void popup_menu(EVENT* ep); + virtual void handler(WINDOW win, EVENT* ep); + virtual void update(); + virtual bool on_key(KEY k); + +public: + virtual PNT log2dev(long lx, long ly) const; + static void lock_preview_update(bool yes) { _locked = yes; } + + void set_printer(TPage_printer* printer) { _printer = printer; } + TPrint_preview_window(int x, int y, int dx, int dy, WINDOW parent, + TWindowed_field* owner, TPage_printer* printer); +}; + +bool TPrint_preview_window::_locked = false; + +PNT TPrint_preview_window::log2dev(long lx, long ly) const +{ + PNT pnt; + pnt.h = short(lx * _zoom / 1000); + pnt.v = short(ly * _zoom / 1000); + + const TPoint orig = origin(); + pnt.h -= short(orig.x*10); + pnt.v -= short(orig.y*10); + + return pnt; +} + +void TPrint_preview_window::update() +{ + if (!_locked) + { + _printer->print_page(_page); + TString80 str; + str.format(FR("Pagina %u di %u"), _page, _last); + statbar_set_title(TASK_WIN, str); + } +} + +#define POPUP_FIRST 20883 +#define POPUP_PREV 20884 +#define POPUP_NEXT 20885 +#define POPUP_SEARCH 20886 +#define POPUP_LAST 20887 +#define POPUP_ZOOMIN 20888 +#define POPUP_ZOOMOUT 20889 +#define POPUP_GRID 20890 + +void TPrint_preview_window::popup_menu(EVENT* ep) +{ + MENU_ITEM menu[16]; // Stiamo larghi + memset(menu, 0, sizeof(menu)); + menu[0].tag = POPUP_FIRST; menu[0].text = (char*)TR("Prima"); menu[0].enabled = true; + menu[1].tag = POPUP_PREV; menu[1].text = (char*)TR("Indietro"); menu[1].enabled = _page > 1; + menu[2].tag = POPUP_NEXT; menu[2].text = (char*)TR("Avanti"); menu[2].enabled = _last == 0 || _page < _last; + menu[3].tag = POPUP_LAST; menu[3].text = (char*)TR("Ultima"); menu[3].enabled = true; + menu[4].tag = -1; menu[4].separator = true; + menu[5].tag = POPUP_ZOOMIN; menu[5].text = (char*)TR("Zoom +"); menu[5].enabled = _zoom < 300; + menu[6].tag = POPUP_ZOOMOUT; menu[6].text = (char*)TR("Zoom -"); menu[6].enabled = _zoom > 75; + menu[7].tag = -1; menu[7].separator = true; + menu[8].tag = POPUP_GRID; menu[8].text = (char*)TR("Griglia"); menu[8].enabled = true; + menu[8].checkable = true; menu[8].checked = _printer->show_preview_grid(); + + const PNT& p = ep->v.mouse.where; + xvt_menu_popup(menu, win(), p, XVT_POPUP_CENTER, 0); +} + +void TPrint_preview_window::page_select() +{ + TMask m("Ricerca", 1, 28, 4); + m.add_number(101, 0, "Pagina ", 1, 1, 4, "U").check_type(CHECK_REQUIRED); + m.add_button(DLG_OK, 0, "", -12, -1, 10, 2); + m.add_button(DLG_CANCEL, 0, "", -22, -1, 10, 2); + if (m.run()) + { + _page = m.get_int(101); + if (_last > 0 && _page > _last) + _page = _last; + } +} + +void TPrint_preview_window::handler(WINDOW win, EVENT* ep) +{ + switch (ep->type) + { + case E_MOUSE_MOVE: + if (_printer->find_link(ep->v.mouse.where) != NULL) + xvt_win_set_cursor(win, CURSOR_CROSS); + else + xvt_win_set_cursor(win, CURSOR_ARROW); + break; + case E_MOUSE_DOWN: + if (ep->v.mouse.button == 0) + { + const TReport_link* lnk = _printer->find_link(ep->v.mouse.where); + if (lnk != NULL) + _printer->on_link(*lnk); + } + else + popup_menu(ep); + break; + case E_COMMAND: + { + bool processed = true; + switch(ep->v.cmd.tag) + { + case POPUP_FIRST : _page = 1; break; + case POPUP_PREV : if (_page > 1) _page--; break; + case POPUP_SEARCH : page_select(); break; + case POPUP_NEXT : if (_last == 0 || _page < _last) _page++; break; + case POPUP_LAST : + if (_last == 0) + { + _printer->print_page(9999); + _last = _printer->last_printed_page(); + } + _page = _last; + break; + case POPUP_ZOOMIN : if (_zoom < 300) { _zoom += 25; } break; + case POPUP_ZOOMOUT: if (_zoom > 75) { _zoom -= 25; } break; + case POPUP_GRID : _printer->toggle_preview_grid(); break; + default:processed = false; break; + } + if (processed) + { + _print_aborted = false; + force_update(); + } + } + break; + default: + break; + } + TField_window::handler(win, ep); +} + +bool TPrint_preview_window::on_key(KEY k) +{ + bool ok = true; + switch (k) + { + case '+' : dispatch_e_menu(win(), POPUP_ZOOMIN); break; + case '-' : dispatch_e_menu(win(), POPUP_ZOOMOUT); break; + case K_HOME : + case K_LHOME : dispatch_e_menu(win(), POPUP_FIRST); break; + case K_LEFT : dispatch_e_menu(win(), POPUP_PREV); break; + case K_RIGHT : dispatch_e_menu(win(), POPUP_NEXT); break; + case K_END: + case K_LEND : dispatch_e_menu(win(), POPUP_LAST); break; + case 'G' : + case 'g' : dispatch_e_menu(win(), POPUP_GRID); break; + default : ok = TField_window::on_key(k); break; + }; + return ok; +} + +TPrint_preview_window::TPrint_preview_window(int x, int y, int dx, int dy, WINDOW parent, + TWindowed_field* owner, TPage_printer* printer) +: TField_window(x, y, dx, dy, parent, owner), _printer(printer), + _page(1), _last(0), _zoom(120) +{ + RCT rct; xvt_vobj_get_client_rect(win(), &rct); + set_scroll_max(rct.right, rct.bottom); +} + +/////////////////////////////////////////////////////////// +// TPrint_preview_field +/////////////////////////////////////////////////////////// + +class TPrint_preview_field : public TWindowed_field +{ + TPage_printer* _printer; + +protected: + virtual TField_window* create_window(int x, int y, int dx, int dy, WINDOW parent); + +public: + TPrint_preview_field(TMask* m, TPage_printer* printer) : TWindowed_field(m), _printer(printer) { } +}; + +TField_window* TPrint_preview_field::create_window(int x, int y, int dx, int dy, WINDOW parent) +{ + return new TPrint_preview_window(x, y, dx, dy, parent, this, _printer); +} + +/////////////////////////////////////////////////////////// +// TPreview_mask +/////////////////////////////////////////////////////////// + +class TPreview_mask : public TAutomask +{ + TPage_printer* _printer; + +protected: + virtual bool on_key(KEY k); + virtual bool on_field_event(TOperable_field& o, TField_event e, long jolly); + virtual TMask_field* parse_field(TScanner& scanner); + +public: + TPreview_mask(TPage_printer* printer); +}; + +bool TPreview_mask::on_key(KEY k) +{ + switch (k) + { + case '+' : + case '-' : + case K_HOME : + case K_LHOME: + case K_LEFT : + case K_RIGHT: + case K_END : + case K_LEND : + case 'G' : + case 'g' : + dispatch_e_char(_printer->win(), k); + return true; + default: + break; + } + return TAutomask::on_key(k); +} + +bool TPreview_mask::on_field_event(TOperable_field& o, TField_event e, long jolly) +{ + if (e == fe_button) + { + switch (o.dlg()) + { + case DLG_FIRSTREC: dispatch_e_menu(_printer->win(), POPUP_FIRST); break; + case DLG_PREVREC : dispatch_e_menu(_printer->win(), POPUP_PREV); break; + case DLG_FINDREC : dispatch_e_menu(_printer->win(), POPUP_SEARCH);break; + case DLG_NEXTREC : dispatch_e_menu(_printer->win(), POPUP_NEXT); break; + case DLG_LASTREC : dispatch_e_menu(_printer->win(), POPUP_LAST); break; + default: break; + } + } + return true; +} + +TMask_field* TPreview_mask::parse_field(TScanner& scanner) +{ + if (scanner.token().starts_with("PR")) + return new TPrint_preview_field(this, _printer); + return TAutomask::parse_field(scanner); +} + +TPreview_mask::TPreview_mask(TPage_printer* printer) + : _printer(printer) +{ + read_mask("ba8303", 0, -1); + set_handlers(); + + int pos = id2pos(DLG_FINDREC); + if (pos >= 0 && id2pos(DLG_FIRSTREC) >= 0) //se e' un bottone pentapartito... + { + TButton_field& f_find = (TButton_field &)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 = id2pos(DLG_FIRSTREC); + if (pos >= 0) + { + r = rct_base; r.top = r.bottom - by; r.right = r.left + bx; + fld(pos).set_rect(r); + } + pos = id2pos(DLG_PREVREC); + if (pos >= 0) + { + r = rct_base; r.bottom = r.top + by; r.right = r.left + bx; + fld(pos).set_rect(r); + } + pos = id2pos(DLG_NEXTREC); + if (pos >= 0) + { + r = rct_base; r.bottom = r.top + by; r.left = r.right - bx; + fld(pos).set_rect(r); + } + pos = id2pos(DLG_LASTREC); + if (pos >= 0) + { + r = rct_base; r.top = r.bottom - by; r.left = r.right - bx; + fld(pos).set_rect(r); + } + } + } +} + +/////////////////////////////////////////////////////////// +// TPage_printer +/////////////////////////////////////////////////////////// + +const char* TPage_printer::form_name() const +{ + return printer().get_form_name(); +} + +const char* TPage_printer::font_name() const +{ + return printer().fontname(); +} + +int TPage_printer::font_size() const +{ + return printer().get_char_size(); +} + +bool TPage_printer::ask_pages() +{ + if (_pageto <= 0) + _pageto = pages(); + + TPrinter& p = printer(); + TMask msk("bagn003"); + msk.set(F_PRINTER, p.printername()); + msk.set(F_FORM, form_name()); + msk.set(F_FONT, font_name()); + msk.set(F_SIZE, font_size()); + msk.set(F_ISGRAPHICS, p.isgraphics() ? "X" : ""); + msk.set(F_FROMPAGE, _pagefrom); + msk.set(F_TOPAGE, _pageto); + msk.set(F_COPIES, _copies); + const bool ok = msk.run() == K_ENTER; + if (ok) + { + _copies = msk.get_int(F_COPIES); + _pagefrom = msk.get_int(F_FROMPAGE); + _pageto = msk.get_int(F_TOPAGE); + if (_pageto < _pagefrom) + _pageto = 0; + } + return ok; +} + +bool TPage_printer::main_loop() +{ + TPrinter& p = printer(); + _rcd = p.get_printrcd(); + if (!xvt_print_is_valid(_rcd)) + return false; // aborted + + WINDOW prwin = xvt_print_create_win(_rcd, (char*)(const char*)form_name()); + if (prwin == NULL_WIN) + return false; // aborted + set_win(prwin); + + xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, _rcd, &_ph, &_pw, &_pvr, &_phr); + + if (_pw <= 0 || _ph <= 0) + return error_box(TR("Dimensioni pagina NULLE")); + + bool ok = true; + if (_pvr < 150 || _phr < 150) + { + ok = yesno_box(TR("Stampante obsoleta o non adeguatamente configurata:\n" + "Risoluzione %ldx%ld. Continuare ugualmente?"), _phr, _pvr); + if (!ok) + return false; + } + _print_aborted = false; + + for (word c = 0; c < _copies && ok; c++) + { + _page = 0; + ok = print_loop(); + } + _print_aborted = false; + + xvt_vobj_destroy(prwin); + set_win(NULL_WIN); + + return ok; +} + +bool TPage_printer::page_in_range() const +{ + if (_page < _pagefrom) + return false; + return _pageto < _pagefrom || _page <= _pageto; +} + +PNT TPage_printer::log2dev(long lx, long ly) const +{ + if (preview_mode()) + return _preview_window->log2dev(lx, ly); + const PNT p = { short(ly), short(lx) }; + return p; +} + +TPoint TPage_printer::dev2log(const PNT& pnt) const +{ + CHECK(0, "dev2log: Pure virtual funtion call"); + const TPoint p; + return p; +} + +bool TPage_printer::open_page() +{ + _page++; + if (page_in_range()) + { + if (print_mode()) + _page_is_open = xvt_print_open_page(_rcd) != 0; + else + { + _page_is_open = true; + + _links.destroy(); // Distrugge elenco dei links + _links_sorted = false; + } + } + else + _page_is_open = false; + + return _page_is_open; +} + +bool TPage_printer::close_page() +{ + const bool was_open = _page_is_open; + if (was_open) + { + if (print_mode()) + xvt_print_close_page(_rcd); + + _page_is_open = false; + } + return was_open; +} + +void TPage_printer::add_link(TReport_link* link) +{ + _links.add(link); + _links_sorted = false; +} + +static BOOLEAN main_loop_callback(long jolly) +{ + TPage_printer* pp = (TPage_printer*)jolly; + return pp->main_loop(); +} + +bool TPage_printer::do_print() +{ + _print_aborted = true; + bool ok = ask_pages(); + if (ok) + ok = xvt_print_start_thread(main_loop_callback, long(this)) == FALSE; + return ok; +} + +void TPage_printer::print_page(word page) +{ + _pagefrom = _pageto = page; + _page = 0; + print_loop(); +} + +bool TPage_printer::do_preview() +{ + _print_aborted = true; + + TPrinter& p = printer(); + _rcd = p.get_printrcd(); + if (!xvt_print_is_valid(_rcd)) + return error_box(TR("Stampante non valida")); + + xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, _rcd, &_ph, &_pw, &_pvr, &_phr); + if (_pw <= 0 || _ph <= 0) + return error_box(TR("Dimensioni pagina NULLE")); + + _preview_mask = new TPreview_mask(this); + TPrint_preview_field& pf = (TPrint_preview_field&)_preview_mask->field(DLG_USER); + _preview_window = &pf.win(); + + set_win(_preview_window->win()); + _pagefrom = _pageto = _page = 1; + _print_aborted = false; + + const KEY key = _preview_mask->run(); + set_win(NULL_WIN); + + delete _preview_mask; + _preview_mask = NULL; + + if (key == K_INS) + print(); + + return true; +} + +const TReport_link* TPage_printer::find_link(const PNT& pnt) const +{ + if (!_links_sorted) + { + TPage_printer* myself = (TPage_printer*)this; + myself->_links.sort(); + myself->_links_sorted = true; + } + + int primo = 0, ultimo = _links.last(); + while (primo <= ultimo) + { + const int in_mezzo = (primo+ultimo)/2; + const TReport_link* lnk = (const TReport_link*)_links.objptr(in_mezzo); + const int cmp = lnk->hit_test(pnt); + if (cmp == 0) + return lnk; + if (cmp < 0) + ultimo = in_mezzo-1; + else + primo = in_mezzo+1; + } + return NULL; +} + +bool TPage_printer::on_link(const TReport_link& lnk) +{ + return false; +} + +bool TPage_printer::print(bool prev_mode) +{ + return prev_mode ? do_preview() : do_print(); +} + +TPoint TPage_printer::page_size() const +{ + return TPoint(_pw, _ph); +} + +TPoint TPage_printer::page_res() const +{ + TPoint pt(_phr, _pvr); + return pt; +} + +TPage_printer::TPage_printer() + : _pagefrom(1), _pageto(0), _copies(1), _preview_mask(NULL), _draw_grid(true) +{ + _pixmap = true; // Fondamentale!!! +} + +TPage_printer::~TPage_printer() +{ } + + +/////////////////////////////////////////////////////////// +// TReport_printer +/////////////////////////////////////////////////////////// + +const char* TReport_printer::form_name() const +{ + return _report.filename(); +} + +const char* TReport_printer::font_name() const +{ + return _report.font().name(); +} + +int TReport_printer::font_size() const +{ + return _report.font().size(); +} + +PNT TReport_printer::log2dev(long lx, long ly) const +{ + const TPoint res = page_res(); + const double cx = (double)res.x / (double)_report.cpi(); + const double cy = (double)res.y / (double)_report.lpi(); + const long x = long((lx + _delta.x) * cx / 100.0); + const long y = long((ly + _delta.y) * cy / 100.0); + + return TPage_printer::log2dev(x, y); // Useful for preview mode +} + +long TReport_printer::print_section(char type, int level) +{ + long h = 0; + TReport_section* rs = _report.find_section(type, level); + if (rs != NULL) + h = print_section(*rs); + return h; +} + +void TReport_printer::draw_preview_page() +{ + clear(COLOR_WHITE); + + if (_draw_grid) + { + const int max = 192; + const int k = 100; + for (int i = 1; i < max; i++) + { + set_pen((i%10) ? MAKE_COLOR(232,232,232) : COLOR_LTGRAY); + line(0, i*k, max*k, i*k); + line(i*k, 0, i*k, max*k); + } + } + + const PNT pag = log2dev(_logical_page_width, _logical_page_height); + hide_pen(); + set_brush(COLOR_GRAY); + RCT rct; + xvt_vobj_get_client_rect(win(), &rct); rct.left = pag.h; + xvt_dwin_draw_rect(win(), &rct); + xvt_vobj_get_client_rect(win(), &rct); rct.top = pag.v; + xvt_dwin_draw_rect(win(), &rct); +} + +bool TReport_printer::open_page() +{ + const bool ok = TPage_printer::open_page(); + + _report.set_curr_page(_page); + + _delta.reset(); + _page_break_allowed = false; + + if (ok && preview_mode()) // Clear page needed + draw_preview_page(); + + TReport_section* page_background = _report.find_section('B', 0); + if (page_background != NULL) + { + _delta = page_background->pos(); + print_section(*page_background); + _delta.reset(); + } + + if (_page == 1) + _delta.y += print_section('H', 1); + + TReport_section* page_head = _report.find_section('H', 0); + if (page_head != NULL && (_page > 1 || !page_head->hidden_if_needed())) + { + _delta += page_head->pos(); + _delta.y += print_section(*page_head); + _delta.x = 0; + } + + return ok; +} + +bool TReport_printer::close_page() +{ + if (_page_is_open) + { + TReport_section* page_foot = _report.find_section('F', 0); + if (page_foot != NULL && (!_is_last_page || !page_foot->hidden_if_needed())) + { + _delta.x = page_foot->pos().x; + _delta.y = _logical_foot_pos; + print_section(*page_foot); + } + } + return TPage_printer::close_page(); +} + +void TReport_printer::create_links(const TReport_section& rs) +{ + TAssoc_array* links = NULL; + for (int i = 0; i < rs.items(); i++) + { + const TReport_field& rf = rs.field(i); + if (rf.link().not_empty()) + { + if (links == NULL) + links = new TAssoc_array; + TToken_string tok(rf.link(), '.'); + TString table, field; + tok.get(0, table); tok.get(1, field); + TReport_link* rl = (TReport_link*)links->objptr(table); + if (rl == NULL) + { + rl = new TReport_link(table); + links->add(table, rl); + } + RCT rct; TWindow::log2dev(rf.get_rect(), rct); + rl->add_rect(rct); + rl->set(field, rf.get()); + } + } + if (links != NULL) + { + FOR_EACH_ASSOC_OBJECT((*links), h, key, l) + add_link((TReport_link*)l); + } +} + +long TReport_printer::print_section(TReport_section& rs) +{ + if (_print_aborted) + return 0; + + rs.load_fields(); + rs.execute_prescript(); + const long height = rs.compute_size().y; // Compute size after the initilization script! + + if (height > 0) // Has some visible fields + { + bool page_break = _page_break_allowed && rs.page_break(); + if (!page_break) + { + long h = height; + if (rs.keep_with_next()) + h += rs.report().section('B', 1).compute_size().y; + page_break = (_delta.y + h > _logical_foot_pos); + } + if (page_break && rs.level() > 0) // Avoid recursion + { + close_page(); + open_page(); + } + if (_page_is_open) + rs.draw(*this, preview_mode() ? rdm_print_preview : rdm_print); + + if (rs.level() > 0) // Ho stampa qualcosa che non sia lo sfondo! + _page_break_allowed = true; + } + + rs.execute_postscript(); + + if (_page_is_open && height > 0 && preview_mode()) + create_links(rs); + + return height; +} + +bool TReport_printer::print_loop() +{ + TRecordset* rex = _report.recordset(); + if (rex == NULL || rex->items() <= 0) + return false; + + const TPoint siz = page_size(); + const TPoint res = page_res(); + const double pollici_pagina_y = (double)siz.y / (double)res.y; + const double righe_pagina = pollici_pagina_y * _report.lpi(); + _logical_page_height = long(righe_pagina*100.0); + + const double pollici_pagina_x = (double)siz.x / (double)res.x; + const double colonne_pagina = pollici_pagina_x * _report.cpi(); + _logical_page_width = long(colonne_pagina*100.0); + + const TReport_section& footer = _report.section('F',0); + _logical_foot_pos = footer.pos().y; + if (_logical_foot_pos <= 0) + { + const long logical_footer_height = footer.compute_size().y; + _logical_foot_pos = _logical_page_height - logical_footer_height; + } + + _report.unmap_font(); // Gestire bene le riscalature + + TString_array oldgroup, newgroup; + const int max_group = _report.find_max_level('H'); + if (max_group >= 2) + { + for (int g = 2; g <= max_group; g++) + oldgroup.add(EMPTY_STRING, g); + } + const int max_body = _report.find_max_level('B'); + int last_body_height = 0; + + _is_last_page = false; + open_page(); + bool aborted = false; + for (bool ok = rex->move_to(0); ok; ok = rex->move_next()) + { + if (_pageto >= _pagefrom && _page > _pageto || _print_aborted) // out of range + { + aborted = true; + break; + } + + if (max_group >= 2) // Gestione raggruppamenti + { + int changed = 0; + TVariant var; + for (int g = 2; g <= max_group; g++) + { + const TString& expr = _report.section('H', g).grouped_by(); + _report.evaluate(expr, var, _alfafld); + const TString& grp = var.as_string(); + newgroup.add(grp, g); + if (newgroup.row(g) != oldgroup.row(g) || rex->current_row() == 0) + changed = g; + } + if (changed) + { + oldgroup = newgroup; + + if (_delta.x > 0) // Devo tornare a capo! + _delta.y += last_body_height; + _delta.x = 0; + + if (rex->current_row() > 0) + { + for (int g = 2; g <= changed; g++) + _delta.y += print_section('F', g); + } + for (int g = changed; g >= 2 ; g--) + _delta.y += print_section('H', g); + } + } + + for (int b = 1; b <= max_body; b++) + { + const int dy = print_section('B', b); + + int column_delta = 0; + // Cerco di vedere se e' possibile la stampa etichette + if (dy > 0) + { + const int dx = _report.section('B', b).size().x; + // Se dx > 0 ho una sezione a dimensione fissa + if (dx > 0 && _delta.x+2*dx <= _logical_page_width) + { + column_delta = dx; + last_body_height = dy; + } + } + if (column_delta > 0) + _delta.x += column_delta; + else + { + _delta.x = 0; + _delta.y += dy; + last_body_height = 0; // Non servirebbe strettamente + } + } + } + if (!aborted) + { + if (max_group >= 2) // Devo stampare tutte le code dei raggrupamenti + { + for (int g = 2; g <= max_group; g++) + _delta.y += print_section('F', g); + } + + if (rex->eof()) + print_section('F', 1); + _is_last_page = true; + close_page(); + } + _lastprinted = _page; + + return true; +} + +bool TReport_printer::print(bool preview_mode) +{ + bool ok = _report.execute_prescript(); + if (ok) + { + ok = TPage_printer::print(preview_mode); + if (ok) + _report.execute_postscript(); + } + return ok; +} + +bool TReport_printer::on_link(const TReport_link& lnk) +{ + return _report.on_link(lnk); +} + + +/////////////////////////////////////////////////////////// +// Remote control interface +/////////////////////////////////////////////////////////// + +void lock_preview_update(bool yes) +{ TPrint_preview_window::lock_preview_update(yes); } + +void abort_printing() +{ _print_aborted = true; } + diff --git a/include/reprint.h b/include/reprint.h new file mode 100755 index 000000000..330a37776 --- /dev/null +++ b/include/reprint.h @@ -0,0 +1,100 @@ +#ifndef __REPRINT_H +#define __REPRINT_H + +#ifndef __REPORT_H +#include +#endif + +class TPage_printer : public TWindow +{ + PRINT_RCD* _rcd; + long _pw, _ph, _phr, _pvr; // Printer width, height, horizontal and vertical resolution + TArray _links; + bool _links_sorted; + +protected: + word _copies, _pagefrom, _pageto, _page, _lastprinted; + bool _page_is_open, _draw_grid; + + TMask* _preview_mask; // owned object + TWindow* _preview_window; // referenced object + + virtual word pages() const { return 0; } + virtual bool print_loop() pure; + +private: + virtual TPoint dev2log(const PNT& p) const; // Never use this! + +protected: + virtual PNT log2dev(long lx, long ly) const; // Always override this! + + virtual const char* form_name() const; + virtual const char* font_name() const; + virtual int font_size() const; + virtual bool ask_pages(); + + virtual bool open_page(); + virtual bool close_page(); + bool page_in_range() const; + + // Called by print(bool) + virtual bool do_print(); + virtual bool do_preview(); + +public: + bool main_loop(); // Internal use only + virtual void print_page(word page); // Inefficient default implementation + + virtual TPoint page_size() const; + virtual TPoint page_res() const; + virtual word last_printed_page() { return _lastprinted; } + + virtual bool print(bool preview = false); + bool print_mode() const { return _preview_mask == NULL; } + bool preview_mode() const { return _preview_mask != NULL; } + + void toggle_preview_grid() { _draw_grid = !_draw_grid; } + bool show_preview_grid() const { return _draw_grid; } + + void add_link(TReport_link* link); + const TReport_link* find_link(const PNT& pnt) const; + virtual bool on_link(const TReport_link& lnk); + + TPage_printer(); + virtual ~TPage_printer(); +}; + +class TReport_printer : public TPage_printer +{ + TReport& _report; + TPoint _delta; + long _logical_page_height, _logical_page_width; + long _logical_foot_pos; + bool _is_last_page, _page_break_allowed; + +protected: + virtual const char* form_name() const; + virtual const char* font_name() const; + virtual int font_size() const; + + virtual PNT log2dev(long x, long y) const; + virtual bool print_loop(); + + virtual void draw_preview_page(); + virtual bool open_page(); + virtual bool close_page(); + long print_section(TReport_section& rs); + long print_section(char type, int level); + void create_links(const TReport_section& rs); + +public: + virtual bool print(bool preview = false); + virtual bool on_link(const TReport_link& lnk); + + TReport_printer(TReport& r) : _report(r) { } +}; + +void lock_preview_update(bool yes); +void abort_printing(); + +#endif diff --git a/include/sheet.cpp b/include/sheet.cpp index a112a8f7e..136fdaf71 100755 --- a/include/sheet.cpp +++ b/include/sheet.cpp @@ -17,7 +17,7 @@ class TSheet_control : public TControl { - enum { FIRST_FIELD = 101, MAX_COL = 128 }; + enum { FIRST_FIELD = 101, MAX_COL = 256 }; TSheet* _sheet; long _cur_rec; @@ -117,15 +117,14 @@ TSheet_control::TSheet_control( TToken_string header(head); TToken_string new_header(256); - int i = 0; int f_width = NUMBER_WIDTH; // Stima larghezza colonne fisse int max_width = f_width; // Stima larghezza della colonna piu' grande int lines_in_header = 1; const char * h; - for (h = header.get(); h; h = header.get(), i++) + int i = 0; + for (h = header.get(); h && i < MAX_COL; h = header.get(), i++) { - CHECKD(i < MAX_COL, "Tu meni calumns in scit: ", i); _type[i] = ' '; TString testa(h); @@ -236,7 +235,7 @@ TSheet_control::TSheet_control( coldef->v.column->column_platform = TRUE; coldef->v.column->center_heading = TRUE; - for (h = new_header.get(0), i = 0; h; h = new_header.get(), i++) + for (h = new_header.get(0), i = 0; h && i < MAX_COL; h = new_header.get(), i++) { long attr = XI_ATR_VISIBLE | XI_ATR_ENABLED | XI_ATR_AUTOSCROLL | XI_ATR_READONLY; if (_type[i] == 'C') diff --git a/include/window.h b/include/window.h index b25fe08ee..779311985 100755 --- a/include/window.h +++ b/include/window.h @@ -66,7 +66,7 @@ public: bool contains(const TPoint& p) const; bool intersects(const TRectangle& r) const; void merge(const TRectangle& r); - bool is_empty() const { return _size.x > 0 && _size.y > 0; } + bool is_empty() const { return _size.x == 0 || _size.y == 0; } TRectangle& operator=(const TRectangle& r) { copy(r); return *this; } TRectangle() : TPoint(0,0), _size(0,0) { }