#include #include #include #include /////////////////////////////////////////////////////////// // Callbacks /////////////////////////////////////////////////////////// HIDDEN bool callback_compare_node(TTree& node, void* jolly, word when) { const TString& id = *((TString*)jolly); TString cur_id; node.curr_id(cur_id); return id == cur_id; } HIDDEN bool callback_expand_node(TTree& node, void* jolly, word when) { if (when == SCAN_PRE_ORDER) node.expand(); return FALSE; } HIDDEN bool callback_find_father(TTree& node, void* jolly, word when) { TString_array& father_and_son = *(TString_array*)jolly; TString myself; node.curr_id(myself); if (when == SCAN_PRE_ORDER) { if (myself == father_and_son.row(1)) father_and_son.add(myself, 0); } else { if (father_and_son.objptr(0)) { father_and_son.add(myself, 0); return TRUE; } } return FALSE; } HIDDEN bool callback_find_brother(TTree& node, void* jolly, word when) { TString_array& brother_and_sister = *(TString_array*)jolly; TString myself; node.curr_id(myself); if (when == SCAN_PRE_ORDER) { if (myself == brother_and_sister.row(1)) brother_and_sister.add(myself, 0); } else { if (brother_and_sister.objptr(0)) { brother_and_sister.add(myself, 0); return TRUE; } } return FALSE; } /////////////////////////////////////////////////////////// // TTtree /////////////////////////////////////////////////////////// bool TTree::scan_depth_first(NODE_HANDLER nh, void* jolly, word flags) { if ((flags & 0x7) == 0) flags |= SCAN_PRE_ORDER; bool test_myself = TRUE; if ((flags & SCAN_IGNORING_LEAVES) && !has_son()) test_myself = FALSE; TString myself; curr_id(myself); if (test_myself) { if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER)) return TRUE; const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded(); if (!stop && goto_firstson()) { if (scan_depth_first(nh, jolly, flags)) return TRUE; goto_node(myself); } if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER)) return TRUE; } if (goto_rbrother()) { if (scan_depth_first(nh, jolly, flags)) return TRUE; goto_node(myself); } if (test_myself) { if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER)) return TRUE; } return FALSE; } bool TTree::scan_breadth_first(NODE_HANDLER nh, void* jolly, word flags) { if ((flags & 0x7) == 0) flags |= SCAN_PRE_ORDER; bool test_myself = TRUE; if ((flags & SCAN_IGNORING_LEAVES) && !has_son()) test_myself = FALSE; if (test_myself) { if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER)) return TRUE; } TString myself; curr_id(myself); if (goto_rbrother()) { if (scan_breadth_first(nh, jolly, flags)) return TRUE; goto_node(myself); } if (test_myself) { if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER)) return TRUE; const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded(); if (!stop && goto_firstson()) { if (scan_breadth_first(nh, jolly, flags)) return TRUE; goto_node(myself); } if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER)) return TRUE; } return FALSE; } bool TTree::has_root() const { TString myself; curr_id(myself); bool ok = ((TTree*)this)->goto_root(); if (ok) ((TTree*)this)->goto_node(myself); return ok; } bool TTree::has_father() const { TString myself; curr_id(myself); bool ok = ((TTree*)this)->goto_father(); if (ok) ((TTree*)this)->goto_node(myself); return ok; } bool TTree::goto_father() { TString myself; curr_id(myself); bool ok = goto_root(); if (ok) { TString_array father_and_son; father_and_son.add(myself, 1); ok = scan_breadth_first(callback_find_father, &father_and_son, SCAN_PRE_ORDER | SCAN_IN_ORDER); } if (!ok) goto_node(myself); return ok; } bool TTree::has_lbrother() const { TString myself; curr_id(myself); bool ok = ((TTree*)this)->goto_lbrother(); if (ok) ((TTree*)this)->goto_node(myself); return ok; } bool TTree::goto_lbrother() { TString myself; curr_id(myself); bool ok = goto_root(); if (ok) { TString_array brother_and_sister; brother_and_sister.add(myself, 1); ok = scan_depth_first(callback_find_brother, &brother_and_sister, SCAN_PRE_ORDER | SCAN_POST_ORDER); } if (!ok) goto_node(myself); return ok; } bool TTree::expanded() const { TString str; curr_id(str); bool yes = _expanded.is_key(str); return yes; } bool TTree::expand() { bool ok = enabled() && has_son(); if (ok) { TString str; curr_id(str); ok = !_expanded.is_key(str); if (ok) _expanded.add(str); } return ok; } bool TTree::shrink() { TString str; curr_id(str); bool ok = _expanded.is_key(str); if (ok) _expanded.remove(str); return ok; } bool TTree::expand_all() { bool ok = goto_root(); if (ok) scan_breadth_first(callback_expand_node, NULL); return ok; } bool TTree::shrink_all() { _expanded.destroy(); return goto_root(); } TImage* TTree::get_res_image(short bmp_id) const { TImage* bmp = (TImage*)_image.objptr(bmp_id); if (bmp == NULL) { bmp = new TImage(bmp_id); if (bmp->ok()) { bmp->convert_transparent_color(NORMAL_BACK_COLOR); ((TTree*)this)->_image.add(bmp, bmp_id); } else { delete bmp; bmp = NULL; } } return bmp; } TImage* TTree::image(bool selected) const { short bmp_id = BMP_FILE; if (has_son()) bmp_id = selected ? BMP_DIRDN : BMP_DIR; return get_res_image(bmp_id); } /////////////////////////////////////////////////////////// // TBidirectional_tree /////////////////////////////////////////////////////////// bool TBidirectional_tree::scan_depth_first(NODE_HANDLER nh, void* jolly, word flags) { if ((flags & 0x7) == 0) flags |= SCAN_PRE_ORDER; bool test_myself = TRUE; if ((flags & SCAN_IGNORING_LEAVES) && !has_son()) test_myself = FALSE; if (test_myself) { if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER)) return TRUE; const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded(); if (!stop && goto_firstson()) { if (scan_depth_first(nh, jolly, flags)) return TRUE; goto_father(); } if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER)) return TRUE; } if (goto_rbrother()) { if (scan_depth_first(nh, jolly, flags)) return TRUE; goto_lbrother(); } if (test_myself) { if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER)) return TRUE; } return FALSE; } bool TBidirectional_tree::scan_breadth_first(NODE_HANDLER nh, void* jolly, word flags) { if ((flags & 0x7) == 0) flags |= SCAN_PRE_ORDER; bool test_myself = TRUE; if ((flags & SCAN_IGNORING_LEAVES) && !has_son()) test_myself = FALSE; if (test_myself) { if ((flags & SCAN_PRE_ORDER) && nh(*this, jolly, SCAN_PRE_ORDER)) return TRUE; } if (goto_rbrother()) { if (scan_breadth_first(nh, jolly, flags)) return TRUE; goto_lbrother(); } if (test_myself) { if ((flags & SCAN_IN_ORDER) && nh(*this, jolly, SCAN_IN_ORDER)) return TRUE; const bool stop = (flags & SCAN_IGNORING_UNEXPANDED) && !expanded(); if (!stop && goto_firstson()) { if (scan_breadth_first(nh, jolly, flags)) return TRUE; goto_father(); } if ((flags & SCAN_POST_ORDER) && nh(*this, jolly, SCAN_POST_ORDER)) return TRUE; } return FALSE; } bool TBidirectional_tree::goto_node(const TString &id) { bool ok = goto_root(); if (ok && id.not_empty()) ok = scan_breadth_first(callback_compare_node, (void *)&id); return ok; } /////////////////////////////////////////////////////////// // TObject tree /////////////////////////////////////////////////////////// bool TObject_tree::expanded() const { bool ok = _current && _current->_expanded; return ok; } bool TObject_tree::expand() { bool ok = _current && _current->_son && !_current->_expanded && enabled(); if (ok) _current->_expanded = TRUE; return ok; } bool TObject_tree::shrink() { bool ok = _current && _current->_expanded; if (ok) _current->_expanded = FALSE; return ok; } void TObject_tree::node2id(const TObject* node, TString& id) const { id.format("%p", (const void*)node); } bool TObject_tree::goto_node(const TString& node) { bool ok = TRUE; if (node.not_empty()) { #ifdef DBG_TREE if (!_expanded.is_key(node)) // Usa l'assoc array per testare i nodi validi! { NFCHECK("Invalid node: %s", (const char*)node); return goto_root(); } #endif void* p; sscanf(node, "%p", &p); _current = (TTree_node*)p; } else ok = goto_root(); return ok; } bool TObject_tree::goto_root() { _current = _root; return _root != NULL; } bool TObject_tree::goto_firstson() { TTree_node* n = _current ? _current->_son : NULL; if (n) _current = n; return n != NULL; } bool TObject_tree::goto_rbrother() { TTree_node* n = _current ? _current->_rbrother : NULL; if (n) _current = n; return n != NULL; } bool TObject_tree::goto_father() { TTree_node* n = _current ? _current->_father : NULL; if (n) _current = n; return n != NULL; } bool TObject_tree::goto_lbrother() { TTree_node* n = _current ? _current->_lbrother : NULL; if (n) _current = n; return n != NULL; } bool TObject_tree::set_object(TObject* obj) { if (_current) { #ifdef DBG_TREE TString str; curr_id(str); _expanded.add(str); // Usa l'assoc array per memorizzare i nodi validi! #endif if (_current->_obj) delete _current->_obj; _current->_obj = obj; } return _current != NULL; } bool TObject_tree::set_object(const TObject& obj) { bool ok = FALSE; if (_current) { TObject* ptr = obj.dup(); ok = set_object(ptr); if (!ok) delete ptr; } return ok; } bool TObject_tree::create_root() { if (!has_root()) _root = _current = new TTree_node; return goto_root(); } bool TObject_tree::add_son(TObject* obj) { bool ok = FALSE; if (_root) { TTree_node*& curson = _current->_son; TTree_node* newson = new TTree_node; newson->_father = _current; if (curson != NULL) { curson->_lbrother = newson; newson->_rbrother = curson; } curson = newson; ok = goto_firstson(); } else ok = create_root(); if (ok) ok = set_object(obj); return ok; } bool TObject_tree::add_son(const TObject& obj) { TObject* ptr = obj.dup(); bool ok = add_son(ptr); if (!ok) delete ptr; return ok; } bool TObject_tree::add_brother(TObject* obj) { bool ok = FALSE; if (goto_father()) ok = add_son(obj); else ok = create_root() && set_object(obj); return ok; } bool TObject_tree::add_brother(const TObject& obj) { TObject* ptr = obj.dup(); bool ok = add_brother(ptr); if (!ok) delete ptr; return ok; } bool TObject_tree::add_rbrother(TObject* obj) { bool ok = FALSE; if (has_father()) { TTree_node* newbrother = new TTree_node; newbrother->_father = _current->_father; newbrother->_lbrother = _current; newbrother->_rbrother = _current->_rbrother; _current->_rbrother = newbrother; ok = goto_rbrother(); } else ok = create_root(); if (ok) set_object(obj); return ok; } bool TObject_tree::add_rbrother(const TObject& obj) { TObject* ptr = obj.dup(); bool ok = add_rbrother(ptr); if (!ok) delete ptr; return ok; } bool TObject_tree::add_lbrother(TObject* obj) { bool ok = FALSE; if (has_father()) { if (_current->_lbrother == NULL) ok = add_brother(obj); else { TTree_node* newbrother = new TTree_node; newbrother->_father = _current->_father; newbrother->_rbrother = _current; newbrother->_lbrother = _current->_lbrother; _current->_lbrother = newbrother; ok = goto_lbrother(); } } else ok = create_root(); if (ok) set_object(obj); return ok; } bool TObject_tree::add_lbrother(const TObject& obj) { TObject* ptr = obj.dup(); bool ok = add_lbrother(ptr); if (!ok) delete ptr; return ok; } bool TObject_tree::kill_node() { bool ok = FALSE; if (_current) { TTree_node* cur = _current; while (goto_firstson()) { kill_node(); _current = cur; } if (_current->_lbrother) { _current->_lbrother->_rbrother = _current->_rbrother; } else { if (_current->_father) _current->_father->_son = _current->_rbrother; } if (_current->_rbrother) _current->_rbrother->_lbrother = _current->_lbrother; #ifdef DBG_TREE TString id; curr_id(id); _expanded.remove(id); #endif delete _current; if (_current == _root) _root = NULL; if (cur->_rbrother) _current = cur->_rbrother; else { if (cur->_lbrother) _current = cur->_lbrother; else _current = cur->_father; } ok = TRUE; } return ok; } bool TObject_tree::get_description(TString& str) const { TObject* obj = curr_node(); if (obj) str << *obj; return obj != NULL; } TObject_tree::TObject_tree() { _root = _current = NULL; } TObject_tree::~TObject_tree() { if (goto_root()) kill_node(); } /////////////////////////////////////////////////////////// // TString_tree /////////////////////////////////////////////////////////// bool TString_tree::get_description(TString& str) const { const TString* obj = (const TString*)curr_node(); if (obj) str = *obj; else str.cut(0); return obj != NULL; } /////////////////////////////////////////////////////////// // TNode_info /////////////////////////////////////////////////////////// class TNode_info : public TSortable { public: bool _valid : 1; bool _enabled : 1; bool _expandable : 1; bool _expanded : 1; TString _id; long _plusx; long _startx; long _endx; const TNode_info& operator =(const TNode_info& ni) { _valid = ni._valid; _enabled = ni._enabled; _expanded = ni._expanded; _expandable = ni._expandable; _id = ni._id; _plusx = ni._plusx; _startx = ni._startx; _endx = ni._endx; return ni; } virtual int compare(const TSortable& s) const { const TNode_info& ni = (const TNode_info&)s; return _id.compare(ni._id); } virtual bool ok() const { return _valid; } void reset() { _valid = FALSE; } TNode_info() : _valid(FALSE) { } virtual ~TNode_info() { } }; class TNode_info_array : public TObject { TArray _data; public: int last() const; int find(const TNode_info& ni) const; TNode_info& operator[](int index); const TNode_info& operator[](int index) const { return (const TNode_info&)_data[index]; } void reset(); }; TNode_info& TNode_info_array::operator[](int index) { CHECKD(index >= 0, "Bad index ", index); TNode_info* ni = (TNode_info*)_data.objptr(index); if (ni == NULL) { ni = new TNode_info; _data.add(ni, index); } return *ni; } void TNode_info_array::reset() { for (int i = _data.last(); i >= 0; i = _data.pred(i)) ((TNode_info*)_data.objptr(i))->reset(); } int TNode_info_array::last() const { for (int i = _data.last(); i >= 0; i = _data.pred(i)) if (_data[i].ok()) break; return i; } int TNode_info_array::find(const TNode_info& ni) const { for (int i = last(); i >= 0; i = _data.pred(i)) { if (((TNode_info*)_data.objptr(i))->compare(ni) == 0) break; } return i; } /////////////////////////////////////////////////////////// // TTree_window /////////////////////////////////////////////////////////// #include class TTree_window : public TField_window { TTree* _tree; bool _hide_leaves; long _first_line; // Prima riga disegnata TNode_info_array _node_info; TNode_info _curr_info; // Nodo attualmente selezionato TAuto_token_string _header; int _headlines; static int _row_height; static bool _tree_locked; protected: // TWindow virtual void update(); virtual bool on_key(KEY key); virtual void handler(WINDOW win, EVENT* ep); PNT log2dev(long x, long y) const; TPoint dev2log(const PNT& p) const; protected: // Internal use static bool callback_draw_node(TTree& node, void* jolly, word); void update_header(); void draw_plus_minus(); int info2index(const TNode_info& ni) const; bool index2info(long index, TNode_info& ni); public: TTree* tree() const { return _tree; } void set_tree(TTree* tree) { _tree = tree; _first_line = -1; } void hide_leaves(bool yes) { _hide_leaves = yes; } bool select_current(); bool goto_selected(); void set_header(const char* head); TTree_window(int x, int y, int dx, int dy, WINDOW parent, TTree_field* owner); virtual ~TTree_window() { } }; int TTree_window::_row_height = CHARY+2; bool TTree_window::_tree_locked = FALSE; const int TABX = 3; struct TUpdate_info { TTree_window* _win; TToken_string _str; long _x, _y; long _firsty, _lasty; long _jolly; int _headlines; TNode_info_array* _node_info; TNode_info* _curr_info; }; PNT TTree_window::log2dev(long x, long y) const { if (_headlines > 0) y += _headlines; if (autoscrolling()) { if (_pixmap) { x -= origin().x * CHARX; y -= origin().y * _row_height; } else { x -= origin().x; y -= origin().y; } } PNT pnt; pnt.h = (int)x; pnt.v = (int)y; if (!_pixmap) { pnt.h *= CHARX; pnt.v *= _row_height; } return pnt; } TPoint TTree_window::dev2log(const PNT& dp) const { PNT p = dp; if (_headlines > 0) p.v -= _headlines * _row_height; TPoint pnt(_pixmap ? p.h : p.h/CHARX, _pixmap ? p.v : p.v/_row_height); return pnt; } bool TTree_window::callback_draw_node(TTree& node, void* jolly, word when) { TUpdate_info* ui = (TUpdate_info*)jolly; if (when == SCAN_PRE_ORDER) { if (ui->_y >= ui->_firsty && ui->_y <= ui->_lasty) { node.curr_id(ui->_str); const bool is_selected = ui->_str == ui->_curr_info->_id; const bool is_enabled = node.enabled(); int text_len = 0; if (node.get_description(ui->_str)) { text_len = ui->_str.len(); if (is_selected) ui->_win->set_color(FOCUS_COLOR, FOCUS_BACK_COLOR); else ui->_win->set_color(is_enabled ? NORMAL_COLOR : DISABLED_COLOR, NORMAL_BACK_COLOR); ui->_win->stringat(int(ui->_x+2), int(ui->_y), ui->_str); } const int ry = int(ui->_y - ui->_firsty); for (int by = ry-1; by >= 0; by--) { long rx = (*ui->_node_info)[by]._startx; if (rx < ui->_x) break; } const PNT p = ui->_win->log2dev(ui->_x, ui->_y); const WINDOW win = ui->_win->win(); ui->_win->set_pen(DISABLED_COLOR); PNT q; q.h = p.h; q.v = p.v + _row_height/2; xvt_dwin_draw_set_pos(win, q); q.h -= TABX*CHARX - 3*CHARX/2; xvt_dwin_draw_line(win, q); q.v = (by+1+ui->_headlines)*_row_height; xvt_dwin_draw_line(win, q); TImage* bmp = node.image(is_selected); if (bmp) { const int x = p.h; const int y = p.v + (_row_height - bmp->height()) / 2; if (is_enabled) bmp->draw(win, x, y); else { TImage dis(*bmp); for (int j = dis.height()-1; j >= 0; j--) for (int i = dis.width() - (j&1 ? 2 : 1); i >= 0; i-=2) dis.set_pixel(i, j, NORMAL_BACK_COLOR); dis.draw(win, x, y); } } TNode_info& ni = (*ui->_node_info)[ry]; node.curr_id(ni._id); ni._valid = TRUE; ni._startx = ui->_x; ni._endx = ui->_x + text_len + 2; ni._enabled = is_enabled; ni._expandable = is_enabled && node.has_son(); if (ni._expandable) { ni._plusx = ui->_x - TABX + 1; ni._expanded = node.expanded(); } else { ni._plusx = 0; ni._expanded = FALSE; } } ui->_y++; ui->_x += TABX; if (ui->_x > ui->_jolly) ui->_jolly = ui->_x; } else if (when == SCAN_IN_ORDER) { ui->_x -= TABX; if (ui->_y > ui->_lasty) return TRUE; } return FALSE; } void TTree_window::draw_plus_minus() { const long firsty = origin().y; const int last_drawn = _node_info.last(); for (int i = last_drawn; i >= 0; i--) { TNode_info& ni = _node_info[i]; if (ni._plusx > 0) { if (ni._expanded) { bool spudorato = i == last_drawn; // Falso espandibile if (!spudorato) { const TNode_info& nni = _node_info[i+1]; spudorato = nni._startx <= ni._startx; } if (spudorato) { ni._expandable = FALSE; ni._expanded = FALSE; ni._plusx = 0; continue; } } WINDOW w = win(); PNT r = log2dev(ni._plusx, firsty + i); r.v += _row_height/2; r.h += CHARX/2; RCT rct; rct.left = r.h - 4; rct.top = r.v - 4; rct.right = r.h + 5; rct.bottom = r.v + 5; set_pen(NORMAL_COLOR); set_brush(NORMAL_BACK_COLOR); xvt_dwin_draw_rect(w, &rct); PNT f, t; f.h = rct.left+2; f.v = r.v; t.h = rct.right+-2; t.v = r.v; xvt_dwin_draw_set_pos(w, f); xvt_dwin_draw_line(w, t); if (!ni._expanded) { f.h = r.h; f.v = rct.top+2; t.h = r.h; t.v = rct.bottom-2; xvt_dwin_draw_set_pos(w, f); xvt_dwin_draw_line(w, t); } } } } void TTree_window::set_header(const char* head) { _header = head; _headlines = _header.items(); } void TTree_window::update_header() { if (_headlines > 0) { autoscroll(FALSE); set_brush(MASK_BACK_COLOR); bar(0,-_headlines,columns(),0); autoscroll(TRUE); set_opaque_text(FALSE); for (int i = 0; i < _headlines; i++) stringat(2, int(origin().y+i-_headlines), _header.get(i)); } } void TTree_window::update() { TField_window::update(); if (_tree == NULL) return; update_header(); TUpdate_info ui; ui._win = this; ui._x = 0; ui._y = 0; ui._firsty = origin().y; ui._lasty = ui._firsty + rows(); ui._jolly = 0; ui._headlines = _headlines; ui._node_info = &_node_info; ui._curr_info = &_curr_info; bool ok = FALSE; if (_first_line > 0) { const long index = origin().y - _first_line; TNode_info ni; if (index2info(index, ni)) { ok = _tree->goto_node(ni._id); ui._x = ni._startx; ui._y = ui._firsty; } } if (!ok) { ok = _tree->goto_root(); if (!ok) return; } _node_info.reset(); set_opaque_text(TRUE); word flags = SCAN_PRE_ORDER | SCAN_IN_ORDER | SCAN_IGNORING_UNEXPANDED; if (_hide_leaves) flags |= SCAN_IGNORING_LEAVES; _tree->scan_depth_first(callback_draw_node, &ui, flags); while (ui._y < ui._lasty) { if (_tree->goto_father()) { ui._x -= TABX; if (_tree->goto_rbrother()) _tree->scan_depth_first(callback_draw_node, &ui, flags); } else break; } draw_plus_minus(); set_scroll_max(ui._jolly+columns(), ui._y); _first_line = ui._firsty; } // Fa diventare il nodo corrente selezionato bool TTree_window::select_current() { if (_tree) _tree->curr_id(_curr_info._id); return _tree != NULL; } // Salta al nodo correntemente selezionato bool TTree_window::goto_selected() { bool ok = FALSE; if (_tree) ok = _tree->goto_node(_curr_info._id); return ok; } int TTree_window::info2index(const TNode_info& info) const { return _node_info.find(info); } bool TTree_window::index2info(long index, TNode_info& ni) { bool ok = FALSE; const int last = _node_info.last(); if (index == -1) { if (index2info(0, ni) && _tree->goto_node(ni._id)) { ok = _tree->goto_lbrother(); if (!ok) { ok = _tree->goto_father(); ni._startx -= TABX; } if (ok) _tree->curr_id(ni._id); } } else if (index == last+1 && last >= 0) { if (index2info(last, ni) && _tree->goto_node(ni._id)) { ok = _tree->expanded() && _tree->goto_firstson(); if (ok && _hide_leaves && !_tree->has_son()) { _tree->goto_node(ni._id); // Ritorno al padre perche' ignoro le foglie ok = FALSE; } if (!ok) { ok = _tree->goto_rbrother(); ni._startx += TABX; } if (ok) { _tree->curr_id(ni._id); } } } else if (index >= 0 && index <= last) { TNode_info& info = _node_info[int(index)]; ok = info.ok(); if (ok) ni = info; } return ok; } bool TTree_window::on_key(KEY key) { if (_tree == NULL || _tree_locked) return FALSE; if (key == K_RIGHT) { if (_tree->goto_node(_curr_info._id) && !_tree->expanded() && _tree->has_son()) key = K_ENTER; else key = K_DOWN; } if (key == K_LEFT) { if (_tree->goto_node(_curr_info._id) && _tree->expanded()) key = K_ENTER; else key = K_UP; } if (key == K_UP || key == K_DOWN) { _tree_locked = TRUE; int index = info2index(_curr_info); if (key == K_UP) index--; else index++; bool ok = FALSE; bool scroll = FALSE; TNode_info ni; if (index2info(index, ni)) { ok = _tree->goto_node(ni._id); scroll = ok && (index < 0 || index > _node_info.last()); } else { const int index = key == K_UP ? 0 : _node_info.last(); ok = index2info(index, ni) && _tree->goto_node(ni._id); } if (ok && _curr_info != ni) { _tree->curr_id(_curr_info._id); owner().on_key(K_SPACE); if (!scroll) { force_update(); do_events(); } } _tree_locked = FALSE; if (!scroll) return TRUE; } if (key == K_ENTER) { _tree_locked = TRUE; if (_tree->goto_node(_curr_info._id)) { if (_tree->has_son()) { bool ok; if (_tree->expanded()) ok = _tree->shrink(); else ok = _tree->expand(); if (ok) { force_update(); do_events(); // Make sure of being well positioned after the redraw!!! _tree->goto_node(_curr_info._id); } } owner().on_key(K_CTRL + K_SPACE); } _tree_locked = FALSE; } return TField_window::on_key(key); } void TTree_window::handler(WINDOW win, EVENT* ep) { switch(ep->type) { case E_MOUSE_DBL: case E_MOUSE_DOWN: if (_tree && !_tree_locked) { _tree_locked = TRUE; const TPoint lp = dev2log(ep->v.mouse.where); const int c = (int)lp.x; const int r = (int)lp.y; TNode_info ni; bool ok = index2info(r, ni); if (ok && (c == ni._plusx || (c >= ni._startx && c < ni._endx)) && _tree->goto_node(ni._id)) { if (c == ni._plusx || (ni._expandable && ep->type == E_MOUSE_DBL)) { if (_tree->expanded()) ok = _tree->shrink(); else ok = _tree->expand(); if (ok) owner().on_key(K_CTRL + K_SPACE); } else { KEY key = K_SPACE; if (ep->type == E_MOUSE_DBL) key += K_CTRL; owner().on_key(key); } if (ok) { _curr_info = ni; force_update(); } } _tree_locked = FALSE; } break; default: TField_window::handler(win, ep); break; } } TTree_window::TTree_window(int x, int y, int dx, int dy, WINDOW parent, TTree_field* owner) : TField_window(x, y, dx, dy, parent, owner), _tree(NULL), _hide_leaves(FALSE) { _row_height = CHARY+2; } /////////////////////////////////////////////////////////// // TTree_field /////////////////////////////////////////////////////////// word TTree_field::class_id() const { return CLASS_TREE_FIELD; } bool TTree_field::is_kind_of(word cid) const { return cid == CLASS_TREE_FIELD || TWindowed_field::is_kind_of(cid); } #define tree_win() ((TTree_window&)win()) TTree* TTree_field::tree() const { return tree_win().tree(); } void TTree_field::set_tree(TTree* tree) { tree_win().set_tree(tree); tree_win().select_current(); } void TTree_field::hide_leaves(bool yes) { tree_win().hide_leaves(yes); } bool TTree_field::select_current() { return tree_win().select_current(); } bool TTree_field::goto_selected() { return tree_win().goto_selected(); } void TTree_field::set_header(const char* head) { tree_win().set_header(head); } TField_window* TTree_field::create_window(int x, int y, int dx, int dy, WINDOW parent) { return new TTree_window(x, y, dx, dy, parent, this); }