Files correlati : Ricompilazione Demo : [ ] Commento : Riportata la versione 3.1 patch 766 git-svn-id: svn://10.65.10.50/trunk@14628 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1466 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1466 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| #include <colors.h>
 | |
| #include <tree.h>
 | |
| #include <urldefid.h>
 | |
| #include <image.h>
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // 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::has_rbrother() const
 | |
| {
 | |
|   TString myself; curr_id(myself);
 | |
|   bool ok = ((TTree*)this)->goto_rbrother();
 | |
|   if (ok)
 | |
|     ((TTree*)this)->goto_node(myself);
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| bool TTree::has_son() const
 | |
| {
 | |
|   TString myself; curr_id(myself);
 | |
|   bool ok = ((TTree*)this)->goto_firstson();
 | |
|   if (ok)
 | |
|     ((TTree*)this)->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)
 | |
| {
 | |
|   void* p = NULL;
 | |
|   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
 | |
|     sscanf(node, "%p", &p);
 | |
|   }
 | |
| 
 | |
|   bool ok = true;
 | |
|   if (p == NULL)
 | |
|     ok = goto_root();
 | |
|   else
 | |
|     _current = (TTree_node*)p;
 | |
|   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
 | |
| 
 | |
|     TTree_node* to_be_killed = _current;
 | |
| 
 | |
|     if (_current == _root)
 | |
|        _root = NULL;
 | |
| 
 | |
|     if (_current->_rbrother)
 | |
|       _current = _current->_rbrother;
 | |
|     else
 | |
|     {
 | |
|       if (_current->_lbrother)
 | |
|         _current = _current->_lbrother;
 | |
|       else
 | |
|         _current = _current->_father;
 | |
|     }
 | |
| 
 | |
|     delete to_be_killed;
 | |
| 
 | |
|     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
 | |
| {
 | |
| 	int i;
 | |
|   for (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
 | |
| {
 | |
| 	int i;
 | |
| 	for (i = last(); i >= 0; i = _data.pred(i))
 | |
|   {
 | |
|     if (((TNode_info*)_data.objptr(i))->compare(ni) == 0)
 | |
|       break;
 | |
|   }
 | |
|   return i;
 | |
| }
 | |
| 
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // TTree_window
 | |
| ///////////////////////////////////////////////////////////
 | |
| 
 | |
| #include <maskfld.h>
 | |
| 
 | |
| 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);
 | |
| 
 | |
| 	int row_height() const { return _row_height; }
 | |
|   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();
 | |
|   void draw_text(int x, int y, const TString& str);
 | |
| 
 | |
|   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);
 | |
|   void set_row_height(int rh);
 | |
| 
 | |
|   TTree_window(int x, int y, int dx, int dy,
 | |
|                WINDOW parent, TTree_field* owner);
 | |
|   virtual ~TTree_window() { }
 | |
| };
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| void TTree_window::draw_text(int x, int y, const TString& str)
 | |
| {
 | |
|   int start = 0, pos = 0;
 | |
|   while (pos >= 0)
 | |
|   {
 | |
|     pos = str.find("  ", start);
 | |
|     const TString& mid = str.sub(start, pos);
 | |
|     if (!mid.blank())
 | |
|       stringat(x + start, y, mid);
 | |
|     start = pos+2;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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();
 | |
|       const bool is_marked = node.marked();
 | |
|       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, 
 | |
|                               is_marked ?  REQUIRED_BACK_COLOR : NORMAL_BACK_COLOR);
 | |
|         ui->_win->draw_text(int(ui->_x+2), int(ui->_y), ui->_str);
 | |
|       }
 | |
| 
 | |
|       const int ry = int(ui->_y - ui->_firsty);
 | |
|       int by;
 | |
| 
 | |
|       for (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);
 | |
| 
 | |
| 			const int rh = ui->_win->row_height();
 | |
| 
 | |
|       PNT q;
 | |
|       q.h = p.h; q.v = p.v + rh/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)*rh;
 | |
| 
 | |
|       xvt_dwin_draw_line(win, q);
 | |
| 
 | |
|       TImage* bmp = node.image(is_selected);
 | |
|       if (bmp)
 | |
|       {
 | |
|         const int x = p.h;
 | |
|         const int y = p.v + (rh - 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++)
 | |
|     {
 | |
|       const TFixed_string str(_header.get(i));
 | |
|       draw_text(2, int(origin().y+i-_headlines), str);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| void TTree_window::set_row_height(int rh)
 | |
| {
 | |
| 	if (rh <= 0)
 | |
| 		rh = CHARY+2;
 | |
| 	_row_height = rh;
 | |
| }
 | |
| 
 | |
| 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 && c <= ni._plusx+3))
 | |
|         {
 | |
|           if (_tree->expanded())
 | |
|             ok = _tree->shrink();
 | |
|           else
 | |
|             ok = _tree->expand();
 | |
|           if (ok)
 | |
|           {
 | |
|             owner().on_key(K_SHIFT + K_SPACE);  // Expansion
 | |
|             owner().on_key(K_SPACE);            // Selection
 | |
|           }
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|           KEY key = K_SPACE;
 | |
|           if (ep->type == E_MOUSE_DBL)
 | |
|             key += K_CTRL;                      // Double click selection
 | |
|           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), _headlines(0)
 | |
| {
 | |
|   set_row_height(-1);  // Compute default row height
 | |
| }
 | |
| 
 | |
| ///////////////////////////////////////////////////////////
 | |
| // 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);
 | |
| }
 | |
| 
 | |
| void TTree_field::set_row_height(int rh)
 | |
| {
 | |
|   tree_win().set_row_height(rh);
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 |