#include #include #include #include #include "ba0102.h" /////////////////////////////////////////////////////////// // TMenu_tree /////////////////////////////////////////////////////////// const TSubmenu& TMenu_tree::curr_submenu() const { CHECKS(_submenu, "NULL submenu ", (const char*)_curr_id); return *_submenu; } const TMenuitem& TMenu_tree::curr_item() const { const TSubmenu& sm = curr_submenu(); if (_menuitem < 0 || _menuitem >= sm.items()) { NFCHECK("Invalid submenu item %d in %s", _menuitem, (const char*)sm.name()); return sm.item(0); } return sm.item(_menuitem); } struct TFind_node_data { TString _id; size_t _count; }; struct TFind_string_data { TString _str; TAssoc_array* _ignore_list; }; HIDDEN bool find_string_callback(TTree& tree, void* jolly, word flags) { if (flags == SCAN_PRE_ORDER) { TMenu_tree& mt = (TMenu_tree&)tree; const TSubmenu& sm = mt.curr_submenu(); if (sm.disabled()) return FALSE; TFind_string_data& data = *(TFind_string_data*)jolly; if (data._ignore_list->is_key(sm.name())) return FALSE; TString desc; mt.get_description(desc); desc.upper(); if (desc.find(data._str) >= 0 || desc.match(data._str)) return TRUE; } return FALSE; } HIDDEN bool find_node_callback(TTree& tree, void* jolly, word flags) { if (flags == SCAN_PRE_ORDER) { TFind_node_data& data = *(TFind_node_data*)jolly; data._count++; TString id; tree.curr_id(id); if (id == data._id) return TRUE; } return FALSE; } HIDDEN bool find_leaf_callback(TTree& tree, void* jolly, word flags) { if (flags == SCAN_PRE_ORDER) { const TString& leaf = *(TString*)jolly; TString id; tree.curr_id(id); const int slash = id.rfind('/'); if (slash > 0) id = id.mid(slash+1); if (id == leaf) return TRUE; } return FALSE; } bool TMenu_tree::find_string(const TString& str) { TFind_string_data data; data._str = str; data._str.upper(); data._ignore_list = &_menu->search_ignore_list(); if (data._str != _menu->last_search_string()) { _menu->last_search_string() = data._str; _menu->search_ignore_list().destroy(); } goto_root(); bool ok = scan_depth_first(find_string_callback, &data, SCAN_PRE_ORDER); if (ok) _menu->search_ignore_list().add(curr_submenu().name()); else _menu->search_ignore_list().destroy(); return ok; } bool TMenu_tree::find_leaf(const TString& str) { goto_root(); bool ok = scan_depth_first(find_leaf_callback, (void *)&str, SCAN_PRE_ORDER); return ok; } void TMenu_tree::node2id(const TObject* node, TString& id) const { TString* str = (TString*)node; id = *str; } bool TMenu_tree::goto_root() { TSubmenu* sm = _menu->find(_root_id); if (sm) { _curr_id = _root_id; _curr_id << ".0"; _submenu = sm; _menuitem = 0; } return sm != NULL; } bool TMenu_tree::goto_firstson() { const TMenuitem& mi = curr_item(); if (mi.is_submenu()) { const TSubmenu* sm = _menu->find(mi.action()); if (sm && sm->items() > 0) { _curr_id << '/' << mi.action() << ".0"; _submenu = sm; _menuitem = 0; return TRUE; } } return FALSE; } bool TMenu_tree::goto_rbrother() { if (_menuitem < _submenu->items()-1) { const int dot = _curr_id.rfind('.'); _curr_id.cut(dot+1); _curr_id << (++_menuitem); return TRUE; } return FALSE; } bool TMenu_tree::goto_node(const TString &id) { const int dot = id.rfind('.'); CHECKS(dot > 0, "Invalid tree node ", (const char*)id); _menuitem = atoi(id.mid(dot+1)); _curr_id = id.left(dot); const int slash = _curr_id.rfind('/'); _curr_id = _curr_id.mid(slash+1); _submenu = _menu->find(_curr_id); _curr_id = id; return _submenu != NULL; } bool TMenu_tree::has_son() const { const TMenuitem& mi = curr_item(); return mi.is_submenu(); } bool TMenu_tree::has_rbrother() const { return _menuitem < _submenu->items()-1; } bool TMenu_tree::has_root() const { return _root_id.not_empty(); } bool TMenu_tree::has_father() const { return _curr_id.find('/') > 0; } bool TMenu_tree::has_lbrother() const { return _menuitem > 0; } bool TMenu_tree::goto_father() { const int slash = _curr_id.rfind('/'); if (slash > 0) { const TString id = _curr_id.left(slash); return goto_node(id); } return FALSE; } bool TMenu_tree::goto_lbrother() { if (_menuitem > 0) { const int dot = _curr_id.rfind('.'); _curr_id.cut(dot+1); _curr_id << (--_menuitem); return TRUE; } return FALSE; } TObject* TMenu_tree::curr_node() const { return &((TMenu_tree*)this)->_curr_id; } bool TMenu_tree::get_description(TString& desc) const { const TMenuitem& mi = curr_item(); desc = mi.caption(); return desc.not_empty(); } TImage* TMenu_tree::image(bool selected) const { const TMenuitem& mi = curr_item(); if (mi.disabled()) return get_res_image(BMP_STOP); return TTree::image(selected); } long TMenu_tree::find_node(const TString& id) { TFind_node_data data; data._id = id; data._count = 0; goto_root(); scan_depth_first(find_node_callback, &data, SCAN_PRE_ORDER | SCAN_IGNORING_UNEXPANDED); return data._count; } TMenu_tree::TMenu_tree(TMenu& menu) : _menu(&menu), _curr_id(128, '/') { _root_id = _menu->current().name(); goto_root(); } /////////////////////////////////////////////////////////// // Utility /////////////////////////////////////////////////////////// void synchronize_tree_field(TTree_field& tf) { TMenu_tree& mt = *(TMenu_tree*)tf.tree(); tf.select_current(); TString id; mt.curr_id(id); // Memorizza nodo corrente mt.shrink_all(); mt.goto_node(id); do { mt.expand(); } while (mt.goto_father()); mt.goto_node(id); tf.set_tree(&mt); // Azzera origine TField_window& win = tf.win(); win.force_update(); } /////////////////////////////////////////////////////////// // TMenulist_field /////////////////////////////////////////////////////////// class TMenulist_images : public TCache { WINDOW _win; protected: TObject* key2obj(const char* key); bool can_be_transparent(const TImage& i) const; public: void set_owner(WINDOW win) { _win = win; } TImage* image(const TString& filename); TMenulist_images() : TCache(17), _win(NULL_WIN) { } }; bool TMenulist_images::can_be_transparent(const TImage& i) const { const int w = i.width()-1; const int h = i.height()-1; const COLOR col = i.get_pixel(0,0); if (i.get_pixel(w,0) != col) return FALSE; if (i.get_pixel(w,h) != col) return FALSE; if (i.get_pixel(0,h) != col) return FALSE; return TRUE; } inline int fast_hypot(int x, int y) { // loop unrolled #define TEST(s, h, i) { const int k = h+i; if (k*k <= s) h = k; } const int s = x*x + y*y; int h = 0; TEST(s, h, 512); TEST(s, h, 256); TEST(s, h, 128); TEST(s, h, 64); TEST(s, h, 32); TEST(s, h, 16); TEST(s, h, 8); TEST(s, h, 4); TEST(s, h, 2); TEST(s, h, 1); return h; } TObject* TMenulist_images::key2obj(const char* key) { TImage* img = NULL; TFilename name; const char* ext[3] = { "jpg", "gif", "bmp" }; for (int i = 0; i < 3; i++) { name = key; name << '.' << ext[i]; name.custom_path(); if (name.exist()) break; } if (name.exist()) { TWait_cursor hourglass; TImage image(name); if (can_be_transparent(image)) image.convert_transparent_color(NORMAL_BACK_COLOR); const int w = image.width(); const int h = image.height(); const int radius = 3*min(w, h)/4; const clock_t start_timer = clock(); for (int y = h-1; y >= 0; y--) { for (int x = w-1; x >= 0; x--) { const int r = fast_hypot(x-w/2, y-h/2); if (r < radius) { // const double perc = 0.7 - (0.7 * r / radius); const double perc = 0.5 - (0.5 * r / radius); COLOR col = image.get_pixel(x, y); COLOR bri = blend_colors(col, NORMAL_BACK_COLOR, perc); image.set_pixel(x, y, bri); } else image.set_pixel(x, y, NORMAL_BACK_COLOR); } } const clock_t stop_timer = clock()-start_timer; RCT rct; xvt_vobj_get_client_rect(_win, &rct); const double ratiox = double(rct.right) / image.width(); const double ratioy = double(rct.bottom) / image.height(); const double ratio = max(ratiox, ratioy); const int maxx = int(ratio * image.width())-2; const int maxy = int(ratio * image.height())-2; img = new TImage(image, maxx, maxy); } return img; } TImage* TMenulist_images::image(const TString& filename) { TObject* obj = objptr(filename); if (obj == NULL && filename != "ba00") obj = objptr("ba00"); return (TImage*)obj; } class TMenulist_window : public TField_window { enum { MENU_COLS = 3, MENU_ROWS = 5 }; private: TMenu_tree* _tree; TString _curr_node; bool _can_go_back; TMenulist_images _images; TString _image_name; int _selected; TPointer_array _sorted; protected: virtual void update(); virtual void handler(WINDOW win, EVENT* ep); virtual bool on_key(KEY k); void synchronize_buddy_tree() const; void draw_item(int i); void click_on(int index); void select(int s, int direction); public: void set_menu(TMenu_tree& mt); TMenulist_window(int x, int y, int dx, int dy, WINDOW parent, TMenulist_field* owner); virtual ~TMenulist_window(); }; void TMenulist_window::draw_item(int i) { if (i < 0 && i >= _sorted.items()) return; // Scarta elementi non validi RCT rct; xvt_vobj_get_client_rect(win(), &rct); const int width = rct.right - rct.left; const int height = rct.bottom - rct.top; xvt_set_font(win(), NULL, 0, 0); // Set default font set_opaque_text(TRUE); const TMenuitem& item = (const TMenuitem&)_sorted[i]; if (i == _selected && item.enabled()) set_color(item.color(), FOCUS_BACK_COLOR); else { COLOR bc = item.enabled() ? REQUIRED_BACK_COLOR : DISABLED_BACK_COLOR; set_color(item.color(), bc); } const int row = i / MENU_COLS; const int col = i % MENU_COLS; const int left = col * width / MENU_COLS; const int right = (col+1) * width / MENU_COLS; const int top = row * height / MENU_ROWS; const int bottom = (row+1) * height / MENU_ROWS; const int maxchars = (right-left)/CHARX - 1; const int cx = (left+right)/2; const int cy = (top+bottom)/2; const int ico = item.enabled() ? item.icon() : 0; const int ix = cx-16; const int iy = top+2; if (item.is_submenu()) { xvt_dwin_draw_icon(win(), ix, iy, 10202); if ( ico > 0) xvt_dwin_draw_icon(win(), ix, iy+4, ico); } else { xvt_dwin_draw_icon(win(), ix, iy, ico > 0 ? ico : ICON_RSRC); } TString str = item.caption(); if (i == 0 && _can_go_back) str.insert("(..)\n"); TParagraph_string para(str, maxchars); int y = iy + 32 + CHARY-2; FOR_EACH_TOKEN(para, line) { const int ll = xvt_dwin_get_text_width(win(), line, -1); const int x = cx - ll/2; xvt_dwin_draw_text(win(), x, y, line, -1); y += CHARY-2; } if (item.disabled()) xvt_dwin_draw_icon(win(), ix+4, iy+4, 10203); // Stop icon } void TMenulist_window::update() { const bool db = _tree != NULL && ADVANCED_GRAPHICS; TImage* img = db ? _images.image(_image_name) : NULL; // Delay time before clearing TField_window::update(); if (_tree == NULL) return; // Nothing to draw if (img != NULL) { RCT rct; xvt_vobj_get_client_rect(win(), &rct); const int x = (rct.right - img->width())/2; const int y = (rct.bottom - img->height())/2; img->draw(win(), x, y); } for (int i = 0; i < _sorted.items(); i++) draw_item(i); } void TMenulist_window::click_on(int index) { if (index >= 0 && index < _sorted.items()) { const TMenuitem& mi = (const TMenuitem&)_sorted[index]; if (mi.enabled()) { if (mi.is_submenu()) { if (index == 0 && _can_go_back) // Sł di un livello { _tree->goto_node(_curr_node); _tree->goto_father(); set_menu(*_tree); } else // Gił di un livello { if (mi.perform()) // Eventuale richiesta ditta { _tree->goto_node(_curr_node); const TSubmenu& mnu = _tree->curr_submenu(); for (int i = 0; i < mnu.items(); i++) { const TMenuitem& ti = mnu[i]; if (ti.action() == mi.action()) { _tree->goto_firstson(); set_menu(*_tree); synchronize_buddy_tree(); break; } _tree->goto_rbrother(); } } } } else { mi.perform(); } set_focus(); } } } void TMenulist_window::handler(WINDOW win, EVENT* ep) { switch (ep->type) { case E_MOUSE_DOWN: { int index = 0; if (ep->v.mouse.button > 0) // Tasto destro = Torna sł { if (!_can_go_back) return; } else { RCT rct; xvt_vobj_get_client_rect(win, &rct); const int row = ep->v.mouse.where.v * MENU_ROWS / rct.bottom; const int col = ep->v.mouse.where.h * MENU_COLS / rct.right; index = row * MENU_COLS + col; } click_on(index); } break; default: break; } TField_window::handler(win, ep); } void TMenulist_window::select(int s, int direction) { const int old_selection = _selected; if (s < 0) s = 0; if (s >= _sorted.items()) s = _sorted.last(); _selected = s; const TMenuitem& mi = (const TMenuitem&)_sorted[_selected]; if (!mi.enabled()) { for (_selected += direction; ; _selected += direction) { if (_selected < 0) _selected = _sorted.last(); if (_selected >= _sorted.items()) _selected = 0; if (_selected == s) // Ho rifatto l'intero giro! break; const TMenuitem& item = (const TMenuitem&)_sorted[_selected]; if (item.enabled()) break; // Ho trovato un elemento abilitato! } } draw_item(old_selection); draw_item(_selected); } bool TMenulist_window::on_key(KEY k) { switch (k) { case K_ESC: case K_BACKSPACE: if (_tree != NULL && _can_go_back) // Sł di un livello click_on(0); break; case K_ENTER: case K_SPACE: click_on(_selected); break; case K_HOME: select(0, +1); break; case K_UP: case K_PREV: select(_selected - MENU_COLS, -1); break; case K_DOWN: case K_NEXT: select(_selected + MENU_COLS, +1); break; case K_LEFT: case K_BTAB: select(_selected-1, -1); break; case K_RIGHT: case K_TAB: select(_selected+1, +1); break; case K_END: select(_sorted.last(), -1); break; default: break; } return TRUE; } void TMenulist_window::synchronize_buddy_tree() const { TMask& m = owner().mask(); for (int i = 0; i < m.fields(); i++) { TMask_field& mf = m.fld(i); if (mf.is_kind_of(CLASS_TREE_FIELD)) { TTree_field& tf = (TTree_field&)mf; synchronize_tree_field(tf); break; } } } void TMenulist_window::set_menu(TMenu_tree& tree) { _tree = &tree; tree.curr_id(_curr_node); const int dot = _curr_node.rfind('.')+1; int sel = -1; if (dot > 0) { sel = atoi(_curr_node.mid(dot)); _curr_node.overwrite("0", dot); } _sorted.destroy(); _can_go_back = tree.goto_father(); if (_can_go_back) { _sorted.add(tree.curr_item()); tree.goto_node(_curr_node); } int folders = _sorted.items(); // Lista riordinata dei menu items const TSubmenu& mnu = tree.curr_submenu(); for (int i = 0; i < mnu.items(); i++) { const TMenuitem& item = mnu[i]; if (item.is_submenu()) _sorted.insert(item, folders++); else _sorted.add(item); } TString80 sel_act; if (sel >= 0 && sel < mnu.items()) sel_act= mnu[sel].action(); for (_selected = _sorted.last(); _selected > 0; _selected--) { const TMenuitem& sm = (const TMenuitem&)_sorted[_selected]; if (sm.enabled() && sm.action() == sel_act) break; } _image_name = mnu.picture(); force_update(); } TMenulist_window::TMenulist_window(int x, int y, int dx, int dy, WINDOW parent, TMenulist_field* owner) : TField_window(x, y, dx, dy, parent, owner), _tree(NULL) { set_scroll_max(0, 0); // Get rid of that useless scrollbars _images.set_owner(win()); } TMenulist_window::~TMenulist_window() { } TField_window* TMenulist_field::create_window(int x, int y, int dx, int dy, WINDOW parent) { return new TMenulist_window(x, y, dx, dy, parent, this); } void TMenulist_field::create(short dlg, int x, int y, int dx, int dy) { _dlg = dlg; _win = create_window(x, y, dx, dy, mask().win()); } void TMenulist_field::set_menu(TMenu_tree& mt) { TMenulist_window& w = (TMenulist_window&)win(); w.set_menu(mt); }