1 /********************************************************************************
2 * *
3 * T r e e L i s t O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2005 by Jeroen van der Zijp. All Rights Reserved. *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation; either *
11 * version 2.1 of the License, or (at your option) any later version. *
12 * *
13 * This library is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this library; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
21 *********************************************************************************
22 * $Id: FXTreeList.cpp,v 1.155 2005/02/06 17:20:00 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "FXHash.h"
29 #include "FXThread.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXRegistry.h"
36 #include "FXApp.h"
37 #include "FXDCWindow.h"
38 #include "FXFont.h"
39 #include "FXIcon.h"
40 #include "FXScrollBar.h"
41 #include "FXTreeList.h"
42
43
44 /*
45 Notes:
46 - Tooltip should pop up exactly on top of current item.
47 - Clicking on + does not make it current.
48 - Need translate right-clicks into message with item figured out...
49 - In autoselect mode, all items are expanded.
50 - Sortfunc's will be hard to serialize.
51 - As with FXIconList, it probably shouldn't autoscroll when draggin icons.
52 - Maybe moving (dragging) items around in the treelist is something that should
53 be supported?
54 */
55
56
57 #define ICON_SPACING 4 // Spacing between parent and child in x direction
58 #define TEXT_SPACING 4 // Spacing between icon and text
59 #define SIDE_SPACING 4 // Spacing between side and item
60 #define DEFAULT_INDENT 8 // Indent between parent and child
61 #define HALFBOX_SIZE 4 // Half box size
62 #define BOX_FUDGE 3 // Fudge border around box
63
64
65 #define SELECT_MASK (TREELIST_SINGLESELECT|TREELIST_BROWSESELECT)
66 #define TREELIST_MASK (SELECT_MASK|TREELIST_AUTOSELECT|TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES|TREELIST_ROOT_BOXES)
67
68
69 using namespace FX;
70
71 /*******************************************************************************/
72
73 namespace FX {
74
75
76 // Object implementation
77 FXIMPLEMENT(FXTreeItem,FXObject,NULL,0)
78
79
80
81 // Draw item
draw(const FXTreeList * list,FXDC & dc,FXint xx,FXint yy,FXint,FXint hh) const82 void FXTreeItem::draw(const FXTreeList* list,FXDC& dc,FXint xx,FXint yy,FXint,FXint hh) const {
83 register FXIcon *icon=(state&OPENED)?openIcon:closedIcon;
84 register FXFont *font=list->getFont();
85 register FXint th=0,tw=0,ih=0,iw=0;
86 xx+=SIDE_SPACING/2;
87 if(icon){
88 iw=icon->getWidth();
89 ih=icon->getHeight();
90 dc.drawIcon(icon,xx,yy+(hh-ih)/2);
91 xx+=ICON_SPACING+iw;
92 }
93 if(!label.empty()){
94 tw=4+font->getTextWidth(label.text(),label.length());
95 th=4+font->getFontHeight();
96 yy+=(hh-th)/2;
97 if(isSelected()){
98 dc.setForeground(list->getSelBackColor());
99 dc.fillRectangle(xx,yy,tw,th);
100 }
101 if(hasFocus()){
102 dc.drawFocusRectangle(xx+1,yy+1,tw-2,th-2);
103 }
104 if(!isEnabled())
105 dc.setForeground(makeShadowColor(list->getBackColor()));
106 else if(isSelected())
107 dc.setForeground(list->getSelTextColor());
108 else
109 dc.setForeground(list->getTextColor());
110 dc.drawText(xx+2,yy+font->getFontAscent()+2,label.text(),label.length());
111 }
112 }
113
114
115 // See if item got hit, and where:- 1 is icon, 2 is text
hitItem(const FXTreeList * list,FXint xx,FXint yy) const116 FXint FXTreeItem::hitItem(const FXTreeList* list,FXint xx,FXint yy) const {
117 register FXint oiw=0,ciw=0,oih=0,cih=0,tw=0,th=0,iw,ih,ix,iy,tx,ty,h;
118 register FXFont *font=list->getFont();
119 if(openIcon){
120 oiw=openIcon->getWidth();
121 oih=openIcon->getHeight();
122 }
123 if(closedIcon){
124 ciw=closedIcon->getWidth();
125 cih=closedIcon->getHeight();
126 }
127 if(!label.empty()){
128 tw=4+font->getTextWidth(label.text(),label.length());
129 th=4+font->getFontHeight();
130 }
131 iw=FXMAX(oiw,ciw);
132 ih=FXMAX(oih,cih);
133 h=FXMAX(th,ih);
134 ix=SIDE_SPACING/2;
135 tx=SIDE_SPACING/2;
136 if(iw) tx+=iw+ICON_SPACING;
137 iy=(h-ih)/2;
138 ty=(h-th)/2;
139
140 // In icon?
141 if(ix<=xx && iy<=yy && xx<ix+iw && yy<iy+ih) return 1;
142
143 // In text?
144 if(tx<=xx && ty<=yy && xx<tx+tw && yy<ty+th) return 2;
145
146 // Outside
147 return 0;
148 }
149
150
151 // Set or kill focus
setFocus(FXbool focus)152 void FXTreeItem::setFocus(FXbool focus){
153 if(focus) state|=FOCUS; else state&=~FOCUS;
154 }
155
156 // Select or deselect item
setSelected(FXbool selected)157 void FXTreeItem::setSelected(FXbool selected){
158 if(selected) state|=SELECTED; else state&=~SELECTED;
159 }
160
161 // Set item opened
setOpened(FXbool opened)162 void FXTreeItem::setOpened(FXbool opened){
163 if(opened) state|=OPENED; else state&=~OPENED;
164 }
165
166 // Set item expanded
setExpanded(FXbool expanded)167 void FXTreeItem::setExpanded(FXbool expanded){
168 if(expanded) state|=EXPANDED; else state&=~EXPANDED;
169 }
170
171 // Enable or disable the item
setEnabled(FXbool enabled)172 void FXTreeItem::setEnabled(FXbool enabled){
173 if(enabled) state&=~DISABLED; else state|=DISABLED;
174 }
175
176 // Icon is draggable
setDraggable(FXbool draggable)177 void FXTreeItem::setDraggable(FXbool draggable){
178 if(draggable) state|=DRAGGABLE; else state&=~DRAGGABLE;
179 }
180
181
182 // Change item's text label
setText(const FXString & txt)183 void FXTreeItem::setText(const FXString& txt){
184 label=txt;
185 }
186
187
188 // Change open icon
setOpenIcon(FXIcon * icon,FXbool owned)189 void FXTreeItem::setOpenIcon(FXIcon* icon,FXbool owned){
190 if(openIcon && (state&OPENICONOWNED)){
191 if(openIcon!=icon) delete openIcon;
192 state&=~OPENICONOWNED;
193 }
194 openIcon=icon;
195 if(openIcon && owned){
196 state|=OPENICONOWNED;
197 }
198 }
199
200
201 // Change closed icon
setClosedIcon(FXIcon * icon,FXbool owned)202 void FXTreeItem::setClosedIcon(FXIcon* icon,FXbool owned){
203 if(closedIcon && (state&CLOSEDICONOWNED)){
204 if(closedIcon!=icon) delete closedIcon;
205 state&=~CLOSEDICONOWNED;
206 }
207 closedIcon=icon;
208 if(closedIcon && owned){
209 state|=CLOSEDICONOWNED;
210 }
211 }
212
213
214 // Change has items flag
setHasItems(FXbool flag)215 void FXTreeItem::setHasItems(FXbool flag){
216 if(flag) state|=HASITEMS; else state&=~HASITEMS;
217 }
218
219
220 // Create icon
create()221 void FXTreeItem::create(){
222 if(openIcon) openIcon->create();
223 if(closedIcon) closedIcon->create();
224 }
225
226
227 // Destroy icon
destroy()228 void FXTreeItem::destroy(){
229 if((state&OPENICONOWNED) && openIcon) openIcon->destroy();
230 if((state&CLOSEDICONOWNED) && closedIcon) closedIcon->destroy();
231 }
232
233
234 // Detach from icon resource
detach()235 void FXTreeItem::detach(){
236 if(openIcon) openIcon->detach();
237 if(closedIcon) closedIcon->detach();
238 }
239
240
241 // Get number of child items
getNumChildren() const242 FXint FXTreeItem::getNumChildren() const {
243 register FXTreeItem *item=first;
244 register FXint n=0;
245 while(item){item=item->next;n++;}
246 return n;
247 }
248
249
250
251 // Get item (logically) below this one
getBelow() const252 FXTreeItem* FXTreeItem::getBelow() const {
253 register FXTreeItem* item=(FXTreeItem*)this;
254 if(first) return first;
255 while(!item->next && item->parent) item=item->parent;
256 return item->next;
257 }
258
259
260 // Get item (logically) above this one
getAbove() const261 FXTreeItem* FXTreeItem::getAbove() const {
262 register FXTreeItem* item=prev;
263 if(!item) return parent;
264 while(item->last) item=item->last;
265 return item;
266 }
267
268
269 // Return true if child of parent item
isChildOf(const FXTreeItem * item) const270 FXbool FXTreeItem::isChildOf(const FXTreeItem* item) const {
271 register const FXTreeItem* child=this;
272 while(child){ child=child->parent; if(child==item) return TRUE; }
273 return FALSE;
274 }
275
276
277 // Return true if parent of child item
isParentOf(const FXTreeItem * item) const278 FXbool FXTreeItem::isParentOf(const FXTreeItem* item) const {
279 register const FXTreeItem* child=item;
280 while(child){ child=child->parent; if(child==this) return TRUE; }
281 return FALSE;
282 }
283
284
285 // Get item width
getWidth(const FXTreeList * list) const286 FXint FXTreeItem::getWidth(const FXTreeList* list) const {
287 register FXint w=0,oiw=0,ciw=0;
288 if(openIcon) oiw=openIcon->getWidth();
289 if(closedIcon) ciw=closedIcon->getWidth();
290 w=FXMAX(oiw,ciw);
291 if(!label.empty()){
292 if(w) w+=ICON_SPACING;
293 w+=4+list->getFont()->getTextWidth(label.text(),label.length());
294 }
295 return SIDE_SPACING+w;
296 }
297
298
299 // Get item height
getHeight(const FXTreeList * list) const300 FXint FXTreeItem::getHeight(const FXTreeList* list) const {
301 register FXint th=0,oih=0,cih=0;
302 if(openIcon) oih=openIcon->getHeight();
303 if(closedIcon) cih=closedIcon->getHeight();
304 if(!label.empty()) th=4+list->getFont()->getFontHeight();
305 return FXMAX3(th,oih,cih);
306 }
307
308
309 // Save data
save(FXStream & store) const310 void FXTreeItem::save(FXStream& store) const {
311 FXObject::save(store);
312 store << prev;
313 store << next;
314 store << parent;
315 store << first;
316 store << last;
317 store << label;
318 store << openIcon;
319 store << closedIcon;
320 store << state;
321 }
322
323
324 // Load data
load(FXStream & store)325 void FXTreeItem::load(FXStream& store){
326 FXObject::load(store);
327 store >> prev;
328 store >> next;
329 store >> parent;
330 store >> first;
331 store >> last;
332 store >> label;
333 store >> openIcon;
334 store >> closedIcon;
335 store >> state;
336 }
337
338
339 // Delete icons if owned
~FXTreeItem()340 FXTreeItem::~FXTreeItem(){
341 if(state&OPENICONOWNED) delete openIcon;
342 if(state&CLOSEDICONOWNED) delete closedIcon;
343 parent=(FXTreeItem*)-1L;
344 prev=(FXTreeItem*)-1L;
345 next=(FXTreeItem*)-1L;
346 first=(FXTreeItem*)-1L;
347 last=(FXTreeItem*)-1L;
348 openIcon=(FXIcon*)-1L;
349 closedIcon=(FXIcon*)-1L;
350 }
351
352
353 /*******************************************************************************/
354
355 // Map
356 FXDEFMAP(FXTreeList) FXTreeListMap[]={
357 FXMAPFUNC(SEL_PAINT,0,FXTreeList::onPaint),
358 FXMAPFUNC(SEL_MOTION,0,FXTreeList::onMotion),
359 FXMAPFUNC(SEL_TIMEOUT,FXWindow::ID_AUTOSCROLL,FXTreeList::onAutoScroll),
360 FXMAPFUNC(SEL_TIMEOUT,FXTreeList::ID_TIPTIMER,FXTreeList::onTipTimer),
361 FXMAPFUNC(SEL_TIMEOUT,FXTreeList::ID_LOOKUPTIMER,FXTreeList::onLookupTimer),
362 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXTreeList::onLeftBtnPress),
363 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXTreeList::onLeftBtnRelease),
364 FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXTreeList::onRightBtnPress),
365 FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXTreeList::onRightBtnRelease),
366 FXMAPFUNC(SEL_UNGRABBED,0,FXTreeList::onUngrabbed),
367 FXMAPFUNC(SEL_KEYPRESS,0,FXTreeList::onKeyPress),
368 FXMAPFUNC(SEL_KEYRELEASE,0,FXTreeList::onKeyRelease),
369 FXMAPFUNC(SEL_ENTER,0,FXTreeList::onEnter),
370 FXMAPFUNC(SEL_LEAVE,0,FXTreeList::onLeave),
371 FXMAPFUNC(SEL_FOCUSIN,0,FXTreeList::onFocusIn),
372 FXMAPFUNC(SEL_FOCUSOUT,0,FXTreeList::onFocusOut),
373 FXMAPFUNC(SEL_CLICKED,0,FXTreeList::onClicked),
374 FXMAPFUNC(SEL_DOUBLECLICKED,0,FXTreeList::onDoubleClicked),
375 FXMAPFUNC(SEL_TRIPLECLICKED,0,FXTreeList::onTripleClicked),
376 FXMAPFUNC(SEL_COMMAND,0,FXTreeList::onCommand),
377 FXMAPFUNC(SEL_QUERY_TIP,0,FXTreeList::onQueryTip),
378 FXMAPFUNC(SEL_QUERY_HELP,0,FXTreeList::onQueryHelp),
379 };
380
381
382 // Object implementation
FXIMPLEMENT(FXTreeList,FXScrollArea,FXTreeListMap,ARRAYNUMBER (FXTreeListMap))383 FXIMPLEMENT(FXTreeList,FXScrollArea,FXTreeListMap,ARRAYNUMBER(FXTreeListMap))
384
385
386 /*******************************************************************************/
387
388
389 // Tree List
390 FXTreeList::FXTreeList(){
391 flags|=FLAG_ENABLED;
392 firstitem=NULL;
393 lastitem=NULL;
394 anchoritem=NULL;
395 currentitem=NULL;
396 extentitem=NULL;
397 cursoritem=NULL;
398 font=(FXFont*)-1L;
399 sortfunc=NULL;
400 textColor=0;
401 selbackColor=0;
402 seltextColor=0;
403 lineColor=0;
404 treeWidth=0;
405 treeHeight=0;
406 visible=0;
407 indent=DEFAULT_INDENT;
408 grabx=0;
409 graby=0;
410 state=FALSE;
411 }
412
413
414 // Tree List
FXTreeList(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h)415 FXTreeList::FXTreeList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):
416 FXScrollArea(p,opts,x,y,w,h){
417 flags|=FLAG_ENABLED;
418 target=tgt;
419 message=sel;
420 firstitem=NULL;
421 lastitem=NULL;
422 anchoritem=NULL;
423 currentitem=NULL;
424 extentitem=NULL;
425 cursoritem=NULL;
426 font=getApp()->getNormalFont();
427 sortfunc=NULL;
428 textColor=getApp()->getForeColor();
429 selbackColor=getApp()->getSelbackColor();
430 seltextColor=getApp()->getSelforeColor();
431 lineColor=getApp()->getShadowColor();
432 treeWidth=0;
433 treeHeight=0;
434 visible=0;
435 indent=DEFAULT_INDENT;
436 grabx=0;
437 graby=0;
438 state=FALSE;
439 }
440
441
442 // Create window
create()443 void FXTreeList::create(){
444 register FXTreeItem *item=firstitem;
445 FXScrollArea::create();
446 while(item){
447 item->create();
448 if(item->first){item=item->first;continue;}
449 while(!item->next && item->parent){item=item->parent;}
450 item=item->next;
451 }
452 font->create();
453 }
454
455
456 // Detach window
detach()457 void FXTreeList::detach(){
458 register FXTreeItem *item=firstitem;
459 FXScrollArea::detach();
460 while(item){
461 item->detach();
462 if(item->first){item=item->first;continue;}
463 while(!item->next && item->parent){item=item->parent;}
464 item=item->next;
465 }
466 font->detach();
467 }
468
469
470 // Can have focus
canFocus() const471 FXbool FXTreeList::canFocus() const { return TRUE; }
472
473
474 // Into focus chain
setFocus()475 void FXTreeList::setFocus(){
476 FXScrollArea::setFocus();
477 setDefault(TRUE);
478 }
479
480
481 // Out of focus chain
killFocus()482 void FXTreeList::killFocus(){
483 FXScrollArea::killFocus();
484 setDefault(MAYBE);
485 }
486
487
488 // Get default width
getDefaultWidth()489 FXint FXTreeList::getDefaultWidth(){
490 return FXScrollArea::getDefaultWidth();
491 }
492
493
494 // Get default height
getDefaultHeight()495 FXint FXTreeList::getDefaultHeight(){
496 if(visible) return visible*(4+font->getFontHeight());
497 return FXScrollArea::getDefaultHeight();
498 }
499
500
501 // Propagate size change
recalc()502 void FXTreeList::recalc(){
503 FXScrollArea::recalc();
504 flags|=FLAG_RECALC;
505 cursoritem=NULL;
506 }
507
508
509 // List is multiple of nitems
setNumVisible(FXint nvis)510 void FXTreeList::setNumVisible(FXint nvis){
511 if(nvis<0) nvis=0;
512 if(visible!=nvis){
513 visible=nvis;
514 recalc();
515 }
516 }
517
518
519 // Get number of toplevel items
getNumItems() const520 FXint FXTreeList::getNumItems() const {
521 register FXTreeItem *item=firstitem;
522 register FXint n=0;
523 while(item){
524 item=item->next;
525 n++;
526 }
527 return n;
528 }
529
530
531 // Recompute interior
recompute()532 void FXTreeList::recompute(){
533 register FXTreeItem* item;
534 register FXint x,y,w,h;
535 x=y=0;
536 treeWidth=0;
537 treeHeight=0;
538 item=firstitem;
539 if(options&TREELIST_ROOT_BOXES) x+=(4+indent);
540 while(item){
541 item->x=x;
542 item->y=y;
543 w=item->getWidth(this);
544 h=item->getHeight(this);
545 if(x+w>treeWidth) treeWidth=x+w;
546 y+=h;
547 if(item->first && ((options&TREELIST_AUTOSELECT) || item->isExpanded())){
548 x+=(indent+h/2);
549 item=item->first;
550 continue;
551 }
552 while(!item->next && item->parent){
553 item=item->parent;
554 x-=(indent+item->getHeight(this)/2);
555 }
556 item=item->next;
557 }
558 treeHeight=y;
559 flags&=~FLAG_RECALC;
560 }
561
562
563 // Determine content width of tree list
getContentWidth()564 FXint FXTreeList::getContentWidth(){
565 if(flags&FLAG_RECALC) recompute();
566 return treeWidth;
567 }
568
569
570 // Determine content height of tree list
getContentHeight()571 FXint FXTreeList::getContentHeight(){
572 if(flags&FLAG_RECALC) recompute();
573 return treeHeight;
574 }
575
576
577 // Recalculate layout
layout()578 void FXTreeList::layout(){
579
580 // Calculate contents
581 FXScrollArea::layout();
582
583 // Set line size based on item size
584 if(firstitem){
585 vertical->setLine(firstitem->getHeight(this));
586 horizontal->setLine(firstitem->getWidth(this)/10);
587 }
588
589 // Force repaint
590 update();
591
592 // No more dirty
593 flags&=~FLAG_DIRTY;
594 }
595
596
597 // Set item text
setItemText(FXTreeItem * item,const FXString & text)598 void FXTreeList::setItemText(FXTreeItem* item,const FXString& text){
599 if(item==NULL){ fxerror("%s::setItemText: NULL argument.\n",getClassName()); }
600 if(item->getText()!=text){
601 item->setText(text);
602 recalc();
603 }
604 }
605
606
607 // Get item text
getItemText(const FXTreeItem * item) const608 FXString FXTreeList::getItemText(const FXTreeItem* item) const {
609 if(item==NULL){ fxerror("%s::getItemText: NULL argument.\n",getClassName()); }
610 return item->getText();
611 }
612
613
614 // Set item open icon
setItemOpenIcon(FXTreeItem * item,FXIcon * icon,FXbool owned)615 void FXTreeList::setItemOpenIcon(FXTreeItem* item,FXIcon* icon,FXbool owned){
616 if(item==NULL){ fxerror("%s::setItemOpenIcon: NULL argument.\n",getClassName()); }
617 if(item->getOpenIcon()!=icon) recalc();
618 item->setOpenIcon(icon,owned);
619 }
620
621
622 // Get item open icon
getItemOpenIcon(const FXTreeItem * item) const623 FXIcon* FXTreeList::getItemOpenIcon(const FXTreeItem* item) const {
624 if(item==NULL){ fxerror("%s::getItemOpenIcon: NULL argument.\n",getClassName()); }
625 return item->getOpenIcon();
626 }
627
628
629 // Set item closed icon
setItemClosedIcon(FXTreeItem * item,FXIcon * icon,FXbool owned)630 void FXTreeList::setItemClosedIcon(FXTreeItem* item,FXIcon* icon,FXbool owned){
631 if(item==NULL){ fxerror("%s::setItemClosedIcon: NULL argument.\n",getClassName()); }
632 if(item->getClosedIcon()!=icon) recalc();
633 item->setClosedIcon(icon,owned);
634 }
635
636
637 // Get item closed icon
getItemClosedIcon(const FXTreeItem * item) const638 FXIcon* FXTreeList::getItemClosedIcon(const FXTreeItem* item) const {
639 if(item==NULL){ fxerror("%s::getItemClosedIcon: NULL argument.\n",getClassName()); }
640 return item->getClosedIcon();
641 }
642
643
644 // Set item data
setItemData(FXTreeItem * item,void * ptr) const645 void FXTreeList::setItemData(FXTreeItem* item,void* ptr) const {
646 if(item==NULL){ fxerror("%s::setItemData: NULL argument.\n",getClassName()); }
647 item->setData(ptr);
648 }
649
650
651 // Get item data
getItemData(const FXTreeItem * item) const652 void* FXTreeList::getItemData(const FXTreeItem* item) const {
653 if(item==NULL){ fxerror("%s::getItemData: NULL argument.\n",getClassName()); }
654 return item->getData();
655 }
656
657
658 // True if item is selected
isItemSelected(const FXTreeItem * item) const659 FXbool FXTreeList::isItemSelected(const FXTreeItem* item) const {
660 if(!item){ fxerror("%s::isItemSelected: NULL argument.\n",getClassName()); }
661 return item->isSelected();
662 }
663
664
665 // True if item is current
isItemCurrent(const FXTreeItem * item) const666 FXbool FXTreeList::isItemCurrent(const FXTreeItem* item) const {
667 if(!item){ fxerror("%s::isItemCurrent: NULL argument.\n",getClassName()); }
668 return currentitem==item;
669 }
670
671
672 // Check if item is expanded
isItemExpanded(const FXTreeItem * item) const673 FXbool FXTreeList::isItemExpanded(const FXTreeItem* item) const {
674 if(!item){ fxerror("%s::isItemExpanded: NULL argument.\n",getClassName()); }
675 return (options&TREELIST_AUTOSELECT) || item->isExpanded();
676 }
677
678
679 // Is item a leaf item
isItemLeaf(const FXTreeItem * item) const680 FXbool FXTreeList::isItemLeaf(const FXTreeItem* item) const {
681 if(!item){ fxerror("%s::isItemLeaf: NULL argument.\n",getClassName()); }
682 return item->first==NULL;
683 }
684
685
686 // Check if item is enabled
isItemEnabled(const FXTreeItem * item) const687 FXbool FXTreeList::isItemEnabled(const FXTreeItem* item) const {
688 if(!item){ fxerror("%s::isItemEnabled: NULL argument.\n",getClassName()); }
689 return item->isEnabled();
690 }
691
692
693 // Check item is open
isItemOpened(const FXTreeItem * item) const694 FXbool FXTreeList::isItemOpened(const FXTreeItem* item) const {
695 if(!item){ fxerror("%s::isItemOpen: NULL argument.\n",getClassName()); }
696 return item->isOpened();
697 }
698
699
700 // True if item (partially) visible
isItemVisible(const FXTreeItem * item) const701 FXbool FXTreeList::isItemVisible(const FXTreeItem* item) const {
702 if(!item){ fxerror("%s::isItemVisible: NULL argument.\n",getClassName()); }
703 return 0<pos_y+item->y+item->getHeight(this) && pos_y+item->y<viewport_h;
704 }
705
706
707 // Make item fully visible
makeItemVisible(FXTreeItem * item)708 void FXTreeList::makeItemVisible(FXTreeItem* item){
709 register FXTreeItem *par;
710 register FXint x,y,h,w;
711 if(item){
712
713 // Expand parents of this node
714 if(!(options&TREELIST_AUTOSELECT)){
715 for(par=item->parent; par; par=par->parent){
716 expandTree(par);
717 }
718 }
719
720 // Now we adjust the scrolled position to fit everything
721 if(xid){
722
723 // Force layout if dirty
724 if(flags&FLAG_RECALC) layout();
725
726 x=pos_x;
727 y=pos_y;
728 w=item->getWidth(this);
729 h=item->getHeight(this);
730
731 if(viewport_w<=x+item->x+w) x=viewport_w-item->x-w;
732 if(x+item->x<=0) x=-item->x;
733
734 if(viewport_h<=y+item->y+h) y=viewport_h-item->y-h;
735 if(y+item->y<=0) y=-item->y;
736
737 // Scroll into view
738 setPosition(x,y);
739 }
740 }
741 }
742
743
744 // Get item at position x,y
getItemAt(FXint,FXint y) const745 FXTreeItem* FXTreeList::getItemAt(FXint,FXint y) const {
746 register FXTreeItem* item=firstitem;
747 register FXint ix,iy,ih;
748 ix=pos_x;
749 iy=pos_y;
750 if(options&TREELIST_ROOT_BOXES) ix+=(4+indent);
751 while(item && iy<=y){
752 ih=item->getHeight(this);
753 if(y<iy+ih) return item;
754 iy+=ih;
755 if(item->first && ((options&TREELIST_AUTOSELECT) || item->isExpanded())){
756 ix+=(indent+ih/2);
757 item=item->first;
758 continue;
759 }
760 while(!item->next && item->parent){
761 item=item->parent;
762 ix-=(indent+item->getHeight(this)/2);
763 }
764 item=item->next;
765 }
766 return NULL;
767 }
768
769
770 // Did we hit the item, and which part of it did we hit (0=outside, 1=icon, 2=text, 3=box)
hitItem(const FXTreeItem * item,FXint x,FXint y) const771 FXint FXTreeList::hitItem(const FXTreeItem* item,FXint x,FXint y) const {
772 register FXint ix,iy,ih,xh,yh,hit=0;
773 if(item){
774 x-=pos_x;
775 y-=pos_y;
776 ix=item->x;
777 iy=item->y;
778 ih=item->getHeight(this);
779 if(iy<=y && y<iy+ih){
780 if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){
781 xh=ix-indent+(SIDE_SPACING/2);
782 yh=iy+ih/2;
783 if(xh-HALFBOX_SIZE-BOX_FUDGE<=x && x<=xh+HALFBOX_SIZE+BOX_FUDGE && yh-HALFBOX_SIZE-BOX_FUDGE<=y && y<=yh+HALFBOX_SIZE+BOX_FUDGE) return 3;
784 }
785 hit=item->hitItem(this,x-ix,y-iy);
786 }
787 }
788 return hit;
789 }
790
791
792 // Repaint
updateItem(FXTreeItem * item) const793 void FXTreeList::updateItem(FXTreeItem* item) const {
794 if(item) update(0,pos_y+item->y,width,item->getHeight(this));
795 }
796
797
798 // Enable one item
enableItem(FXTreeItem * item)799 FXbool FXTreeList::enableItem(FXTreeItem* item){
800 if(!item){ fxerror("%s::enableItem: NULL argument.\n",getClassName()); }
801 if(!item->isEnabled()){
802 item->setEnabled(TRUE);
803 updateItem(item);
804 return TRUE;
805 }
806 return FALSE;
807 }
808
809
810 // Disable one item
disableItem(FXTreeItem * item)811 FXbool FXTreeList::disableItem(FXTreeItem* item){
812 if(!item){ fxerror("%s::disableItem: NULL argument.\n",getClassName()); }
813 if(item->isEnabled()){
814 item->setEnabled(FALSE);
815 updateItem(item);
816 return TRUE;
817 }
818 return FALSE;
819 }
820
821
822 // Select one item
selectItem(FXTreeItem * item,FXbool notify)823 FXbool FXTreeList::selectItem(FXTreeItem* item,FXbool notify){
824 if(!item){ fxerror("%s::selectItem: NULL argument.\n",getClassName()); }
825 if(!item->isSelected()){
826 switch(options&SELECT_MASK){
827 case TREELIST_SINGLESELECT:
828 case TREELIST_BROWSESELECT:
829 killSelection(notify);
830 case TREELIST_EXTENDEDSELECT:
831 case TREELIST_MULTIPLESELECT:
832 item->setSelected(TRUE);
833 updateItem(item);
834 if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)item);}
835 break;
836 }
837 return TRUE;
838 }
839 return FALSE;
840 }
841
842
843 // Deselect one item
deselectItem(FXTreeItem * item,FXbool notify)844 FXbool FXTreeList::deselectItem(FXTreeItem* item,FXbool notify){
845 if(!item){ fxerror("%s::deselectItem: NULL argument.\n",getClassName()); }
846 if(item->isSelected()){
847 switch(options&SELECT_MASK){
848 case TREELIST_EXTENDEDSELECT:
849 case TREELIST_MULTIPLESELECT:
850 case TREELIST_SINGLESELECT:
851 item->setSelected(FALSE);
852 updateItem(item);
853 if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)item);}
854 break;
855 }
856 return TRUE;
857 }
858 return FALSE;
859 }
860
861
862 // toggle one item
toggleItem(FXTreeItem * item,FXbool notify)863 FXbool FXTreeList::toggleItem(FXTreeItem* item,FXbool notify){
864 if(!item){ fxerror("%s::toggleItem: NULL argument.\n",getClassName()); }
865 switch(options&SELECT_MASK){
866 case TREELIST_BROWSESELECT:
867 if(!item->isSelected()){
868 killSelection(notify);
869 item->setSelected(TRUE);
870 updateItem(item);
871 if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)item);}
872 }
873 break;
874 case TREELIST_SINGLESELECT:
875 if(!item->isSelected()){
876 killSelection(notify);
877 item->setSelected(TRUE);
878 updateItem(item);
879 if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)item);}
880 }
881 else{
882 item->setSelected(FALSE);
883 updateItem(item);
884 if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)item);}
885 }
886 break;
887 case TREELIST_EXTENDEDSELECT:
888 case TREELIST_MULTIPLESELECT:
889 if(!item->isSelected()){
890 item->setSelected(TRUE);
891 updateItem(item);
892 if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)item);}
893 }
894 else{
895 item->setSelected(FALSE);
896 updateItem(item);
897 if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)item);}
898 }
899 break;
900 }
901 return TRUE;
902 }
903
904
905 // Extend selection
extendSelection(FXTreeItem * item,FXbool notify)906 FXbool FXTreeList::extendSelection(FXTreeItem* item,FXbool notify){
907 register FXTreeItem *it,*i1,*i2,*i3;
908 register FXbool changes=FALSE;
909 if(item && anchoritem && extentitem){
910 it=firstitem;
911 i1=i2=i3=NULL;
912
913 // Find segments
914 while(it){
915 if(it==item){i1=i2;i2=i3;i3=it;}
916 if(it==anchoritem){i1=i2;i2=i3;i3=it;}
917 if(it==extentitem){i1=i2;i2=i3;i3=it;}
918 it=it->getBelow();
919 }
920
921 FXASSERT(i1 && i2 && i3);
922
923 // First segment
924 it=i1;
925 while(it!=i2){
926
927 // item = extent - anchor
928 // item = anchor - extent
929 if(i1==item){
930 if(!it->isSelected()){
931 it->setSelected(TRUE);
932 updateItem(it);
933 changes=TRUE;
934 if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)it);}
935 }
936 }
937
938 // extent = anchor - item
939 // extent = item - anchor
940 else if(i1==extentitem){
941 if(it->isSelected()){
942 it->setSelected(FALSE);
943 updateItem(it);
944 changes=TRUE;
945 if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)it);}
946 }
947 }
948 it=it->getBelow();
949 }
950
951 // Second segment
952 it=i2;
953 while(it!=i3){
954 it=it->getBelow();
955
956 // extent - anchor = item
957 // anchor - extent = item
958 if(i3==item){
959 if(!it->isSelected()){
960 it->setSelected(TRUE);
961 updateItem(it);
962 changes=TRUE;
963 if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)it);}
964 }
965 }
966
967 // item - anchor = extent
968 // anchor - item = extent
969 else if(i3==extentitem){
970 if(it->isSelected()){
971 it->setSelected(FALSE);
972 updateItem(it);
973 changes=TRUE;
974 if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)it);}
975 }
976 }
977 }
978 extentitem=item;
979 }
980 return changes;
981 }
982
983
984 // Kill selection
killSelection(FXbool notify)985 FXbool FXTreeList::killSelection(FXbool notify){
986 register FXTreeItem *item=firstitem;
987 register FXbool changes=FALSE;
988 while(item){
989 if(item->isSelected()){
990 item->setSelected(FALSE);
991 updateItem(item);
992 changes=TRUE;
993 if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)item);}
994 }
995 item=item->getBelow();
996 }
997 return changes;
998 }
999
1000
1001 // Open item
openItem(FXTreeItem * item,FXbool notify)1002 FXbool FXTreeList::openItem(FXTreeItem* item,FXbool notify){
1003 if(item==NULL){ fxerror("%s::openItem: NULL argument.\n",getClassName()); }
1004 if(!item->isOpened()){
1005 item->setOpened(TRUE);
1006 updateItem(item);
1007 if(notify && target){target->tryHandle(this,FXSEL(SEL_OPENED,message),(void*)item);}
1008 return TRUE;
1009 }
1010 return FALSE;
1011 }
1012
1013
1014 // Close item
closeItem(FXTreeItem * item,FXbool notify)1015 FXbool FXTreeList::closeItem(FXTreeItem* item,FXbool notify){
1016 if(item==NULL){ fxerror("%s::closeItem: NULL argument.\n",getClassName()); }
1017 if(item->isOpened()){
1018 item->setOpened(FALSE);
1019 updateItem(item);
1020 if(notify && target){target->tryHandle(this,FXSEL(SEL_CLOSED,message),(void*)item);}
1021 return TRUE;
1022 }
1023 return FALSE;
1024 }
1025
1026
1027 // Collapse all subtrees under item
collapseTree(FXTreeItem * tree,FXbool notify)1028 FXbool FXTreeList::collapseTree(FXTreeItem* tree,FXbool notify){
1029 if(tree==NULL){ fxerror("%s::collapseTree: NULL argument.\n",getClassName()); }
1030 if(tree->isExpanded()){
1031 tree->setExpanded(FALSE);
1032 if(!(options&TREELIST_AUTOSELECT)){ // In autoselect, already shown as expanded!
1033 if(tree->first){
1034 recalc();
1035 }
1036 else{
1037 updateItem(tree);
1038 }
1039 }
1040 if(notify && target){target->tryHandle(this,FXSEL(SEL_COLLAPSED,message),(void*)tree);}
1041 return TRUE;
1042 }
1043 return FALSE;
1044 }
1045
1046
1047 // Expand subtree under item
expandTree(FXTreeItem * tree,FXbool notify)1048 FXbool FXTreeList::expandTree(FXTreeItem* tree,FXbool notify){
1049 if(tree==NULL){ fxerror("%s::expandTree: NULL argument.\n",getClassName()); }
1050 if(!tree->isExpanded()){
1051 tree->setExpanded(TRUE);
1052 if(!(options&TREELIST_AUTOSELECT)){ // In autoselect, already shown as expanded!
1053 if(tree->first){
1054 recalc();
1055 }
1056 else{
1057 updateItem(tree);
1058 }
1059 }
1060 if(notify && target){target->tryHandle(this,FXSEL(SEL_EXPANDED,message),(void*)tree);}
1061 return TRUE;
1062 }
1063 return FALSE;
1064 }
1065
1066
1067 // Start motion timer while in this window
onEnter(FXObject * sender,FXSelector sel,void * ptr)1068 long FXTreeList::onEnter(FXObject* sender,FXSelector sel,void* ptr){
1069 FXScrollArea::onEnter(sender,sel,ptr);
1070 getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
1071 cursoritem=NULL;
1072 return 1;
1073 }
1074
1075
1076 // Stop motion timer when leaving window
onLeave(FXObject * sender,FXSelector sel,void * ptr)1077 long FXTreeList::onLeave(FXObject* sender,FXSelector sel,void* ptr){
1078 FXScrollArea::onLeave(sender,sel,ptr);
1079 getApp()->removeTimeout(this,ID_TIPTIMER);
1080 cursoritem=NULL;
1081 return 1;
1082 }
1083
1084
1085 // We timed out, i.e. the user didn't move for a while
onTipTimer(FXObject *,FXSelector,void *)1086 long FXTreeList::onTipTimer(FXObject*,FXSelector,void*){
1087 FXTRACE((200,"%s::onTipTimer %p\n",getClassName(),this));
1088 flags|=FLAG_TIP;
1089 return 1;
1090 }
1091
1092
1093 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)1094 long FXTreeList::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
1095 if(FXWindow::onQueryTip(sender,sel,ptr)) return 1;
1096 if((flags&FLAG_TIP) && !(options&TREELIST_AUTOSELECT)){ // No tip when autoselect!
1097 FXint x,y; FXuint buttons;
1098 getCursorPosition(x,y,buttons);
1099 FXTreeItem *item=getItemAt(x,y);
1100 if(item){
1101 FXString string=item->getText();
1102 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&string);
1103 return 1;
1104 }
1105 }
1106 return 0;
1107 }
1108
1109
1110 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)1111 long FXTreeList::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
1112 if(FXWindow::onQueryHelp(sender,sel,ptr)) return 1;
1113 if(!help.empty() && (flags&FLAG_HELP)){
1114 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
1115 return 1;
1116 }
1117 return 0;
1118 }
1119
1120
1121 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)1122 long FXTreeList::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
1123 FXScrollArea::onFocusIn(sender,sel,ptr);
1124 if(currentitem){
1125 currentitem->setFocus(TRUE);
1126 updateItem(currentitem);
1127 }
1128 return 1;
1129 }
1130
1131
1132 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)1133 long FXTreeList::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
1134 FXScrollArea::onFocusOut(sender,sel,ptr);
1135 if(currentitem){
1136 currentitem->setFocus(FALSE);
1137 updateItem(currentitem);
1138 }
1139 return 1;
1140 }
1141
1142
1143 // Draw item list
onPaint(FXObject *,FXSelector,void * ptr)1144 long FXTreeList::onPaint(FXObject*,FXSelector,void* ptr){
1145 FXEvent* event=(FXEvent*)ptr;
1146 FXTreeItem* item=firstitem;
1147 FXTreeItem* p;
1148 FXint yh,xh,x,y,w,h,xp,hh;
1149 FXDCWindow dc(this,event);
1150 dc.setFont(font);
1151 x=pos_x;
1152 y=pos_y;
1153 if(options&TREELIST_ROOT_BOXES) x+=(4+indent);
1154 while(item && y<event->rect.y+event->rect.h){
1155 w=item->getWidth(this);
1156 h=item->getHeight(this);
1157 if(event->rect.y<=y+h){
1158
1159 // Draw item
1160 dc.setForeground(backColor);
1161 dc.fillRectangle(0,y,width,h);
1162 item->draw(this,dc,x,y,w,h);
1163
1164 // Show other paraphernalia such as dotted lines and expand-boxes
1165 if((options&(TREELIST_SHOWS_LINES|TREELIST_SHOWS_BOXES)) && (item->parent || (options&TREELIST_ROOT_BOXES))){
1166 hh=h/2;
1167 yh=y+hh;
1168 xh=x-indent+(SIDE_SPACING/2);
1169 dc.setForeground(lineColor);
1170 dc.setBackground(backColor);
1171 dc.setStipple(STIPPLE_GRAY,pos_x&1,pos_y&1);
1172 if(options&TREELIST_SHOWS_LINES){ // Connect items with lines
1173 p=item->parent;
1174 xp=xh;
1175 dc.setFillStyle(FILL_OPAQUESTIPPLED);
1176 while(p){
1177 xp-=(indent+p->getHeight(this)/2);
1178 if(p->next) dc.fillRectangle(xp,y,1,h);
1179 p=p->parent;
1180 }
1181 if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){
1182 if(item->prev || item->parent) dc.fillRectangle(xh,y,1,yh-y-HALFBOX_SIZE);
1183 if(item->next) dc.fillRectangle(xh,yh+HALFBOX_SIZE,1,y+h-yh-HALFBOX_SIZE);
1184 }
1185 else{
1186 if(item->prev || item->parent) dc.fillRectangle(xh,y,1,hh);
1187 if(item->next) dc.fillRectangle(xh,yh,1,h);
1188 dc.fillRectangle(xh,yh,x+(SIDE_SPACING/2)-2-xh,1);
1189 }
1190 dc.setFillStyle(FILL_SOLID);
1191 }
1192
1193 // Boxes before items for expand/collapse of item
1194 if((options&TREELIST_SHOWS_BOXES) && (item->hasItems() || item->getFirst())){
1195 dc.setFillStyle(FILL_OPAQUESTIPPLED);
1196 dc.fillRectangle(xh+4,yh,(SIDE_SPACING/2)-2,1);
1197 dc.setFillStyle(FILL_SOLID);
1198 dc.drawRectangle(xh-HALFBOX_SIZE,yh-HALFBOX_SIZE,HALFBOX_SIZE+HALFBOX_SIZE,HALFBOX_SIZE+HALFBOX_SIZE);
1199 dc.setForeground(textColor);
1200 dc.fillRectangle(xh-HALFBOX_SIZE+2,yh,HALFBOX_SIZE+HALFBOX_SIZE-3,1);
1201 if(!(options&TREELIST_AUTOSELECT) && !item->isExpanded()){
1202 dc.fillRectangle(xh,yh-HALFBOX_SIZE+2,1,HALFBOX_SIZE+HALFBOX_SIZE-3);
1203 }
1204 }
1205 }
1206 }
1207
1208 y+=h;
1209
1210 // Move on to the next item
1211 if(item->first && ((options&TREELIST_AUTOSELECT) || item->isExpanded())){
1212 x+=(indent+h/2);
1213 item=item->first;
1214 continue;
1215 }
1216 while(!item->next && item->parent){
1217 item=item->parent;
1218 x-=(indent+item->getHeight(this)/2);
1219 }
1220 item=item->next;
1221 }
1222 if(y<event->rect.y+event->rect.h){
1223 dc.setForeground(backColor);
1224 dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y);
1225 }
1226 return 1;
1227 }
1228
1229 // Zero out lookup string
onLookupTimer(FXObject *,FXSelector,void *)1230 long FXTreeList::onLookupTimer(FXObject*,FXSelector,void*){
1231 lookup=FXString::null;
1232 return 1;
1233 }
1234
1235
1236 // Key Press
onKeyPress(FXObject *,FXSelector,void * ptr)1237 long FXTreeList::onKeyPress(FXObject*,FXSelector,void* ptr){
1238 FXEvent* event=(FXEvent*)ptr;
1239 FXTreeItem *item=currentitem;
1240 flags&=~FLAG_TIP;
1241 if(!isEnabled()) return 0;
1242 if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
1243 if(item==NULL) item=firstitem;
1244 switch(event->code){
1245 case KEY_Control_L:
1246 case KEY_Control_R:
1247 case KEY_Shift_L:
1248 case KEY_Shift_R:
1249 case KEY_Alt_L:
1250 case KEY_Alt_R:
1251 if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
1252 return 1;
1253 case KEY_Page_Up:
1254 case KEY_KP_Page_Up:
1255 lookup=FXString::null;
1256 setPosition(pos_x,pos_y+verticalScrollBar()->getPage());
1257 return 1;
1258 case KEY_Page_Down:
1259 case KEY_KP_Page_Down:
1260 lookup=FXString::null;
1261 setPosition(pos_x,pos_y-verticalScrollBar()->getPage());
1262 return 1;
1263 case KEY_Up: // Move up
1264 case KEY_KP_Up:
1265 if(item){
1266 if(item->prev){
1267 item=item->prev;
1268 while(item->first && ((options&TREELIST_AUTOSELECT) || item->isExpanded())) item=item->last;
1269 }
1270 else if(item->parent){
1271 item=item->parent;
1272 }
1273 }
1274 goto hop;
1275 case KEY_Down: // Move down
1276 case KEY_KP_Down:
1277 if(item){
1278 if(item->first && ((options&TREELIST_AUTOSELECT) || item->isExpanded())){
1279 item=item->first;
1280 }
1281 else{
1282 while(!item->next && item->parent) item=item->parent;
1283 item=item->next;
1284 }
1285 }
1286 goto hop;
1287 case KEY_Right: // Move right/down and open subtree
1288 case KEY_KP_Right:
1289 if(item){
1290 if(!(options&TREELIST_AUTOSELECT) && !item->isExpanded() && (item->hasItems() || item->getFirst())){
1291 expandTree(item,TRUE);
1292 }
1293 else if(item->first){
1294 item=item->first;
1295 }
1296 else{
1297 while(!item->next && item->parent) item=item->parent;
1298 item=item->next;
1299 }
1300 }
1301 goto hop;
1302 case KEY_Left: // Move left/up and close subtree
1303 case KEY_KP_Left:
1304 if(item){
1305 if(!(options&TREELIST_AUTOSELECT) && item->isExpanded() && (item->hasItems() || item->getFirst())){
1306 collapseTree(item,TRUE);
1307 }
1308 else if(item->parent){
1309 item=item->parent;
1310 }
1311 else if(item->prev){
1312 item=item->prev;
1313 }
1314 }
1315 goto hop;
1316 case KEY_Home: // Move to first
1317 case KEY_KP_Home:
1318 item=firstitem;
1319 goto hop;
1320 case KEY_End: // Move to last
1321 case KEY_KP_End:
1322 item=lastitem;
1323 while(item){
1324 if(item->last && ((options&TREELIST_AUTOSELECT) || item->isExpanded())){
1325 item=item->last;
1326 }
1327 else if(item->next){
1328 item=item->next;
1329 }
1330 else{
1331 break;
1332 }
1333 }
1334 hop: lookup=FXString::null;
1335 if(item){
1336 setCurrentItem(item,TRUE);
1337 makeItemVisible(item);
1338 if((options&SELECT_MASK)==TREELIST_EXTENDEDSELECT){
1339 if(item->isEnabled()){
1340 if(event->state&SHIFTMASK){
1341 if(anchoritem){
1342 selectItem(anchoritem,TRUE);
1343 extendSelection(item,TRUE);
1344 }
1345 else{
1346 selectItem(item,TRUE);
1347 setAnchorItem(item);
1348 }
1349 }
1350 else if(!(event->state&CONTROLMASK)){
1351 killSelection(TRUE);
1352 selectItem(item,TRUE);
1353 setAnchorItem(item);
1354 }
1355 }
1356 }
1357 }
1358 handle(this,FXSEL(SEL_CLICKED,0),(void*)currentitem);
1359 if(currentitem && currentitem->isEnabled()){
1360 handle(this,FXSEL(SEL_COMMAND,0),(void*)currentitem);
1361 }
1362 return 1;
1363 case KEY_space:
1364 case KEY_KP_Space:
1365 lookup=FXString::null;
1366 if(item && item->isEnabled()){
1367 switch(options&SELECT_MASK){
1368 case TREELIST_EXTENDEDSELECT:
1369 if(event->state&SHIFTMASK){
1370 if(anchoritem){
1371 selectItem(anchoritem,TRUE);
1372 extendSelection(item,TRUE);
1373 }
1374 else{
1375 selectItem(item,TRUE);
1376 }
1377 }
1378 else if(event->state&CONTROLMASK){
1379 toggleItem(item,TRUE);
1380 }
1381 else{
1382 killSelection(TRUE);
1383 selectItem(item,TRUE);
1384 }
1385 break;
1386 case TREELIST_MULTIPLESELECT:
1387 case TREELIST_SINGLESELECT:
1388 toggleItem(item,TRUE);
1389 break;
1390 }
1391 setAnchorItem(item);
1392 }
1393 handle(this,FXSEL(SEL_CLICKED,0),(void*)currentitem);
1394 if(currentitem && currentitem->isEnabled()){
1395 handle(this,FXSEL(SEL_COMMAND,0),(void*)currentitem);
1396 }
1397 return 1;
1398 case KEY_Return:
1399 case KEY_KP_Enter:
1400 lookup=FXString::null;
1401 handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)currentitem);
1402 if(currentitem && currentitem->isEnabled()){
1403 handle(this,FXSEL(SEL_COMMAND,0),(void*)currentitem);
1404 }
1405 return 1;
1406 default:
1407 if((FXuchar)event->text[0]<' ') return 0;
1408 if(event->state&(CONTROLMASK|ALTMASK)) return 0;
1409 if(!isprint((FXuchar)event->text[0])) return 0;
1410 lookup.append(event->text);
1411 getApp()->addTimeout(this,ID_LOOKUPTIMER,getApp()->getTypingSpeed());
1412 item=findItem(lookup,currentitem,SEARCH_FORWARD|SEARCH_WRAP|SEARCH_PREFIX);
1413 if(item){
1414 setCurrentItem(item,TRUE);
1415 makeItemVisible(item);
1416 if((options&SELECT_MASK)==TREELIST_EXTENDEDSELECT){
1417 if(item->isEnabled()){
1418 killSelection(TRUE);
1419 selectItem(item,TRUE);
1420 }
1421 }
1422 setAnchorItem(item);
1423 }
1424 handle(this,FXSEL(SEL_CLICKED,0),(void*)currentitem);
1425 if(currentitem && currentitem->isEnabled()){
1426 handle(this,FXSEL(SEL_COMMAND,0),(void*)currentitem);
1427 }
1428 return 1;
1429 }
1430 return 0;
1431 }
1432
1433
1434 // Key Release
onKeyRelease(FXObject *,FXSelector,void * ptr)1435 long FXTreeList::onKeyRelease(FXObject*,FXSelector,void* ptr){
1436 FXEvent* event=(FXEvent*)ptr;
1437 if(!isEnabled()) return 0;
1438 if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
1439 switch(event->code){
1440 case KEY_Shift_L:
1441 case KEY_Shift_R:
1442 case KEY_Control_L:
1443 case KEY_Control_R:
1444 case KEY_Alt_L:
1445 case KEY_Alt_R:
1446 if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
1447 return 1;
1448 }
1449 return 0;
1450 }
1451
1452
1453 // Scroll timer
onAutoScroll(FXObject * sender,FXSelector sel,void * ptr)1454 long FXTreeList::onAutoScroll(FXObject* sender,FXSelector sel,void* ptr){
1455 FXEvent* event=(FXEvent*)ptr;
1456 FXTreeItem *item;
1457 FXint xx,yy;
1458
1459 // Scroll the content
1460 FXScrollArea::onAutoScroll(sender,sel,ptr);
1461
1462 // Drag and drop mode
1463 if(flags&FLAG_DODRAG){
1464 handle(this,FXSEL(SEL_DRAGGED,0),ptr);
1465 return 1;
1466 }
1467
1468 // In autoselect mode, stop scrolling when mouse outside window
1469 if((flags&FLAG_PRESSED) || (options&TREELIST_AUTOSELECT)){
1470
1471 // Validated position
1472 xx=event->win_x; if(xx<0) xx=0; else if(xx>=viewport_w) xx=viewport_w-1;
1473 yy=event->win_y; if(yy<0) yy=0; else if(yy>=viewport_h) yy=viewport_h-1;
1474
1475 // Find item
1476 item=getItemAt(xx,yy);
1477
1478 // Got item and different from last time
1479 if(item && item!=currentitem){
1480
1481 // Make it the current item
1482 setCurrentItem(item,TRUE);
1483
1484 // Extend the selection
1485 if((options&SELECT_MASK)==TREELIST_EXTENDEDSELECT){
1486 state=FALSE;
1487 extendSelection(item,TRUE);
1488 }
1489 }
1490 return 1;
1491 }
1492 return 0;
1493 }
1494
1495
1496 // Mouse motion
onMotion(FXObject *,FXSelector,void * ptr)1497 long FXTreeList::onMotion(FXObject*,FXSelector,void* ptr){
1498 FXEvent* event=(FXEvent*)ptr;
1499 FXTreeItem *oldcursoritem=cursoritem;
1500 FXuint flg=flags;
1501 FXTreeItem *item;
1502
1503 // Kill the tip
1504 flags&=~FLAG_TIP;
1505
1506 // Kill the tip timer
1507 getApp()->removeTimeout(this,ID_TIPTIMER);
1508
1509 // Right mouse scrolling
1510 if(flags&FLAG_SCROLLING){
1511 setPosition(event->win_x-grabx,event->win_y-graby);
1512 return 1;
1513 }
1514
1515 // Drag and drop mode
1516 if(flags&FLAG_DODRAG){
1517 if(startAutoScroll(event,TRUE)) return 1;
1518 handle(this,FXSEL(SEL_DRAGGED,0),ptr);
1519 return 1;
1520 }
1521
1522 // Tentative drag and drop
1523 if((flags&FLAG_TRYDRAG) && event->moved){
1524 flags&=~FLAG_TRYDRAG;
1525 if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)){
1526 flags|=FLAG_DODRAG;
1527 }
1528 return 1;
1529 }
1530
1531 // Normal operation
1532 if((flags&FLAG_PRESSED) || (options&TREELIST_AUTOSELECT)){
1533
1534 // Start auto scrolling?
1535 if(startAutoScroll(event,FALSE)) return 1;
1536
1537 // Find item
1538 item=getItemAt(event->win_x,event->win_y);
1539
1540 // Got an item different from before
1541 if(item && item!=currentitem){
1542
1543 // Make it the current item
1544 setCurrentItem(item,TRUE);
1545
1546 // Extend the selection
1547 if((options&SELECT_MASK)==TREELIST_EXTENDEDSELECT){
1548 state=FALSE;
1549 extendSelection(item,TRUE);
1550 }
1551 }
1552 return 1;
1553 }
1554
1555 // Reset tip timer if nothing's going on
1556 getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
1557
1558 // Get item we're over
1559 cursoritem=getItemAt(event->win_x,event->win_y);
1560
1561 // Force GUI update only when needed
1562 return (cursoritem!=oldcursoritem)||(flg&FLAG_TIP);
1563 }
1564
1565
1566 // Pressed a button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)1567 long FXTreeList::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
1568 FXEvent* event=(FXEvent*)ptr;
1569 FXTreeItem *item;
1570 FXint code;
1571 flags&=~FLAG_TIP;
1572 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
1573 if(isEnabled()){
1574 grab();
1575 flags&=~FLAG_UPDATE;
1576
1577 // First chance callback
1578 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
1579
1580 // Not autoselect mode
1581 if(options&TREELIST_AUTOSELECT) return 1;
1582
1583 // Locate item
1584 item=getItemAt(event->win_x,event->win_y);
1585
1586 // No item
1587 if(item==NULL){
1588 if((options&SELECT_MASK)==TREELIST_EXTENDEDSELECT){
1589 if(!(event->state&(SHIFTMASK|CONTROLMASK))){
1590 killSelection(TRUE);
1591 }
1592 }
1593 return 1;
1594 }
1595
1596 // Find out where hit
1597 code=hitItem(item,event->win_x,event->win_y);
1598
1599 // Maybe clicked on box
1600 if(code==3){
1601 if(isItemExpanded(item))
1602 collapseTree(item,TRUE);
1603 else
1604 expandTree(item,TRUE);
1605 return 1;
1606 }
1607
1608 // Change current item
1609 setCurrentItem(item,TRUE);
1610
1611 // Change item selection
1612 state=item->isSelected();
1613 switch(options&SELECT_MASK){
1614 case TREELIST_EXTENDEDSELECT:
1615 if(event->state&SHIFTMASK){
1616 if(anchoritem){
1617 if(anchoritem->isEnabled()) selectItem(anchoritem,TRUE);
1618 extendSelection(item,TRUE);
1619 }
1620 else{
1621 if(item->isEnabled()) selectItem(item,TRUE);
1622 setAnchorItem(item);
1623 }
1624 }
1625 else if(event->state&CONTROLMASK){
1626 if(item->isEnabled() && !state) selectItem(item,TRUE);
1627 setAnchorItem(item);
1628 }
1629 else{
1630 if(item->isEnabled() && !state){ killSelection(TRUE); selectItem(item,TRUE); }
1631 setAnchorItem(item);
1632 }
1633 break;
1634 case TREELIST_MULTIPLESELECT:
1635 case TREELIST_SINGLESELECT:
1636 if(item->isEnabled() && !state) selectItem(item,TRUE);
1637 break;
1638 }
1639
1640 // Start drag if actually pressed text or icon only
1641 if(code && item->isSelected() && item->isDraggable()){
1642 flags|=FLAG_TRYDRAG;
1643 }
1644
1645 flags|=FLAG_PRESSED;
1646 return 1;
1647 }
1648 return 0;
1649 }
1650
1651
1652 // Released button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)1653 long FXTreeList::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
1654 FXEvent* event=(FXEvent*)ptr;
1655 FXuint flg=flags;
1656 if(isEnabled()){
1657 ungrab();
1658 stopAutoScroll();
1659 flags|=FLAG_UPDATE;
1660 flags&=~(FLAG_PRESSED|FLAG_TRYDRAG|FLAG_DODRAG);
1661
1662 // First chance callback
1663 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
1664
1665 // No activity
1666 if(!(flg&FLAG_PRESSED) && !(options&TREELIST_AUTOSELECT)) return 1;
1667
1668 // Was dragging
1669 if(flg&FLAG_DODRAG){
1670 handle(this,FXSEL(SEL_ENDDRAG,0),ptr);
1671 return 1;
1672 }
1673
1674 // Select only enabled item
1675 switch(options&SELECT_MASK){
1676 case TREELIST_EXTENDEDSELECT:
1677 if(currentitem && currentitem->isEnabled()){
1678 if(event->state&CONTROLMASK){
1679 if(state) deselectItem(currentitem,TRUE);
1680 }
1681 else if(!(event->state&SHIFTMASK)){
1682 if(state){ killSelection(TRUE); selectItem(currentitem,TRUE); }
1683 }
1684 }
1685 break;
1686 case TREELIST_MULTIPLESELECT:
1687 case TREELIST_SINGLESELECT:
1688 if(currentitem && currentitem->isEnabled()){
1689 if(state) deselectItem(currentitem,TRUE);
1690 }
1691 break;
1692 }
1693
1694 // Scroll to make item visibke
1695 makeItemVisible(currentitem);
1696
1697 // Update anchor
1698 setAnchorItem(currentitem);
1699
1700 // Generate clicked callbacks
1701 if(event->click_count==1){
1702 handle(this,FXSEL(SEL_CLICKED,0),(void*)currentitem);
1703 }
1704 else if(event->click_count==2){
1705 handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)currentitem);
1706 }
1707 else if(event->click_count==3){
1708 handle(this,FXSEL(SEL_TRIPLECLICKED,0),(void*)currentitem);
1709 }
1710
1711 // Command callback only when clicked on item
1712 if(currentitem && currentitem->isEnabled()){
1713 handle(this,FXSEL(SEL_COMMAND,0),(void*)currentitem);
1714 }
1715 return 1;
1716 }
1717 return 0;
1718 }
1719
1720
1721 // Pressed right button
onRightBtnPress(FXObject *,FXSelector,void * ptr)1722 long FXTreeList::onRightBtnPress(FXObject*,FXSelector,void* ptr){
1723 FXEvent* event=(FXEvent*)ptr;
1724 flags&=~FLAG_TIP;
1725 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
1726 if(isEnabled()){
1727 grab();
1728 flags&=~FLAG_UPDATE;
1729 if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1;
1730 flags|=FLAG_SCROLLING;
1731 grabx=event->win_x-pos_x;
1732 graby=event->win_y-pos_y;
1733 return 1;
1734 }
1735 return 0;
1736 }
1737
1738
1739 // Released right button
onRightBtnRelease(FXObject *,FXSelector,void * ptr)1740 long FXTreeList::onRightBtnRelease(FXObject*,FXSelector,void* ptr){
1741 if(isEnabled()){
1742 ungrab();
1743 flags&=~FLAG_SCROLLING;
1744 flags|=FLAG_UPDATE;
1745 if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1;
1746 return 1;
1747 }
1748 return 0;
1749 }
1750
1751
1752
1753 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)1754 long FXTreeList::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
1755 FXScrollArea::onUngrabbed(sender,sel,ptr);
1756 flags&=~(FLAG_DODRAG|FLAG_TRYDRAG|FLAG_PRESSED|FLAG_CHANGED|FLAG_SCROLLING);
1757 flags|=FLAG_UPDATE;
1758 stopAutoScroll();
1759 return 1;
1760 }
1761
1762
1763 // Command message
onCommand(FXObject *,FXSelector,void * ptr)1764 long FXTreeList::onCommand(FXObject*,FXSelector,void* ptr){
1765 return target && target->tryHandle(this,FXSEL(SEL_COMMAND,message),ptr);
1766 }
1767
1768
1769 // Clicked in list
onClicked(FXObject *,FXSelector,void * ptr)1770 long FXTreeList::onClicked(FXObject*,FXSelector,void* ptr){
1771 return target && target->tryHandle(this,FXSEL(SEL_CLICKED,message),ptr);
1772 }
1773
1774
1775 // Double clicked in list; ptr may or may not point to an item
onDoubleClicked(FXObject *,FXSelector,void * ptr)1776 long FXTreeList::onDoubleClicked(FXObject*,FXSelector,void* ptr){
1777
1778 // Double click anywhere in the widget
1779 if(target && target->tryHandle(this,FXSEL(SEL_DOUBLECLICKED,message),ptr)) return 1;
1780
1781 // Double click on an item
1782 if(ptr){
1783 if(isItemExpanded((FXTreeItem*)ptr))
1784 collapseTree((FXTreeItem*)ptr,TRUE);
1785 else
1786 expandTree((FXTreeItem*)ptr,TRUE);
1787 }
1788 return 0;
1789 }
1790
1791
1792 // Triple clicked in list; ptr may or may not point to an item
onTripleClicked(FXObject *,FXSelector,void * ptr)1793 long FXTreeList::onTripleClicked(FXObject*,FXSelector,void* ptr){
1794 return target && target->tryHandle(this,FXSEL(SEL_TRIPLECLICKED,message),ptr);
1795 }
1796
1797
1798 // Sort items in ascending order
ascending(const FXTreeItem * a,const FXTreeItem * b)1799 FXint FXTreeList::ascending(const FXTreeItem* a,const FXTreeItem* b){
1800 return compare(a->getText(),b->getText());
1801 }
1802
1803
1804 // Sort items in descending order
descending(const FXTreeItem * a,const FXTreeItem * b)1805 FXint FXTreeList::descending(const FXTreeItem* a,const FXTreeItem* b){
1806 return compare(b->getText(),a->getText());
1807 }
1808
1809
1810 // Sort ascending order, case insensitive
ascendingCase(const FXTreeItem * a,const FXTreeItem * b)1811 FXint FXTreeList::ascendingCase(const FXTreeItem* a,const FXTreeItem* b){
1812 return comparecase(a->getText(),b->getText());
1813 }
1814
1815
1816 // Sort descending order, case insensitive
descendingCase(const FXTreeItem * a,const FXTreeItem * b)1817 FXint FXTreeList::descendingCase(const FXTreeItem* a,const FXTreeItem* b){
1818 return comparecase(b->getText(),a->getText());
1819 }
1820
1821
1822 // Sort items
sort(FXTreeItem * & f1,FXTreeItem * & t1,FXTreeItem * & f2,FXTreeItem * & t2,int n)1823 void FXTreeList::sort(FXTreeItem*& f1,FXTreeItem*& t1,FXTreeItem*& f2,FXTreeItem*& t2,int n){
1824 FXTreeItem *ff1,*tt1,*ff2,*tt2,*q;
1825 FXint m;
1826 if(f2==NULL){
1827 f1=NULL;
1828 t1=NULL;
1829 return;
1830 }
1831 if(n>1){
1832 m=n/2;
1833 n=n-m;
1834 sort(ff1,tt1,f2,t2,n); // 1 or more
1835 sort(ff2,tt2,f2,t2,m); // 0 or more
1836 FXASSERT(ff1);
1837 if(ff2 && sortfunc(ff1,ff2)>0){
1838 f1=ff2;
1839 ff2->prev=NULL;
1840 ff2=ff2->next;
1841 }
1842 else{
1843 f1=ff1;
1844 ff1->prev=NULL;
1845 ff1=ff1->next;
1846 }
1847 t1=f1;
1848 t1->next=NULL;
1849 while(ff1 || ff2){
1850 if(ff1==NULL){ t1->next=ff2; ff2->prev=t1; t1=tt2; break; }
1851 if(ff2==NULL){ t1->next=ff1; ff1->prev=t1; t1=tt1; break; }
1852 if(sortfunc(ff1,ff2)>0){
1853 t1->next=ff2;
1854 ff2->prev=t1;
1855 t1=ff2;
1856 ff2=ff2->next;
1857 }
1858 else{
1859 t1->next=ff1;
1860 ff1->prev=t1;
1861 t1=ff1;
1862 ff1=ff1->next;
1863 }
1864 t1->next=NULL;
1865 }
1866 return;
1867 }
1868 FXASSERT(f2);
1869 f1=f2;
1870 t1=f2;
1871 f2=f2->next;
1872 while(f2){
1873 f2->prev=NULL;
1874 if(sortfunc(f2,t1)>0){
1875 t1->next=f2;
1876 f2->prev=t1;
1877 t1=f2;
1878 f2=f2->next;
1879 continue;
1880 }
1881 if(sortfunc(f1,f2)>0){
1882 q=f2;
1883 f2=f2->next;
1884 q->next=f1;
1885 f1->prev=q;
1886 f1=q;
1887 continue;
1888 }
1889 break;
1890 }
1891 FXASSERT(f1);
1892 FXASSERT(t1);
1893 f1->prev=NULL;
1894 t1->next=NULL;
1895 }
1896
1897
1898 // Sort root items
sortRootItems()1899 void FXTreeList::sortRootItems(){
1900 if(sortfunc){
1901 FXTreeItem* f=firstitem;
1902 FXTreeItem* l=lastitem;
1903 sort(firstitem,lastitem,f,l,getNumItems());
1904 recalc();
1905 }
1906 }
1907
1908
1909 // Sort child items
sortChildItems(FXTreeItem * item)1910 void FXTreeList::sortChildItems(FXTreeItem* item){
1911 if(sortfunc){
1912 FXTreeItem* f=item->first;
1913 FXTreeItem* l=item->last;
1914 sort(item->first,item->last,f,l,item->getNumChildren());
1915 if(item->isExpanded()) recalc(); // No need to recalc if it ain't visible!
1916 }
1917 }
1918
1919
1920 // Sort all items recursively
sortItems()1921 void FXTreeList::sortItems(){
1922 register FXTreeItem *item;
1923 if(sortfunc){
1924 sortRootItems();
1925 item=firstitem;
1926 while(item){
1927 sortChildItems(item);
1928 if(item->first){item=item->first;continue;}
1929 while(!item->next && item->parent){item=item->parent;}
1930 item=item->next;
1931 }
1932 }
1933 }
1934
1935
1936 // Set current item
setCurrentItem(FXTreeItem * item,FXbool notify)1937 void FXTreeList::setCurrentItem(FXTreeItem* item,FXbool notify){
1938 if(item!=currentitem){
1939
1940 // Deactivate old item
1941 if(currentitem){
1942
1943 // No visible change if it doen't have the focus
1944 if(hasFocus()){
1945 currentitem->setFocus(FALSE);
1946 updateItem(currentitem);
1947 }
1948
1949 // Close old item
1950 closeItem(currentitem,notify);
1951 }
1952
1953 currentitem=item;
1954
1955 // Activate new item
1956 if(currentitem){
1957
1958 // No visible change if it doen't have the focus
1959 if(hasFocus()){
1960 currentitem->setFocus(TRUE);
1961 updateItem(currentitem);
1962 }
1963
1964 // Open new item
1965 openItem(currentitem,notify);
1966 }
1967
1968 // Notify item change
1969 if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)currentitem);}
1970 }
1971
1972 // Select if browse mode
1973 if((options&SELECT_MASK)==TREELIST_BROWSESELECT && currentitem && currentitem->isEnabled()){
1974 selectItem(currentitem,notify);
1975 }
1976 }
1977
1978
1979 // Set anchor item
setAnchorItem(FXTreeItem * item)1980 void FXTreeList::setAnchorItem(FXTreeItem* item){
1981 anchoritem=item;
1982 extentitem=item;
1983 }
1984
1985
1986
1987 // Create item
createItem(const FXString & text,FXIcon * oi,FXIcon * ci,void * ptr)1988 FXTreeItem* FXTreeList::createItem(const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr){
1989 return new FXTreeItem(text,oi,ci,ptr);
1990 }
1991
1992
1993 // Insert item under father before other item
insertItem(FXTreeItem * other,FXTreeItem * father,FXTreeItem * item,FXbool notify)1994 FXTreeItem* FXTreeList::insertItem(FXTreeItem* other,FXTreeItem* father,FXTreeItem* item,FXbool notify){
1995 register FXTreeItem* olditem=currentitem;
1996
1997 // Verify correctness of arguments
1998 if(!item){ fxerror("%s::insertItem: NULL item argument.\n",getClassName()); }
1999 if(other && other->parent!=father){ fxerror("%s::insertItem: bad argument.\n",getClassName()); }
2000
2001 // Hang item into the list
2002 if(father){
2003 if(other){
2004 item->next=other;
2005 item->prev=other->prev;
2006 other->prev=item;
2007 }
2008 else{
2009 item->next=NULL;
2010 item->prev=father->last;
2011 father->last=item;
2012 }
2013 if(item->prev) item->prev->next=item; else father->first=item;
2014 }
2015 else{
2016 if(other){
2017 item->next=other;
2018 item->prev=other->prev;
2019 other->prev=item;
2020 }
2021 else{
2022 item->next=NULL;
2023 item->prev=lastitem;
2024 lastitem=item;
2025 }
2026 if(item->prev) item->prev->next=item; else firstitem=item;
2027 }
2028
2029 // Fill in the rest
2030 item->parent=father;
2031 item->first=NULL;
2032 item->last=NULL;
2033 item->x=0;
2034 item->y=0;
2035
2036 // Make current if just added
2037 if(!currentitem && item==lastitem) currentitem=item;
2038
2039 // Notify item has been inserted
2040 if(notify && target){target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)item);}
2041
2042 // Current item may have changed
2043 if(olditem!=currentitem){
2044 if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)currentitem);}
2045 }
2046
2047 // Was new item
2048 if(currentitem==item){
2049 if(hasFocus()){
2050 currentitem->setFocus(TRUE);
2051 }
2052 if((options&SELECT_MASK)==TREELIST_BROWSESELECT && currentitem->isEnabled()){
2053 selectItem(currentitem,notify);
2054 }
2055 }
2056
2057 // Redo layout
2058 recalc();
2059 return item;
2060 }
2061
2062
2063 // Insert item under father before other item
insertItem(FXTreeItem * other,FXTreeItem * father,const FXString & text,FXIcon * oi,FXIcon * ci,void * ptr,FXbool notify)2064 FXTreeItem* FXTreeList::insertItem(FXTreeItem* other,FXTreeItem* father,const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr,FXbool notify){
2065 return insertItem(other,father,createItem(text,oi,ci,ptr),notify);
2066 }
2067
2068
2069 // Append item under father
appendItem(FXTreeItem * father,FXTreeItem * item,FXbool notify)2070 FXTreeItem* FXTreeList::appendItem(FXTreeItem* father,FXTreeItem* item,FXbool notify){
2071 return insertItem(NULL,father,item,notify);
2072 }
2073
2074
2075 // Append item under father
appendItem(FXTreeItem * father,const FXString & text,FXIcon * oi,FXIcon * ci,void * ptr,FXbool notify)2076 FXTreeItem* FXTreeList::appendItem(FXTreeItem* father,const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr,FXbool notify){
2077 return insertItem(NULL,father,createItem(text,oi,ci,ptr),notify);
2078 }
2079
2080
2081 // Prepend item under father
prependItem(FXTreeItem * father,FXTreeItem * item,FXbool notify)2082 FXTreeItem* FXTreeList::prependItem(FXTreeItem* father,FXTreeItem* item,FXbool notify){
2083 return insertItem(father?father->first:firstitem,father,item,notify);
2084 }
2085
2086 // Prepend item under father
prependItem(FXTreeItem * father,const FXString & text,FXIcon * oi,FXIcon * ci,void * ptr,FXbool notify)2087 FXTreeItem* FXTreeList::prependItem(FXTreeItem* father,const FXString& text,FXIcon* oi,FXIcon* ci,void* ptr,FXbool notify){
2088 return insertItem(father?father->first:firstitem,father,createItem(text,oi,ci,ptr),notify);
2089 }
2090
2091
2092 // Fill list by appending items from array of strings
fillItems(FXTreeItem * father,const FXchar ** strings,FXIcon * oi,FXIcon * ci,void * ptr,FXbool notify)2093 FXint FXTreeList::fillItems(FXTreeItem* father,const FXchar** strings,FXIcon* oi,FXIcon* ci,void* ptr,FXbool notify){
2094 register FXint n=0;
2095 if(strings){
2096 while(strings[n]){
2097 appendItem(father,strings[n++],oi,ci,ptr,notify);
2098 }
2099 }
2100 return n;
2101 }
2102
2103
2104 // Fill list by appending items from newline separated strings
fillItems(FXTreeItem * father,const FXString & strings,FXIcon * oi,FXIcon * ci,void * ptr,FXbool notify)2105 FXint FXTreeList::fillItems(FXTreeItem* father,const FXString& strings,FXIcon* oi,FXIcon* ci,void* ptr,FXbool notify){
2106 register FXint n=0;
2107 FXString text;
2108 while(!(text=strings.section('\n',n)).empty()){
2109 appendItem(father,text,oi,ci,ptr,notify);
2110 n++;
2111 }
2112 return n;
2113 }
2114
2115
2116 // Move item under father before other item
moveItem(FXTreeItem * other,FXTreeItem * father,FXTreeItem * item)2117 FXTreeItem *FXTreeList::moveItem(FXTreeItem* other,FXTreeItem* father,FXTreeItem* item){
2118
2119 // Verify arguments
2120 if(!item){ fxerror("%s::moveItem: NULL item argument.\n",getClassName()); }
2121 if(other && other->parent!=father){ fxerror("%s::moveItem: bad argument.\n",getClassName()); }
2122
2123 // Can't move in front of itself
2124 if(item!=other){
2125
2126 // Unlink from current spot
2127 if(item->prev) item->prev->next=item->next; else if(item->parent) item->parent->first=item->next; else firstitem=item->next;
2128 if(item->next) item->next->prev=item->prev; else if(item->parent) item->parent->last=item->prev; else lastitem=item->prev;
2129
2130 // Hang item into the list
2131 if(father){
2132 if(other){
2133 item->next=other;
2134 item->prev=other->prev;
2135 other->prev=item;
2136 }
2137 else{
2138 item->next=NULL;
2139 item->prev=father->last;
2140 father->last=item;
2141 }
2142 if(item->prev) item->prev->next=item; else father->first=item;
2143 }
2144 else{
2145 if(other){
2146 item->next=other;
2147 item->prev=other->prev;
2148 other->prev=item;
2149 }
2150 else{
2151 item->next=NULL;
2152 item->prev=lastitem;
2153 lastitem=item;
2154 }
2155 if(item->prev) item->prev->next=item; else firstitem=item;
2156 }
2157
2158 // Fill in the rest
2159 item->parent=father;
2160
2161 // Redo layout
2162 recalc();
2163 }
2164 return item;
2165 }
2166
2167
2168 // Remove all siblings from [fm,to]
removeItems(FXTreeItem * fm,FXTreeItem * to,FXbool notify)2169 void FXTreeList::removeItems(FXTreeItem* fm,FXTreeItem* to,FXbool notify){
2170 register FXTreeItem *olditem=currentitem;
2171 register FXTreeItem *prv,*nxt,*par;
2172 if(fm && to){
2173 if(fm->parent!=to->parent){ fxerror("%s::removeItems: arguments have different parent.\n",getClassName()); }
2174
2175 // Delete items
2176 while(1){
2177
2178 // Scan till end
2179 while(to->last) to=to->last;
2180
2181 do{
2182
2183 // Notify item will be deleted
2184 if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)to);}
2185
2186 // Remember hookups
2187 nxt=to->next;
2188 prv=to->prev;
2189 par=to->parent;
2190
2191 // Adjust pointers; suggested by Alan Ott <ott@acusoft.com>
2192 if(anchoritem==to){ anchoritem=par; if(prv) anchoritem=prv; if(nxt) anchoritem=nxt; }
2193 if(extentitem==to){ extentitem=par; if(prv) extentitem=prv; if(nxt) extentitem=nxt; }
2194 if(currentitem==to){ currentitem=par; if(prv) currentitem=prv; if(nxt) currentitem=nxt; }
2195
2196 // Remove item from list
2197 if(prv) prv->next=nxt; else if(par) par->first=nxt; else firstitem=nxt;
2198 if(nxt) nxt->prev=prv; else if(par) par->last=prv; else lastitem=prv;
2199
2200 // Delete it
2201 delete to;
2202
2203 // Was last one?
2204 if(to==fm) goto x;
2205 to=par;
2206 }
2207 while(!prv);
2208 to=prv;
2209 }
2210
2211 // Current item has changed
2212 x: if(olditem!=currentitem){
2213 if(notify && target){target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)currentitem);}
2214 }
2215
2216 // Deleted current item
2217 if(currentitem && currentitem!=olditem){
2218 if(hasFocus()){
2219 currentitem->setFocus(TRUE);
2220 }
2221 if((options&SELECT_MASK)==TREELIST_BROWSESELECT && currentitem->isEnabled()){
2222 selectItem(currentitem,notify);
2223 }
2224 }
2225
2226 // Redo layout
2227 recalc();
2228 }
2229 }
2230
2231
2232 // Remove node from list
removeItem(FXTreeItem * item,FXbool notify)2233 void FXTreeList::removeItem(FXTreeItem* item,FXbool notify){
2234 removeItems(item,item,notify);
2235 }
2236
2237
2238 // Remove all items
clearItems(FXbool notify)2239 void FXTreeList::clearItems(FXbool notify){
2240 removeItems(firstitem,lastitem,notify);
2241 }
2242
2243
2244 typedef FXint (*FXCompareFunc)(const FXString&,const FXString &,FXint);
2245
2246
2247 // Get item by name
findItem(const FXString & text,FXTreeItem * start,FXuint flgs) const2248 FXTreeItem* FXTreeList::findItem(const FXString& text,FXTreeItem* start,FXuint flgs) const {
2249 register FXCompareFunc comparefunc;
2250 register FXTreeItem *item;
2251 register FXint len;
2252 if(firstitem){
2253 comparefunc=(flgs&SEARCH_IGNORECASE) ? (FXCompareFunc)comparecase : (FXCompareFunc)compare;
2254 len=(flgs&SEARCH_PREFIX)?text.length():2147483647;
2255 if(flgs&SEARCH_BACKWARD){
2256 item=start;
2257 while(item!=NULL){
2258 if((*comparefunc)(item->getText(),text,len)==0) return item;
2259 item=item->getAbove();
2260 }
2261 if(start && !(flgs&SEARCH_WRAP)) return NULL;
2262 for(item=lastitem; item->getLast(); item=item->getLast());
2263 while(item!=start){
2264 if((*comparefunc)(item->getText(),text,len)==0) return item;
2265 item=item->getAbove();
2266 }
2267 }
2268 else{
2269 item=start;
2270 while(item!=NULL){
2271 if((*comparefunc)(item->getText(),text,len)==0) return item;
2272 item=item->getBelow();
2273 }
2274 if(start && !(flgs&SEARCH_WRAP)) return NULL;
2275 item=firstitem;
2276 while(item!=start){
2277 if((*comparefunc)(item->getText(),text,len)==0) return item;
2278 item=item->getBelow();
2279 }
2280 }
2281 }
2282 return NULL;
2283 }
2284
2285
2286 // Get item by data
findItemByData(const void * ptr,FXTreeItem * start,FXuint flgs) const2287 FXTreeItem* FXTreeList::findItemByData(const void *ptr,FXTreeItem* start,FXuint flgs) const {
2288 register FXTreeItem *item;
2289 if(firstitem){
2290 if(flgs&SEARCH_BACKWARD){
2291 item=start;
2292 while(item!=NULL){
2293 if(item->getData()==ptr) return item;
2294 item=item->getAbove();
2295 }
2296 if(start && !(flgs&SEARCH_WRAP)) return NULL;
2297 for(item=lastitem; item->getLast(); item=item->getLast());
2298 while(item!=start){
2299 if(item->getData()==ptr) return item;
2300 item=item->getAbove();
2301 }
2302 }
2303 else{
2304 item=start;
2305 while(item!=NULL){
2306 if(item->getData()==ptr) return item;
2307 item=item->getBelow();
2308 }
2309 if(start && !(flgs&SEARCH_WRAP)) return NULL;
2310 item=firstitem;
2311 while(item!=start){
2312 if(item->getData()==ptr) return item;
2313 item=item->getBelow();
2314 }
2315 }
2316 }
2317 return NULL;
2318 }
2319
2320
2321 // Change the font
setFont(FXFont * fnt)2322 void FXTreeList::setFont(FXFont* fnt){
2323 if(!fnt){ fxerror("%s::setFont: NULL argument.\n",getClassName()); }
2324 if(font!=fnt){
2325 font=fnt;
2326 recalc();
2327 update();
2328 }
2329 }
2330
2331
2332 // Change help text
setHelpText(const FXString & text)2333 void FXTreeList::setHelpText(const FXString& text){
2334 help=text;
2335 }
2336
2337
2338 // Set text color
setTextColor(FXColor clr)2339 void FXTreeList::setTextColor(FXColor clr){
2340 if(clr!=textColor){
2341 textColor=clr;
2342 update();
2343 }
2344 }
2345
2346
2347 // Set select background color
setSelBackColor(FXColor clr)2348 void FXTreeList::setSelBackColor(FXColor clr){
2349 if(clr!=selbackColor){
2350 selbackColor=clr;
2351 update();
2352 }
2353 }
2354
2355
2356 // Set selected text color
setSelTextColor(FXColor clr)2357 void FXTreeList::setSelTextColor(FXColor clr){
2358 if(clr!=seltextColor){
2359 seltextColor=clr;
2360 update();
2361 }
2362 }
2363
2364
2365 // Set line color
setLineColor(FXColor clr)2366 void FXTreeList::setLineColor(FXColor clr){
2367 if(clr!=lineColor){
2368 lineColor=clr;
2369 update();
2370 }
2371 }
2372
2373
2374 // Set parent to child indent amount
setIndent(FXint in)2375 void FXTreeList::setIndent(FXint in){
2376 if(indent!=in){
2377 indent=in;
2378 recalc();
2379 }
2380 }
2381
2382
2383 // Change list style
setListStyle(FXuint style)2384 void FXTreeList::setListStyle(FXuint style){
2385 FXuint opts=(options&~TREELIST_MASK) | (style&TREELIST_MASK);
2386 if(options!=opts){
2387 options=opts;
2388 recalc();
2389 }
2390 }
2391
2392
2393 // Get list style
getListStyle() const2394 FXuint FXTreeList::getListStyle() const {
2395 return (options&TREELIST_MASK);
2396 }
2397
2398
2399 // Save data
save(FXStream & store) const2400 void FXTreeList::save(FXStream& store) const {
2401 FXScrollArea::save(store);
2402 store << firstitem;
2403 store << lastitem;
2404 store << anchoritem;
2405 store << currentitem;
2406 store << extentitem;
2407 store << font;
2408 store << textColor;
2409 store << selbackColor;
2410 store << seltextColor;
2411 store << lineColor;
2412 store << treeWidth;
2413 store << treeHeight;
2414 store << visible;
2415 store << indent;
2416 store << help;
2417 }
2418
2419
2420 // Load data
load(FXStream & store)2421 void FXTreeList::load(FXStream& store){
2422 FXScrollArea::load(store);
2423 store >> firstitem;
2424 store >> lastitem;
2425 store >> anchoritem;
2426 store >> currentitem;
2427 store >> extentitem;
2428 store >> font;
2429 store >> textColor;
2430 store >> selbackColor;
2431 store >> seltextColor;
2432 store >> lineColor;
2433 store >> treeWidth;
2434 store >> treeHeight;
2435 store >> visible;
2436 store >> indent;
2437 store >> help;
2438 }
2439
2440
2441 // Cleanup
~FXTreeList()2442 FXTreeList::~FXTreeList(){
2443 getApp()->removeTimeout(this,ID_TIPTIMER);
2444 getApp()->removeTimeout(this,ID_LOOKUPTIMER);
2445 clearItems(FALSE);
2446 firstitem=(FXTreeItem*)-1L;
2447 lastitem=(FXTreeItem*)-1L;
2448 anchoritem=(FXTreeItem*)-1L;
2449 currentitem=(FXTreeItem*)-1L;
2450 extentitem=(FXTreeItem*)-1L;
2451 font=(FXFont*)-1L;
2452 }
2453
2454 }
2455