c504b36ab3
Files correlati : ba1.exe Ricompilazione Demo : [ ] Commento : AO20115 Se con la visualizzazione del menù normale dalla videata degli ordini si cambia studio, viene restituito un errore che non riesce a trovare lo sfondo BMP, seguito da un errore su BA0. Ho notato inoltre che la videata degli ordini acquisisce come sfondo quello utilizzato da un'altra videata dalla quale in precedenza si ha effettuato un cambio di studio. git-svn-id: svn://10.65.10.50/trunk@11540 c028cbd2-c16b-5b4b-a496-9718f37d4682
1427 lines
30 KiB
C++
Executable File
1427 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::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
|
|
|
|
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
|
|
{
|
|
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 <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();
|
|
|
|
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() { }
|
|
};
|
|
|
|
/* 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);
|
|
|
|
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++)
|
|
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;
|
|
}
|
|
|
|
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))
|
|
{
|
|
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), _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);
|
|
}
|