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