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