#include #include #include #include #include #include "ba8201.h" #include "ba8301.h" #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); if (_fontid != NULL) xvt_font_destroy(_fontid); TReport_font& my = *(TReport_font*)this; my._fontid = fontid; my._win_mapped = win.win(); xvt_dwin_get_font_metrics(_win_mapped, &my._leading, &my._ascent, &my._descent); } return _fontid; } void TReport_font::create(const char* name, int size, XVT_FONT_STYLE_MASK style) { _name = name; _size = size; _style = style; _cpi = 120 / _size; if (_fontid != NULL) { xvt_font_destroy(_fontid); _fontid = NULL; _win_mapped = NULL_WIN; } } 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.FindFirst("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() { if (_fontid != NULL) xvt_font_destroy(_fontid); } /////////////////////////////////////////////////////////// // 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) { win.set_pen(fore, border, has_pen ? PAT_SOLID : PAT_HOLLOW); win.set_brush(back, has_brush ? PAT_SOLID : PAT_HOLLOW); xvt_dwin_draw_rect(win.win(), (RCT*)&r); } } 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; } xvt_dwin_draw_text(win.win(), x, y, text, -1); } /////////////////////////////////////////////////////////// // 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); } 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); } TPoint TReport_section::compute_size() const { TPoint s = _size; for (int i = 0; i < items(); i++) { const TReport_field& rf = field(i); const TRectangle& r = rf.get_rect(); if (r.right() > s.x) s.x = r.right(); if (r.bottom() > s.y) s.y = r.bottom(); } 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 { for (int i = 0; i < items(); i++) { const TReport_field& f = field(i); f.draw(win, rdm); } } void TReport_section::compute_values() { for (int i = 0; i < items(); i++) { TReport_field& f = field(i); f.compute_value(); } } TReport_section::TReport_section(TReport& r, char t, int l) : _report(r), _type(t), _level(l), _font(NULL), _size(0,0), _hidden_if_needed(false) { } TReport_section::~TReport_section() { if (_font) delete _font; } /////////////////////////////////////////////////////////// // 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; } 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::copy(const TReport_field& rf) { _section = rf._section; _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; _selected = false; set_font(rf.font()); } const char* TReport_field::type_name() const { const char* n = NULL; switch (_type) { 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 'R': n = "Rettangolo"; break; case 'S': n = "Stringa"; break; case 'T': n = "Testo"; break; default : break; } return n; } bool TReport_field::compute_value() { const TRecordset* rex = section().report().recordset(); if (rex) _value = rex->get(_field); else _value.cut(0); return rex != NULL; } 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()); } void TReport_field::draw_text(TWindow& win, const char* text) const { RCT r; win.log2dev(get_rect(), r); advanced_draw_rect(win, r, border(), fore_color(), back_color()); xvt_dwin_set_font(win.win(), font().get_xvt_font(win)); win.set_color(fore_color(), back_color()); advanced_draw_text(win, text, r, _halign, _valign); } void TReport_field::draw(TWindow& win, TReport_draw_mode rdm) const { switch (_type) { case 'E': { const bool has_pen = border() > 0; const bool has_brush = color_distance(back_color(), COLOR_WHITE) != 0; win.set_pen(fore_color(), border(), has_pen ? PAT_SOLID : PAT_HOLLOW); win.set_brush(back_color(), has_brush ? PAT_SOLID : PAT_HOLLOW); RCT r; win.log2dev(get_rect(), r); xvt_dwin_draw_oval(win.win(), &r); } 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()); } break; case 'R': draw_rect(win); break; case 'T': draw_text(win, _picture); break; default : draw_text(win, rdm == rdm_edit ? _field : _value); break; } } 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); } } void TReport_field::save(TXmlItem& root) const { TXmlItem& fld = root.AddChild("field"); fld.SetAttr("type", type_name()); const TRectangle& rct = get_rect(); 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); 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); 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 'T': fld.SetAttr("valign", "top"); break; case 'C': fld.SetAttr("valign", "center"); break; default : break; }; fld.SetAttr("border", border()); fld.SetAttr("text", picture()); if (field().not_empty()) fld.AddChild("source") << field(); } 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; } bool TReport_field::load(const TXmlItem& fld) { set_type(get_chr_attr(fld, "type", 'T')); 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)); 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", 'B')); set_picture(fld.GetAttr("text")); TXmlItem* src = fld.FindFirst("source"); if (src != NULL) src->GetEnclosedText(_field); TReport_font font; if (font.load(fld)) set_font(font); return true; } TReport_field::TReport_field(TReport_section* sec) : _section(sec), _type('T'), _selected(false), _font(NULL), _border(0), _fgcolor(COLOR_BLACK), _bgcolor(COLOR_WHITE) { set_pos(0,0); set_size(1600,100); } TReport_field::TReport_field(const TReport_field& rf) : _font(NULL) { copy(rf); } TReport_field::~TReport_field() { if (_font != NULL) delete _font; } /////////////////////////////////////////////////////////// // 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); return _sections.remove(key); } int TReport::find_max_level(char type) const { int lev = 1; TAssoc_array& ass = (TAssoc_array&)_sections; FOR_EACH_ASSOC_OBJECT(ass, h, k, o) if (k[0] == type) { const int l = k[1]-'0'; 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", 6, true) == 0) ok = set_recordset(new TSQL_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; key.format("%c%d", type, level); _sections.add(key, sec); } return *sec; } 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.hide_if_needed(sec.GetIntAttr("hidden_if_needed") != 0); TReport_font font; if (font.load(sec)) rs.set_font(font); if (level > 1) { const TXmlItem* gb = xml.FindFirst("groupby"); if (gb != NULL) { TString str; gb->GetEnclosedText(str); rs.group_by(str); } } for (int j = 0; j < sec.GetChildren(); j++) { const TXmlItem& fld = *sec.GetChild(j); if (fld.GetTag() != "field") continue; TReport_field* rf = new TReport_field(&rs); if (rf->load(fld)) rs.add(rf); else delete rf; } } } 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.FindFirst("description"); if (desc != NULL) desc->GetEnclosedText(_description); if (xml.FindFirst("section") != NULL) load_sections(xml); const TXmlItem* sql = xml.FindFirst("sql"); if (sql != NULL) { TString str; sql->GetEnclosedText(str); set_recordset(str); } } return ok; } void TReport::save_section(const TReport_section& rs, TXmlItem& xml) const { TXmlItem& item = xml.AddChild("section"); char* tipo = NULL; switch (rs.type()) { case 'H': tipo = "Head"; break; case 'F': tipo = "Foot"; break; default : tipo = "Body"; break; } item.SetAttr("type", tipo); item.SetAttr("level", rs.level()); item.SetAttr("hidden_if_needed", rs.hidden_if_needed()); if (rs.grouped_by().not_empty()) item.AddChild("groupby") << rs.grouped_by(); if (rs.has_font()) rs.font().save(item); for (int i = 0; i < rs.items(); i++) { const TReport_field& rf = rs.field(i); rf.save(item); } } 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; _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 = find_section(sectype[j], i); if (rs != NULL && (rs->height() > 0 || rs->items() > 0)) save_section(*rs, xml); } } if (_recordset != NULL) xml.AddChild("sql") << _recordset->query_text(); xml.Save(fname); } return ok; } TReport::TReport() : _lpi(6), _recordset(NULL) { } TReport::~TReport() { destroy(); } /////////////////////////////////////////////////////////// // 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 TRUE; // aborted WINDOW prwin = xvt_print_create_win(_rcd, (char*)(const char*)form_name()); if (prwin == NULL_WIN) return TRUE; // aborted set_win(prwin); xvt_app_escape (XVT_ESC_GET_PRINTER_INFO, _rcd, &_ph, &_pw, &_pvr, &_phr); bool ok = true; for (word c = 0; c < _copies && ok; c++) { _page = 0; ok = print_loop(); } 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; } bool TPage_printer::open_page() { _page++; if (page_in_range()) _page_is_open = xvt_print_open_page(_rcd) != 0; else _page_is_open = false; return _page_is_open; } bool TPage_printer::close_page() { const bool was_open = _page_is_open; if (was_open) { xvt_print_close_page(_rcd); _page_is_open = false; } return was_open; } static BOOLEAN main_loop_callback(long jolly) { TPage_printer* pp = (TPage_printer*)jolly; return pp->main_loop(); } bool TPage_printer::print() { bool ok = ask_pages(); if (ok) ok = xvt_print_start_thread(main_loop_callback, long(this)) == FALSE; return ok; } TPage_printer::TPage_printer() : _pagefrom(1), _pageto(0), _copies(1) { } 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 double cx = (double)_phr / (double)_report.cpi(); const double cy = (double)_pvr / (double)_report.lpi(); const short x = short((lx + _delta.x) * cx / 100.0); const short y = short((ly + _delta.y) * cy / 100.0); const PNT pnt = { y, x }; return pnt; } TPoint TReport_printer::dev2log(const PNT& pnt) const { CHECK(0, "dev2log: Pure virtual funtion call"); const TPoint p; return p; } 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; } bool TReport_printer::open_page() { const bool ok = TPage_printer::open_page(); _delta.x = _delta.y = 0; print_section('B', 0); 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.y += print_section(*page_head); 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 = 0; _delta.y = _logical_foot_pos; print_section(*page_foot); } } return TPage_printer::close_page(); } long TReport_printer::print_section(TReport_section& rs) { const long height = rs.compute_size().y; if (_delta.y + height > _logical_foot_pos && rs.level() > 0) // Avoid recursion { close_page(); open_page(); } rs.compute_values(); if (_page_is_open) rs.draw(*this, rdm_print); return height; } bool TReport_printer::print_loop() { if (_report.recordset() == NULL) return false; TRecordset& rex = *_report.recordset(); if (rex.items() <= 0) return false; _kx = double(_phr) / double(_report.cpi()*100.0); _ky = double(_pvr) / double(_report.lpi()*100.0); const double pollici_pagina = (double)_ph / (double)_pvr; const double righe_pagina = pollici_pagina * _report.lpi(); _logical_page_height = long(righe_pagina*100.0); const long logical_footer_height = _report.section('F',0).compute_size().y; _logical_foot_pos = _logical_page_height - logical_footer_height; _is_last_page = false; open_page(); for (bool ok = rex.move_to(0); ok; ok = rex.move_next()) { if (_pageto >= _pagefrom && _page > _pageto) // out of range break; _delta.y += print_section('B', 1); } if (rex.eof()) print_section('F', 1); _is_last_page = true; close_page(); return true; }