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