1 /********************************************************************************
2 *                                                                               *
3 *                          I c o n L i s t   O b j e c 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 "FXAccelTable.h"
42 #include "FXFont.h"
43 #include "FXEvent.h"
44 #include "FXWindow.h"
45 #include "FXDCWindow.h"
46 #include "FXApp.h"
47 #include "FXImage.h"
48 #include "FXIcon.h"
49 #include "FXButton.h"
50 #include "FXScrollBar.h"
51 #include "FXScrollArea.h"
52 #include "FXHeader.h"
53 #include "FXIconList.h"
54 
55 
56 /*
57   To do:
58   - In detail-mode, some items should be left, some right, and some centered in the field.
59   - Return key simulates double click.
60   - Need method to set header columns.
61   - Sortfunc's will be hard to serialize, and hard to write w/o secretly #including
62     the FXTreeItem header!
63   - Rapid key actions are wrongly interpreted as double clicks
64   - Upgrade later to accomodate heterogeneous item sizes (this means a more
65     complex layout algorithm, and likely also explicit x,y recorded in each item).
66   - In all list widgets, get rid of this complex marking business.
67   - Should adding/removing items send SEL_INSERTED and SEL_DELETED callbacks.
68   - Need to add support for arbitrary icon sizes same as FXTreeList already has;
69     layout needs to be such that each column is as wide as widest item in that
70     column only (and not as wide as the widest item in the list).
71   - It may be convenient to have ways to move items around.
72   - Need insertSorted() API to add item in the right place based on current
73     sort function.
74   - Changing icon should NOT cause recalc() when size does not change.
75   - When XDND, autoscrolling happens a bit too fast because we get timers
76     from motion as well as dnd-motion events; probably, it should not
77     autoscroll when dragging icons.
78   - Perhaps drawDetails() should ignore x coordinate and just look at getItemOffest()
79     from the header instead.
80   - Perhaps the ICONLIST_AUTOSIZE mode should be set with a separate API so that
81     the visual stuff changed setListStyle().
82   - Since '\0' is no longer special in FXString, perhaps we can replace the function
83     of '\t' with '\0'.  This would be significantly more efficient.
84 */
85 
86 
87 
88 #define SIDE_SPACING             4    // Left or right spacing between items
89 #define DETAIL_TEXT_SPACING      2    // Spacing between text and icon in detail icon mode
90 #define MINI_TEXT_SPACING        2    // Spacing between text and icon in mini icon mode
91 #define BIG_LINE_SPACING         6    // Line spacing in big icon mode
92 #define BIG_TEXT_SPACING         2    // Spacing between text and icon in big icon mode
93 #define ITEM_SPACE             128    // Default space for item
94 
95 #define SELECT_MASK   (ICONLIST_EXTENDEDSELECT|ICONLIST_SINGLESELECT|ICONLIST_BROWSESELECT|ICONLIST_MULTIPLESELECT)
96 #define ICONLIST_MASK (SELECT_MASK|ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS|ICONLIST_COLUMNS|ICONLIST_AUTOSIZE)
97 
98 using namespace FX;
99 
100 /*******************************************************************************/
101 
102 namespace FX {
103 
104 
105 // Object implementation
106 FXIMPLEMENT(FXIconItem,FXObject,NULL,0)
107 
108 
109 // Draw item
draw(const FXIconList * list,FXDC & dc,FXint x,FXint y,FXint w,FXint h) const110 void FXIconItem::draw(const FXIconList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const {
111   FXuint options=list->getListStyle();
112   if(options&ICONLIST_BIG_ICONS) drawBigIcon(list,dc,x,y,w,h);
113   else if(options&ICONLIST_MINI_ICONS) drawMiniIcon(list,dc,x,y,w,h);
114   else drawDetails(list,dc,x,y,w,h);
115   }
116 
117 
118 // Draw big icon
drawBigIcon(const FXIconList * list,FXDC & dc,FXint x,FXint y,FXint w,FXint h) const119 void FXIconItem::drawBigIcon(const FXIconList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const {
120   FXint iw=0,ih=0,tw=0,th=0,ss=0,len,dw,s,space,xt,yt,xi,yi;
121   FXFont *font=list->getFont();
122   dc.fillRectangle(x,y,w,h);
123   space=w-SIDE_SPACING;
124   if(!label.empty()){
125     for(len=0; len<label.length() && label[len]!='\t'; len++){}
126     tw=4+font->getTextWidth(label.text(),len);
127     th=4+font->getFontHeight();
128     yt=y+h-th-BIG_LINE_SPACING/2;
129     dw=0;
130     if(tw>space){
131       dw=font->getTextWidth("...",3);
132       s=space-dw;
133       while((tw=4+font->getTextWidth(label.text(),len))>s && len>1) len=label.dec(len);
134       if(tw>s) dw=0;
135       }
136     if(tw<=space){         // FIXME as below in drawDetails
137       xt=x+(w-tw-dw)/2;
138       if(isSelected()){
139         dc.setForeground(list->getSelBackColor());
140         dc.fillRectangle(xt,yt,tw+dw,th);
141         }
142       if(!isEnabled())
143         dc.setForeground(makeShadowColor(list->getBackColor()));
144       else if(isSelected())
145         dc.setForeground(list->getSelTextColor());
146       else
147         dc.setForeground(list->getTextColor());
148       dc.drawText(xt+2,yt+font->getFontAscent()+2,label.text(),len);
149       if(dw) dc.drawText(xt+tw-2,yt+font->getFontAscent()+2,"...",3);
150       if(hasFocus()){
151         dc.drawFocusRectangle(xt+1,yt+1,tw+dw-2,th-2);
152         }
153       }
154     ss=BIG_TEXT_SPACING;    // Space between text and icon only added if we have both icon and text
155     }
156   if(bigIcon){
157     iw=bigIcon->getWidth();
158     ih=bigIcon->getHeight();
159     xi=x+(w-iw)/2;
160     yi=y+BIG_LINE_SPACING/2+(h-th-BIG_LINE_SPACING-ss-ih)/2;
161     if(isSelected()){
162       dc.drawIconShaded(bigIcon,xi,yi);
163       }
164     else{
165       dc.drawIcon(bigIcon,xi,yi);
166       }
167     }
168   }
169 
170 
171 // Draw mini icon
drawMiniIcon(const FXIconList * list,FXDC & dc,FXint x,FXint y,FXint w,FXint h) const172 void FXIconItem::drawMiniIcon(const FXIconList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const {
173   FXint iw=0,ih=0,tw=0,th=0,len,dw,s,space;
174   FXFont *font=list->getFont();
175   dc.fillRectangle(x,y,w,h);
176   x+=SIDE_SPACING/2;
177   space=w-SIDE_SPACING;
178   if(miniIcon){
179     iw=miniIcon->getWidth();
180     ih=miniIcon->getHeight();
181     if(isSelected()){
182       dc.drawIconShaded(miniIcon,x,y+(h-ih)/2);
183       }
184     else{
185       dc.drawIcon(miniIcon,x,y+(h-ih)/2);
186       }
187     x+=iw+MINI_TEXT_SPACING;
188     space-=iw+MINI_TEXT_SPACING;
189     }
190   if(!label.empty()){
191     for(len=0; len<label.length() && label[len]!='\t'; len++){}
192     tw=4+font->getTextWidth(label.text(),len);
193     th=4+font->getFontHeight();
194     dw=font->getTextWidth("...",3);
195     y+=(h-th)/2;
196     dw=0;
197     if(tw>space){                  // FIXME as below in drawDetails
198       dw=font->getTextWidth("...",3);
199       s=space-dw;
200       while((tw=4+font->getTextWidth(label.text(),len))>s && len>1) len=label.dec(len);
201       if(tw>s) dw=0;
202       }
203     if(tw<=space){
204       if(isSelected()){
205         dc.setForeground(list->getSelBackColor());
206         dc.fillRectangle(x,y,tw+dw,th);
207         }
208       if(!isEnabled())
209         dc.setForeground(makeShadowColor(list->getBackColor()));
210       else if(isSelected())
211         dc.setForeground(list->getSelTextColor());
212       else
213         dc.setForeground(list->getTextColor());
214       dc.drawText(x+2,y+font->getFontAscent()+2,label.text(),len);
215       if(dw) dc.drawText(x+tw-2,y+font->getFontAscent()+2,"...",3);
216       if(hasFocus()){
217         dc.drawFocusRectangle(x+1,y+1,tw+dw-2,th-2);
218         }
219       }
220     }
221   }
222 
223 
224 // Draw detail
drawDetails(const FXIconList * list,FXDC & dc,FXint x,FXint y,FXint w,FXint h) const225 void FXIconItem::drawDetails(const FXIconList* list,FXDC& dc,FXint x,FXint y,FXint w,FXint h) const {
226   FXint iw=0,ih=0,tw=0,th=0,yt,beg,end,hi,drw,space,used,dw,xx;
227   FXHeader *header=list->getHeader();
228   FXFont *font=list->getFont();
229   if(header->getNumItems()==0) return;
230   if(isSelected()){
231     dc.setForeground(list->getSelBackColor());
232     dc.fillRectangle(x,y,w,h);
233     }
234   else{
235     dc.fillRectangle(x,y,w,h);
236     }
237   if(hasFocus()){
238     dc.drawFocusRectangle(x+1,y+1,w-2,h-2);
239     }
240   xx=x+SIDE_SPACING/2;
241   if(miniIcon){
242     iw=miniIcon->getWidth();
243     ih=miniIcon->getHeight();
244     dc.setClipRectangle(x,y,header->getItemSize(0),h);
245     dc.drawIcon(miniIcon,xx,y+(h-ih)/2);
246     dc.clearClipRectangle();
247     xx+=iw+DETAIL_TEXT_SPACING;
248     }
249   if(!label.empty()){
250     th=font->getFontHeight();
251     dw=font->getTextWidth("...",3);
252     yt=y+(h-th-4)/2;
253     if(!isEnabled())
254       dc.setForeground(makeShadowColor(list->getBackColor()));
255     else if(isSelected())
256       dc.setForeground(list->getSelTextColor());
257     else
258       dc.setForeground(list->getTextColor());
259     used=iw+DETAIL_TEXT_SPACING+SIDE_SPACING/2;
260     for(hi=beg=0; beg<label.length() && hi<header->getNumItems(); hi++,beg=end+1){
261       space=header->getItemSize(hi)-used;
262       for(end=beg; end<label.length() && label[end]!='\t'; end++){}
263       if(end>beg){
264         drw=end-beg;
265         tw=font->getTextWidth(&label[beg],drw);
266         if(tw>space-4){
267           while((tw=font->getTextWidth(&label[beg],drw))+dw>space-4 && drw>1) drw=label.dec(drw);
268           dc.setClipRectangle(xx,y,space,h);
269           dc.drawText(xx+2,yt+font->getFontAscent()+2,&label[beg],drw);
270           dc.drawText(xx+tw+2,yt+font->getFontAscent()+2,"...",3);
271           dc.clearClipRectangle();
272           }
273         else{
274           dc.drawText(xx+2,yt+font->getFontAscent()+2,&label[beg],drw);
275           }
276         }
277       xx+=space;
278       used=0;
279       }
280     }
281   }
282 
283 
284 // See if item got hit and where: 0 is outside, 1 is icon, 2 is text
hitItem(const FXIconList * list,FXint rx,FXint ry,FXint rw,FXint rh) const285 FXint FXIconItem::hitItem(const FXIconList* list,FXint rx,FXint ry,FXint rw,FXint rh) const {
286   FXFont *font=list->getFont();
287   FXuint options=list->getListStyle();
288   FXint iw=0,tw=0,ih=0,th=0,ss=0,ix,iy,tx,ty,w,h,sp,tlen;
289   for(tlen=0; tlen<label.length() && label[tlen]!='\t'; tlen++){}
290   if(options&ICONLIST_BIG_ICONS){
291     w=list->getItemWidth();
292     h=list->getItemHeight();
293     sp=w-SIDE_SPACING;
294     if(!label.empty()){
295       tw=4+font->getTextWidth(label.text(),tlen);
296       th=4+font->getFontHeight();
297       if(tw>sp) tw=sp;
298       if(bigIcon) ss=BIG_TEXT_SPACING;
299       }
300     if(bigIcon){
301       iw=bigIcon->getWidth();
302       ih=bigIcon->getHeight();
303       }
304     ty=h-th-BIG_LINE_SPACING/2;
305     iy=BIG_LINE_SPACING/2+(h-th-BIG_LINE_SPACING-ss-ih)/2;
306     ix=(w-iw)/2;
307     tx=(w-tw)/2;
308     }
309   else if(options&ICONLIST_MINI_ICONS){
310     sp=list->getItemWidth()-SIDE_SPACING;
311     ix=SIDE_SPACING/2;
312     tx=SIDE_SPACING/2;
313     if(miniIcon){
314       iw=miniIcon->getWidth();
315       ih=miniIcon->getHeight();
316       tx+=iw+MINI_TEXT_SPACING;
317       sp=sp-iw-MINI_TEXT_SPACING;
318       }
319     if(!label.empty()){
320       tw=4+font->getTextWidth(label.text(),tlen);
321       th=4+font->getFontHeight();
322       if(tw>sp) tw=sp;
323       }
324     h=list->getItemHeight();
325     iy=(h-ih)/2;
326     ty=(h-th)/2;
327     }
328   else{
329     ix=SIDE_SPACING/2;
330     tx=SIDE_SPACING/2;
331     if(miniIcon){
332       iw=miniIcon->getWidth();
333       ih=miniIcon->getHeight();
334       tx+=iw+DETAIL_TEXT_SPACING;
335       }
336     if(!label.empty()){
337       tw=10000000;
338       th=4+font->getFontHeight();
339       }
340     h=list->getItemHeight();
341     iy=(h-ih)/2;
342     ty=(h-th)/2;
343     }
344 
345   // In icon?
346   if(ix<=rx+rw && iy<=ry+rh && rx<ix+iw && ry<iy+ih) return 1;
347 
348   // In text?
349   if(tx<=rx+rw && ty<=ry+rh && rx<tx+tw && ry<ty+th) return 2;
350 
351   // Outside
352   return 0;
353   }
354 
355 
356 // Set or kill focus
setFocus(FXbool focus)357 void FXIconItem::setFocus(FXbool focus){
358   state^=((0-focus)^state)&FOCUS;
359   }
360 
361 // Select or deselect item
setSelected(FXbool selected)362 void FXIconItem::setSelected(FXbool selected){
363   state^=((0-selected)^state)&SELECTED;
364   }
365 
366 // Enable or disable the item
setEnabled(FXbool enabled)367 void FXIconItem::setEnabled(FXbool enabled){
368   state^=((enabled-1)^state)&DISABLED;
369   }
370 
371 // Icon is draggable
setDraggable(FXbool draggable)372 void FXIconItem::setDraggable(FXbool draggable){
373   state^=((0-draggable)^state)&DRAGGABLE;
374   }
375 
376 
377 // Change item's text label
setText(const FXString & txt)378 void FXIconItem::setText(const FXString& txt){
379   label=txt;
380   }
381 
382 
383 // Change item's big icon
setBigIcon(FXIcon * icn,FXbool owned)384 void FXIconItem::setBigIcon(FXIcon* icn,FXbool owned){
385   if(bigIcon && (state&BIGICONOWNED)){
386     if(bigIcon!=icn) delete bigIcon;
387     state&=~BIGICONOWNED;
388     }
389   bigIcon=icn;
390   if(bigIcon && owned){
391     state|=BIGICONOWNED;
392     }
393   }
394 
395 
396 // Change item's mini icon
setMiniIcon(FXIcon * icn,FXbool owned)397 void FXIconItem::setMiniIcon(FXIcon* icn,FXbool owned){
398   if(miniIcon && (state&MINIICONOWNED)){
399     if(miniIcon!=icn) delete miniIcon;
400     state&=~MINIICONOWNED;
401     }
402   miniIcon=icn;
403   if(miniIcon && owned){
404     state|=MINIICONOWNED;
405     }
406   }
407 
408 
409 // Create icon
create()410 void FXIconItem::create(){
411   if(bigIcon) bigIcon->create();
412   if(miniIcon) miniIcon->create();
413   }
414 
415 
416 // Destroy icon
destroy()417 void FXIconItem::destroy(){
418   if((state&BIGICONOWNED) && bigIcon) bigIcon->destroy();
419   if((state&MINIICONOWNED) && miniIcon) miniIcon->destroy();
420   }
421 
422 
423 // Detach from icon resource
detach()424 void FXIconItem::detach(){
425   if(bigIcon) bigIcon->detach();
426   if(miniIcon) miniIcon->detach();
427   }
428 
429 
430 // Return tip text
getTipText() const431 FXString FXIconItem::getTipText() const {
432   return label.section('\t',0);
433   }
434 
435 
436 // Get item width
getWidth(const FXIconList * list) const437 FXint FXIconItem::getWidth(const FXIconList* list) const {
438   FXFont *font=list->getFont();
439   FXuint options=list->getListStyle();
440   FXint iw=0,tw=0,w=0,tlen;
441   for(tlen=0; tlen<label.length() && label[tlen]!='\t'; tlen++){}
442   if(options&ICONLIST_BIG_ICONS){
443     if(bigIcon) iw=bigIcon->getWidth();
444     if(!label.empty()) tw=4+font->getTextWidth(label.text(),tlen);
445     w=SIDE_SPACING+FXMAX(tw,iw);
446     }
447   else if(options&ICONLIST_MINI_ICONS){
448     if(miniIcon) iw=miniIcon->getWidth();
449     if(!label.empty()) tw=4+font->getTextWidth(label.text(),tlen);
450     if(iw && tw) iw+=MINI_TEXT_SPACING;
451     w=SIDE_SPACING+iw+tw;
452     }
453   else{
454     w=SIDE_SPACING;
455     }
456   return w;
457   }
458 
459 
460 // Get item height
getHeight(const FXIconList * list) const461 FXint FXIconItem::getHeight(const FXIconList* list) const {
462   FXFont *font=list->getFont();
463   FXuint options=list->getListStyle();
464   FXint ih=0,th=0,h=0;
465   if(options&ICONLIST_BIG_ICONS){
466     if(bigIcon) ih=bigIcon->getHeight();
467     if(!label.empty()) th=4+font->getFontHeight();
468     if(ih && th) ih+=BIG_TEXT_SPACING;
469     h=BIG_LINE_SPACING+ih+th;
470     }
471   else if(options&ICONLIST_MINI_ICONS){
472     if(miniIcon) ih=miniIcon->getHeight();
473     if(!label.empty()) th=4+font->getFontHeight();
474     h=FXMAX(ih,th);
475     }
476   else{
477     if(miniIcon) ih=miniIcon->getHeight();
478     if(!label.empty()) th=4+font->getFontHeight();
479     h=FXMAX(ih,th);
480     }
481   return h;
482   }
483 
484 
485 // Save data
save(FXStream & store) const486 void FXIconItem::save(FXStream& store) const {
487   FXObject::save(store);
488   store << label;
489   store << bigIcon;
490   store << miniIcon;
491   store << state;
492   }
493 
494 
495 // Load data
load(FXStream & store)496 void FXIconItem::load(FXStream& store){
497   FXObject::load(store);
498   store >> label;
499   store >> bigIcon;
500   store >> miniIcon;
501   store >> state;
502   }
503 
504 
505 // Delete icons if owned
~FXIconItem()506 FXIconItem::~FXIconItem(){
507   if(state&BIGICONOWNED) delete bigIcon;
508   if(state&MINIICONOWNED) delete miniIcon;
509   bigIcon=(FXIcon*)-1L;
510   miniIcon=(FXIcon*)-1L;
511   }
512 
513 /*******************************************************************************/
514 
515 // Map
516 FXDEFMAP(FXIconList) FXIconListMap[]={
517   FXMAPFUNC(SEL_PAINT,0,FXIconList::onPaint),
518   FXMAPFUNC(SEL_MOTION,0,FXIconList::onMotion),
519   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXIconList::onLeftBtnPress),
520   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXIconList::onLeftBtnRelease),
521   FXMAPFUNC(SEL_RIGHTBUTTONPRESS,0,FXIconList::onRightBtnPress),
522   FXMAPFUNC(SEL_RIGHTBUTTONRELEASE,0,FXIconList::onRightBtnRelease),
523   FXMAPFUNC(SEL_TIMEOUT,FXIconList::ID_AUTOSCROLL,FXIconList::onAutoScroll),
524   FXMAPFUNC(SEL_TIMEOUT,FXIconList::ID_TIPTIMER,FXIconList::onTipTimer),
525   FXMAPFUNC(SEL_TIMEOUT,FXIconList::ID_LOOKUPTIMER,FXIconList::onLookupTimer),
526   FXMAPFUNC(SEL_UNGRABBED,0,FXIconList::onUngrabbed),
527   FXMAPFUNC(SEL_KEYPRESS,0,FXIconList::onKeyPress),
528   FXMAPFUNC(SEL_KEYRELEASE,0,FXIconList::onKeyRelease),
529   FXMAPFUNC(SEL_ENTER,0,FXIconList::onEnter),
530   FXMAPFUNC(SEL_LEAVE,0,FXIconList::onLeave),
531   FXMAPFUNC(SEL_FOCUSIN,0,FXIconList::onFocusIn),
532   FXMAPFUNC(SEL_FOCUSOUT,0,FXIconList::onFocusOut),
533   FXMAPFUNC(SEL_CLICKED,0,FXIconList::onClicked),
534   FXMAPFUNC(SEL_DOUBLECLICKED,0,FXIconList::onDoubleClicked),
535   FXMAPFUNC(SEL_TRIPLECLICKED,0,FXIconList::onTripleClicked),
536   FXMAPFUNC(SEL_COMMAND,0,FXIconList::onCommand),
537   FXMAPFUNC(SEL_QUERY_TIP,0,FXIconList::onQueryTip),
538   FXMAPFUNC(SEL_QUERY_HELP,0,FXIconList::onQueryHelp),
539   FXMAPFUNC(SEL_CHANGED,FXIconList::ID_HEADER,FXIconList::onChgHeader),
540   FXMAPFUNC(SEL_CLICKED,FXIconList::ID_HEADER,FXIconList::onClkHeader),
541   FXMAPFUNC(SEL_UPDATE,FXIconList::ID_SHOW_DETAILS,FXIconList::onUpdShowDetails),
542   FXMAPFUNC(SEL_UPDATE,FXIconList::ID_SHOW_MINI_ICONS,FXIconList::onUpdShowMiniIcons),
543   FXMAPFUNC(SEL_UPDATE,FXIconList::ID_SHOW_BIG_ICONS,FXIconList::onUpdShowBigIcons),
544   FXMAPFUNC(SEL_UPDATE,FXIconList::ID_ARRANGE_BY_ROWS,FXIconList::onUpdArrangeByRows),
545   FXMAPFUNC(SEL_UPDATE,FXIconList::ID_ARRANGE_BY_COLUMNS,FXIconList::onUpdArrangeByColumns),
546   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_SHOW_DETAILS,FXIconList::onCmdShowDetails),
547   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_SHOW_MINI_ICONS,FXIconList::onCmdShowMiniIcons),
548   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_SHOW_BIG_ICONS,FXIconList::onCmdShowBigIcons),
549   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_ARRANGE_BY_ROWS,FXIconList::onCmdArrangeByRows),
550   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_ARRANGE_BY_COLUMNS,FXIconList::onCmdArrangeByColumns),
551   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_SELECT_ALL,FXIconList::onCmdSelectAll),
552   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_DESELECT_ALL,FXIconList::onCmdDeselectAll),
553   FXMAPFUNC(SEL_COMMAND,FXIconList::ID_SELECT_INVERSE,FXIconList::onCmdSelectInverse),
554   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETVALUE,FXIconList::onCmdSetValue),
555   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_SETINTVALUE,FXIconList::onCmdSetIntValue),
556   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_GETINTVALUE,FXIconList::onCmdGetIntValue),
557   };
558 
559 
560 // Object implementation
FXIMPLEMENT(FXIconList,FXScrollArea,FXIconListMap,ARRAYNUMBER (FXIconListMap))561 FXIMPLEMENT(FXIconList,FXScrollArea,FXIconListMap,ARRAYNUMBER(FXIconListMap))
562 
563 
564 /*******************************************************************************/
565 
566 
567 // Serialization
568 FXIconList::FXIconList(){
569   flags|=FLAG_ENABLED;
570   header=(FXHeader*)-1L;
571   nrows=1;
572   ncols=1;
573   anchor=-1;
574   current=-1;
575   extent=-1;
576   viewable=-1;
577   font=(FXFont*)-1L;
578   sortfunc=NULL;
579   textColor=0;
580   selbackColor=0;
581   seltextColor=0;
582   itemSpace=ITEM_SPACE;
583   itemWidth=1;
584   itemHeight=1;
585   anchorx=0;
586   anchory=0;
587   currentx=0;
588   currenty=0;
589   grabx=0;
590   graby=0;
591   state=false;
592   }
593 
594 
595 // Icon List
FXIconList(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h)596 FXIconList::FXIconList(FXComposite *p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXScrollArea(p,opts,x,y,w,h){
597   flags|=FLAG_ENABLED;
598   header=new FXHeader(this,this,FXIconList::ID_HEADER,HEADER_TRACKING|HEADER_BUTTON|HEADER_RESIZE|FRAME_RAISED|FRAME_THICK);
599   target=tgt;
600   message=sel;
601   nrows=1;
602   ncols=1;
603   anchor=-1;
604   current=-1;
605   extent=-1;
606   viewable=-1;
607   font=getApp()->getNormalFont();
608   sortfunc=NULL;
609   textColor=getApp()->getForeColor();
610   selbackColor=getApp()->getSelbackColor();
611   seltextColor=getApp()->getSelforeColor();
612   itemSpace=ITEM_SPACE;
613   itemWidth=1;
614   itemHeight=1;
615   anchorx=0;
616   anchory=0;
617   currentx=0;
618   currenty=0;
619   grabx=0;
620   graby=0;
621   state=false;
622   }
623 
624 
625 // Create window
create()626 void FXIconList::create(){
627   FXint i;
628   FXScrollArea::create();
629   for(i=0; i<items.no(); i++){items[i]->create();}
630   font->create();
631   }
632 
633 
634 // Detach window
detach()635 void FXIconList::detach(){
636   FXint i;
637   FXScrollArea::detach();
638   for(i=0; i<items.no(); i++){items[i]->detach();}
639   font->detach();
640   }
641 
642 
643 // If window can have focus
canFocus() const644 FXbool FXIconList::canFocus() const { return true; }
645 
646 
647 // Into focus chain
setFocus()648 void FXIconList::setFocus(){
649   FXScrollArea::setFocus();
650   setDefault(true);
651   }
652 
653 
654 // Out of focus chain
killFocus()655 void FXIconList::killFocus(){
656   FXScrollArea::killFocus();
657   setDefault(maybe);
658   }
659 
660 
661 // Propagate size change
recalc()662 void FXIconList::recalc(){
663   FXScrollArea::recalc();
664   flags|=FLAG_RECALC;
665   }
666 
667 
668 // Return visible area y position
getVisibleY() const669 FXint FXIconList::getVisibleY() const {
670   return (options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS)) ? 0 : header->getHeight();
671   }
672 
673 
674 // Return visible area height
getVisibleHeight() const675 FXint FXIconList::getVisibleHeight() const {
676   return (options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS)) ? height-horizontal->getHeight() : height-header->getHeight()-horizontal->getHeight();
677   }
678 
679 
680 // Move content
moveContents(FXint x,FXint y)681 void FXIconList::moveContents(FXint x,FXint y){
682   FXScrollArea::moveContents(x,y);
683   if(!(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS))){
684     header->setPosition(x);
685     }
686   }
687 
688 
689 // Determine number of columns and number of rows
getrowscols(FXint & nr,FXint & nc,FXint w,FXint h) const690 void FXIconList::getrowscols(FXint& nr,FXint& nc,FXint w,FXint h) const {
691   if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
692     if(options&ICONLIST_COLUMNS){
693       nc=w/itemWidth;
694       if(nc<1) nc=1;
695       nr=(items.no()+nc-1)/nc;
696       if(nr*itemHeight > h){
697         nc=(w-vertical->getDefaultWidth())/itemWidth;
698         if(nc<1) nc=1;
699         nr=(items.no()+nc-1)/nc;
700         }
701       if(nr<1) nr=1;
702       }
703     else{
704       nr=h/itemHeight;
705       if(nr<1) nr=1;
706       nc=(items.no()+nr-1)/nr;
707       if(nc*itemWidth > w){
708         nr=(h-horizontal->getDefaultHeight())/itemHeight;
709         if(nr<1) nr=1;
710         nc=(items.no()+nr-1)/nr;
711         }
712       if(nc<1) nc=1;
713       }
714     }
715   else{
716     nr=items.no();
717     nc=1;
718     }
719   }
720 
721 
722 // Recompute interior
recompute()723 void FXIconList::recompute(){
724   FXint w,h,i;
725 
726   itemWidth=1;
727   itemHeight=1;
728 
729   // Measure the items
730   for(i=0; i<items.no(); i++){
731     w=items[i]->getWidth(this);
732     h=items[i]->getHeight(this);
733     if(w>itemWidth) itemWidth=w;
734     if(h>itemHeight) itemHeight=h;
735     }
736 
737   // Normally, apply fixed item size unless autosize is on
738   if(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS)){
739     if(!(options&ICONLIST_AUTOSIZE)) itemWidth=itemSpace;
740     }
741 
742   // In detail mode, item width depends only on header
743   else{
744     itemWidth=header->getTotalSize();
745     }
746 
747   // Get number of rows or columns
748   getrowscols(nrows,ncols,width,height);
749 
750   // Done
751   flags&=~FLAG_RECALC;
752   }
753 
754 
755 // Determine content width of icon list
getContentWidth()756 FXint FXIconList::getContentWidth(){
757   if(flags&FLAG_RECALC) recompute();
758   return ncols*itemWidth;
759   }
760 
761 
762 // Determine content height of icon list
getContentHeight()763 FXint FXIconList::getContentHeight(){
764   if(flags&FLAG_RECALC) recompute();
765   return nrows*itemHeight;
766   }
767 
768 
769 // Recalculate layout
layout()770 void FXIconList::layout(){
771   FXint hh=(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS)) ? 0 : header->getDefaultHeight();
772 
773   // Place scroll bars
774   placeScrollBars(width,height-hh);
775 
776   // Place header control
777   header->position(0,0,width,hh);
778 
779   // Set line size
780   vertical->setLine(itemHeight);
781   horizontal->setLine(itemWidth);
782 
783   // We were supposed to make this item viewable
784   if(0<=viewable){
785     makeItemVisible(viewable);
786     }
787 
788   // Force repaint
789   update();
790 
791   // Clean
792   flags&=~FLAG_DIRTY;
793   }
794 
795 
796 // Changed size:- this is a bit tricky, because
797 // we don't want to re-measure the items, but the content
798 // size has changed because the number of rows/columns has...
resize(FXint w,FXint h)799 void FXIconList::resize(FXint w,FXint h){
800   FXint nr=nrows;
801   FXint nc=ncols;
802   if(w!=width || h!=height){
803     getrowscols(nrows,ncols,w,h);
804     if(nr!=nrows || nc!=ncols) update();
805     }
806   FXScrollArea::resize(w,h);
807   }
808 
809 
810 // Changed size and/or pos:- this is a bit tricky, because
811 // we don't want to re-measure the items, but the content
812 // size has changed because the number of rows/columns has...
position(FXint x,FXint y,FXint w,FXint h)813 void FXIconList::position(FXint x,FXint y,FXint w,FXint h){
814   FXint nr=nrows;
815   FXint nc=ncols;
816   if(w!=width || h!=height){
817     getrowscols(nrows,ncols,w,h);
818     if(nr!=nrows || nc!=ncols) update();
819     }
820   FXScrollArea::position(x,y,w,h);
821   }
822 
823 
824 // Header changed but content size didn't
onChgHeader(FXObject *,FXSelector,void *)825 long FXIconList::onChgHeader(FXObject*,FXSelector,void*){
826   return 1;
827   }
828 
829 
830 // Header subdivision resize has been requested;
831 // we want to set the width of the header column
832 // to that of the widest item.
onClkHeader(FXObject *,FXSelector,void * ptr)833 long FXIconList::onClkHeader(FXObject*,FXSelector,void* ptr){
834   FXint hi=(FXint)(FXival)ptr;
835   FXint i,iw,tw,w,nw=0;
836   FXString text;
837 
838   // For detailed icon list
839   if(!(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS))){
840     for(i=0; i<items.no(); i++){
841       w=0;
842 
843       // The first header item may have an icon
844       if(hi==0){
845         if(items[i]->miniIcon){
846           iw=items[i]->miniIcon->getWidth();
847           w+=iw+DETAIL_TEXT_SPACING+SIDE_SPACING/2;
848           }
849         }
850 
851       // Measure section of text
852       text=items[i]->label.section('\t',hi);
853       if(!text.empty()){
854         tw=font->getTextWidth(text.text(),text.length());
855         w+=tw+SIDE_SPACING+2;
856         }
857 
858       // Keep the max
859       if(w>nw) nw=w;
860       }
861 
862     // Set new header width
863     if(nw>0 && nw!=header->getItemSize(hi)){
864       header->setItemSize(hi,nw);
865       flags&=~FLAG_RECALC;
866       }
867     }
868   return 1;
869   }
870 
871 
872 // Set headers from array of strings
setHeaders(const FXchar ** strings,FXint size)873 void FXIconList::setHeaders(const FXchar** strings,FXint size){
874   header->clearItems();
875   header->fillItems(strings,NULL,size);
876   }
877 
878 
879 // Set headers from newline separated strings
setHeaders(const FXString & strings,FXint size)880 void FXIconList::setHeaders(const FXString& strings,FXint size){
881   header->clearItems();
882   header->fillItems(strings,NULL,size);
883   }
884 
885 
886 // Append header caption
appendHeader(const FXString & text,FXIcon * icon,FXint size)887 void FXIconList::appendHeader(const FXString& text,FXIcon *icon,FXint size){
888   header->appendItem(text,icon,size);
889   }
890 
891 
892 // Remove header caption
removeHeader(FXint index)893 void FXIconList::removeHeader(FXint index){
894   if(index<0 || header->getNumItems()<=index){ fxerror("%s::removeHeader: index out of range.\n",getClassName()); }
895   header->removeItem(index);
896   }
897 
898 
899 // Change header caption
setHeaderText(FXint index,const FXString & text)900 void FXIconList::setHeaderText(FXint index,const FXString& text){
901   if(index<0 || header->getNumItems()<=index){ fxerror("%s::setHeaderText: index out of range.\n",getClassName()); }
902   header->setItemText(index,text);
903   }
904 
905 
906 // Get header caption
getHeaderText(FXint index) const907 FXString FXIconList::getHeaderText(FXint index) const {
908   if(index<0 || header->getNumItems()<=index){ fxerror("%s::getHeaderText: index out of range.\n",getClassName()); }
909   return header->getItemText(index);
910   }
911 
912 
913 // Change header icon
setHeaderIcon(FXint index,FXIcon * icon)914 void FXIconList::setHeaderIcon(FXint index,FXIcon *icon){
915   if(index<0 || header->getNumItems()<=index){ fxerror("%s::setHeaderIcon: index out of range.\n",getClassName()); }
916   header->setItemIcon(index,icon);
917   }
918 
919 
920 // Get header icon
getHeaderIcon(FXint index) const921 FXIcon* FXIconList::getHeaderIcon(FXint index) const {
922   if(index<0 || header->getNumItems()<=index){ fxerror("%s::getHeaderIcon: index out of range.\n",getClassName()); }
923   return header->getItemIcon(index);
924   }
925 
926 
927 // Change header size
setHeaderSize(FXint index,FXint size)928 void FXIconList::setHeaderSize(FXint index,FXint size){
929   if(index<0 || header->getNumItems()<=index){ fxerror("%s::setHeaderSize: index out of range.\n",getClassName()); }
930   header->setItemSize(index,size);
931   }
932 
933 
934 // Get header size
getHeaderSize(FXint index) const935 FXint FXIconList::getHeaderSize(FXint index) const {
936   if(index<0 || header->getNumItems()<=index){ fxerror("%s::getHeaderSize: index out of range.\n",getClassName()); }
937   return header->getItemSize(index);
938   }
939 
940 
941 // Return number of headers
getNumHeaders() const942 FXint FXIconList::getNumHeaders() const {
943   return header->getNumItems();
944   }
945 
946 
947 // Change item text
setItemText(FXint index,const FXString & text)948 void FXIconList::setItemText(FXint index,const FXString& text){
949   if(index<0 || items.no()<=index){ fxerror("%s::setItemText: index out of range.\n",getClassName()); }
950   if(items[index]->getText()!=text){
951     items[index]->setText(text);
952     recalc();
953     }
954   }
955 
956 
957 // Get item text
getItemText(FXint index) const958 FXString FXIconList::getItemText(FXint index) const {
959   if(index<0 || items.no()<=index){ fxerror("%s::getItemText: index out of range.\n",getClassName()); }
960   return items[index]->getText();
961   }
962 
963 
964 // Set item icon
setItemBigIcon(FXint index,FXIcon * icon,FXbool owned)965 void FXIconList::setItemBigIcon(FXint index,FXIcon* icon,FXbool owned){
966   if(index<0 || items.no()<=index){ fxerror("%s::setItemBigIcon: index out of range.\n",getClassName()); }
967   if(items[index]->getBigIcon()!=icon) recalc();
968   items[index]->setBigIcon(icon,owned);
969   }
970 
971 
972 // Get item icon
getItemBigIcon(FXint index) const973 FXIcon* FXIconList::getItemBigIcon(FXint index) const {
974   if(index<0 || items.no()<=index){ fxerror("%s::getItemBigIcon: index out of range.\n",getClassName()); }
975   return items[index]->getBigIcon();
976   }
977 
978 
979 // Set item icon
setItemMiniIcon(FXint index,FXIcon * icon,FXbool owned)980 void FXIconList::setItemMiniIcon(FXint index,FXIcon* icon,FXbool owned){
981   if(index<0 || items.no()<=index){ fxerror("%s::setItemMiniIcon: index out of range.\n",getClassName()); }
982   if(items[index]->getMiniIcon()!=icon) recalc();
983   items[index]->setMiniIcon(icon,owned);
984   }
985 
986 
987 // Get item icon
getItemMiniIcon(FXint index) const988 FXIcon* FXIconList::getItemMiniIcon(FXint index) const {
989   if(index<0 || items.no()<=index){ fxerror("%s::getItemMiniIcon: index out of range.\n",getClassName()); }
990   return items[index]->getMiniIcon();
991   }
992 
993 
994 // Set item data
setItemData(FXint index,FXptr ptr)995 void FXIconList::setItemData(FXint index,FXptr ptr){
996   if(index<0 || items.no()<=index){ fxerror("%s::setItemData: index out of range.\n",getClassName()); }
997   items[index]->setData(ptr);
998   }
999 
1000 
1001 // Get item data
getItemData(FXint index) const1002 FXptr FXIconList::getItemData(FXint index) const {
1003   if(index<0 || items.no()<=index){ fxerror("%s::getItemData: index out of range.\n",getClassName()); }
1004   return items[index]->getData();
1005   }
1006 
1007 
1008 // True if item is selected
isItemSelected(FXint index) const1009 FXbool FXIconList::isItemSelected(FXint index) const {
1010   if(index<0 || items.no()<=index){ fxerror("%s::isItemSelected: index out of range.\n",getClassName()); }
1011   return items[index]->isSelected();
1012   }
1013 
1014 
1015 // True if item is current
isItemCurrent(FXint index) const1016 FXbool FXIconList::isItemCurrent(FXint index) const {
1017   if(index<0 || items.no()<=index){ fxerror("%s::isItemCurrent: index out of range.\n",getClassName()); }
1018   return index==current;
1019   }
1020 
1021 
1022 // True if item is enabled
isItemEnabled(FXint index) const1023 FXbool FXIconList::isItemEnabled(FXint index) const {
1024   if(index<0 || items.no()<=index){ fxerror("%s::isItemEnabled: index out of range.\n",getClassName()); }
1025   return items[index]->isEnabled();
1026   }
1027 
1028 
1029 // True if item (partially) visible
isItemVisible(FXint index) const1030 FXbool FXIconList::isItemVisible(FXint index) const {
1031   FXbool vis=false;
1032   FXint x,y,hh;
1033   if(index<0 || items.no()<=index){ fxerror("%s::isItemVisible: index out of range.\n",getClassName()); }
1034   if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1035     if(options&ICONLIST_COLUMNS){
1036       FXASSERT(ncols>0);
1037       x=pos_x+itemWidth*(index%ncols);
1038       y=pos_y+itemHeight*(index/ncols);
1039       }
1040     else{
1041       FXASSERT(nrows>0);
1042       x=pos_x+itemWidth*(index/nrows);
1043       y=pos_y+itemHeight*(index%nrows);
1044       }
1045     if(0<x+itemWidth && x<getVisibleWidth() && 0<y+itemHeight && y<getVisibleHeight()) vis=true;
1046     }
1047   else{
1048     hh=header->getDefaultHeight();
1049     y=pos_y+hh+index*itemHeight;
1050     if(hh<y+itemHeight && y<getVisibleHeight()) vis=true;
1051     }
1052   return vis;
1053   }
1054 
1055 
1056 // Make item fully visible
makeItemVisible(FXint index)1057 void FXIconList::makeItemVisible(FXint index){
1058   if(0<=index && index<items.no()){
1059 
1060     // Remember for later
1061     viewable=index;
1062 
1063     // Was realized
1064     if(xid){
1065       FXint x,y,hh,px,py,vw,vh;
1066 
1067       // Force layout if dirty
1068       if(flags&FLAG_RECALC) layout();
1069 
1070       px=pos_x;
1071       py=pos_y;
1072 
1073       vw=getVisibleWidth();
1074       vh=getVisibleHeight();
1075 
1076       // Showing icon view
1077       if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1078         if(options&ICONLIST_COLUMNS){
1079           FXASSERT(ncols>0);
1080           x=itemWidth*(index%ncols);
1081           y=itemHeight*(index/ncols);
1082           }
1083         else{
1084           FXASSERT(nrows>0);
1085           x=itemWidth*(index/nrows);
1086           y=itemHeight*(index%nrows);
1087           }
1088         if(px+x+itemWidth >= vw) px=vw-x-itemWidth;
1089         if(px+x <= 0) px=-x;
1090         if(py+y+itemHeight >= vh) py=vh-y-itemHeight;
1091         if(py+y <= 0) py=-y;
1092         }
1093 
1094       // Showing list view
1095       else{
1096         hh=header->getDefaultHeight();
1097         y=hh+index*itemHeight;
1098         if(py+y+itemHeight >= vh+hh) py=hh+vh-y-itemHeight;
1099         if(py+y <= hh) py=hh-y;
1100         }
1101 
1102       // Scroll into view
1103       setPosition(px,py);
1104 
1105       // Done it
1106       viewable=-1;
1107       }
1108     }
1109   }
1110 
1111 
1112 // Get item at position x,y
getItemAt(FXint x,FXint y) const1113 FXint FXIconList::getItemAt(FXint x,FXint y) const {
1114   FXint ix,iy;
1115   FXint r,c,index;
1116   y-=pos_y;
1117   x-=pos_x;
1118   if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1119     c=x/itemWidth;
1120     r=y/itemHeight;
1121     if(c<0 || c>=ncols || r<0 || r>=nrows) return -1;
1122     index=(options&ICONLIST_COLUMNS) ? ncols*r+c : nrows*c+r;
1123     if(index<0 || index>=items.no()) return -1;
1124     ix=itemWidth*c;
1125     iy=itemHeight*r;
1126     if(items[index]->hitItem(this,x-ix,y-iy)==0) return -1;
1127     }
1128   else{
1129     y-=header->getDefaultHeight();
1130     c=0;
1131     index=y/itemHeight;
1132     if(index<0 || index>=items.no()) return -1;
1133     }
1134   return index;
1135   }
1136 
1137 
1138 // Compare strings up to n
comp(const FXString & s1,const FXString & s2,FXint n)1139 static FXint comp(const FXString& s1,const FXString& s2,FXint n){
1140   const FXuchar *p1=(const FXuchar *)s1.text();
1141   const FXuchar *p2=(const FXuchar *)s2.text();
1142   FXint c1,c2;
1143   if(0<n){
1144     do{
1145       c1=*p1++; if(c1=='\t') c1=0;
1146       c2=*p2++; if(c2=='\t') c2=0;
1147       }
1148     while(--n && c1 && (c1==c2));
1149     return c1-c2;
1150     }
1151   return 0;
1152   }
1153 
1154 
1155 // Compare strings case insensitive up to n
compcase(const FXString & s1,const FXString & s2,FXint n)1156 static FXint compcase(const FXString& s1,const FXString& s2,FXint n){
1157   const FXuchar *p1=(const FXuchar *)s1.text();
1158   const FXuchar *p2=(const FXuchar *)s2.text();
1159   FXint c1,c2;
1160   if(0<n){
1161     do{
1162       c1=Ascii::toLower(*p1++); if(c1=='\t') c1=0;      // FIXME UTF8 version
1163       c2=Ascii::toLower(*p2++); if(c2=='\t') c2=0;
1164       }
1165     while(--n && c1 && (c1==c2));
1166     return c1-c2;
1167     }
1168   return 0;
1169   }
1170 
1171 
1172 typedef FXint (*FXCompareFunc)(const FXString&,const FXString&,FXint);
1173 
1174 
1175 // Get item by name
findItem(const FXString & text,FXint start,FXuint flgs) const1176 FXint FXIconList::findItem(const FXString& text,FXint start,FXuint flgs) const {
1177   FXCompareFunc comparefunc=(flgs&SEARCH_IGNORECASE) ? (FXCompareFunc)compcase : (FXCompareFunc)comp;
1178   FXint index,len;
1179   if(0<items.no()){
1180     len=(flgs&SEARCH_PREFIX)?text.length():2147483647;
1181     if(flgs&SEARCH_BACKWARD){
1182       if(start<0) start=items.no()-1;
1183       for(index=start; 0<=index; index--){
1184         if((*comparefunc)(items[index]->getText(),text,len)==0) return index;
1185         }
1186       if(!(flgs&SEARCH_WRAP)) return -1;
1187       for(index=items.no()-1; start<index; index--){
1188         if((*comparefunc)(items[index]->getText(),text,len)==0) return index;
1189         }
1190       }
1191     else{
1192       if(start<0) start=0;
1193       for(index=start; index<items.no(); index++){
1194         if((*comparefunc)(items[index]->getText(),text,len)==0) return index;
1195         }
1196       if(!(flgs&SEARCH_WRAP)) return -1;
1197       for(index=0; index<start; index++){
1198         if((*comparefunc)(items[index]->getText(),text,len)==0) return index;
1199         }
1200       }
1201     }
1202   return -1;
1203   }
1204 
1205 
1206 // Get item by data
findItemByData(FXptr ptr,FXint start,FXuint flgs) const1207 FXint FXIconList::findItemByData(FXptr ptr,FXint start,FXuint flgs) const {
1208   FXint index;
1209   if(0<items.no()){
1210     if(flgs&SEARCH_BACKWARD){
1211       if(start<0) start=items.no()-1;
1212       for(index=start; 0<=index; index--){
1213         if(items[index]->getData()==ptr) return index;
1214         }
1215       if(!(flgs&SEARCH_WRAP)) return -1;
1216       for(index=items.no()-1; start<index; index--){
1217         if(items[index]->getData()==ptr) return index;
1218         }
1219       }
1220     else{
1221       if(start<0) start=0;
1222       for(index=start; index<items.no(); index++){
1223         if(items[index]->getData()==ptr) return index;
1224         }
1225       if(!(flgs&SEARCH_WRAP)) return -1;
1226       for(index=0; index<start; index++){
1227         if(items[index]->getData()==ptr) return index;
1228         }
1229       }
1230     }
1231   return -1;
1232   }
1233 
1234 
1235 // Did we hit the item, and which part of it did we hit
hitItem(FXint index,FXint x,FXint y,FXint ww,FXint hh) const1236 FXint FXIconList::hitItem(FXint index,FXint x,FXint y,FXint ww,FXint hh) const {
1237   FXint ix,iy,r,c,hit=0;
1238   if(0<=index && index<items.no()){
1239     x-=pos_x;
1240     y-=pos_y;
1241     if(!(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS))) y-=header->getDefaultHeight();
1242     if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1243       if(options&ICONLIST_COLUMNS){
1244         r=index/ncols;
1245         c=index%ncols;
1246         }
1247       else{
1248         c=index/nrows;
1249         r=index%nrows;
1250         }
1251       }
1252     else{
1253       r=index;
1254       c=0;
1255       }
1256     ix=itemWidth*c;
1257     iy=itemHeight*r;
1258     hit=items[index]->hitItem(this,x-ix,y-iy,ww,hh);
1259     }
1260   return hit;
1261   }
1262 
1263 
1264 // Repaint
updateItem(FXint index) const1265 void FXIconList::updateItem(FXint index) const {
1266   if(xid && 0<=index && index<items.no()){
1267     if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1268       if(options&ICONLIST_COLUMNS){
1269         FXASSERT(ncols>0);
1270         update(pos_x+itemWidth*(index%ncols),pos_y+itemHeight*(index/ncols),itemWidth,itemHeight);
1271         }
1272       else{
1273         FXASSERT(nrows>0);
1274         update(pos_x+itemWidth*(index/nrows),pos_y+itemHeight*(index%nrows),itemWidth,itemHeight);
1275         }
1276       }
1277     else{
1278       update(0,pos_y+header->getDefaultHeight()+index*itemHeight,width,itemHeight);
1279       }
1280     }
1281   }
1282 
1283 
1284 // Enable one item
enableItem(FXint index)1285 FXbool FXIconList::enableItem(FXint index){
1286   if(index<0 || items.no()<=index){ fxerror("%s::enableItem: index out of range.\n",getClassName()); }
1287   if(!items[index]->isEnabled()){
1288     items[index]->setEnabled(true);
1289     updateItem(index);
1290     return true;
1291     }
1292   return false;
1293   }
1294 
1295 
1296 // Disable one item
disableItem(FXint index)1297 FXbool FXIconList::disableItem(FXint index){
1298   if(index<0 || items.no()<=index){ fxerror("%s::disableItem: index out of range.\n",getClassName()); }
1299   if(items[index]->isEnabled()){
1300     items[index]->setEnabled(false);
1301     updateItem(index);
1302     return true;
1303     }
1304   return false;
1305   }
1306 
1307 
1308 // Select one item
selectItem(FXint index,FXbool notify)1309 FXbool FXIconList::selectItem(FXint index,FXbool notify){
1310   if(index<0 || items.no()<=index){ fxerror("%s::selectItem: index out of range.\n",getClassName()); }
1311   if(!items[index]->isSelected()){
1312     switch(options&SELECT_MASK){
1313       case ICONLIST_SINGLESELECT:
1314       case ICONLIST_BROWSESELECT:
1315         killSelection(notify);
1316       case ICONLIST_EXTENDEDSELECT:
1317       case ICONLIST_MULTIPLESELECT:
1318         items[index]->setSelected(true);
1319         updateItem(index);
1320         if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);}
1321         break;
1322       }
1323     return true;
1324     }
1325   return false;
1326   }
1327 
1328 
1329 // Deselect one item
deselectItem(FXint index,FXbool notify)1330 FXbool FXIconList::deselectItem(FXint index,FXbool notify){
1331   if(index<0 || items.no()<=index){ fxerror("%s::deselectItem: index out of range.\n",getClassName()); }
1332   if(items[index]->isSelected()){
1333     switch(options&SELECT_MASK){
1334       case ICONLIST_EXTENDEDSELECT:
1335       case ICONLIST_MULTIPLESELECT:
1336       case ICONLIST_SINGLESELECT:
1337         items[index]->setSelected(false);
1338         updateItem(index);
1339         if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)index);}
1340         break;
1341       }
1342     return true;
1343     }
1344   return false;
1345   }
1346 
1347 
1348 // Toggle one item
toggleItem(FXint index,FXbool notify)1349 FXbool FXIconList::toggleItem(FXint index,FXbool notify){
1350   if(index<0 || items.no()<=index){ fxerror("%s::toggleItem: index out of range.\n",getClassName()); }
1351   switch(options&SELECT_MASK){
1352     case ICONLIST_BROWSESELECT:
1353       if(!items[index]->isSelected()){
1354         killSelection(notify);
1355         items[index]->setSelected(true);
1356         updateItem(index);
1357         if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);}
1358         }
1359       break;
1360     case ICONLIST_SINGLESELECT:
1361       if(!items[index]->isSelected()){
1362         killSelection(notify);
1363         items[index]->setSelected(true);
1364         updateItem(index);
1365         if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);}
1366         }
1367       else{
1368         items[index]->setSelected(false);
1369         updateItem(index);
1370         if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)index);}
1371         }
1372       break;
1373     case ICONLIST_EXTENDEDSELECT:
1374     case ICONLIST_MULTIPLESELECT:
1375       if(!items[index]->isSelected()){
1376         items[index]->setSelected(true);
1377         updateItem(index);
1378         if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)index);}
1379         }
1380       else{
1381         items[index]->setSelected(false);
1382         updateItem(index);
1383         if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)index);}
1384         }
1385       break;
1386     }
1387   return true;
1388   }
1389 
1390 
1391 // Select items in rectangle
selectInRectangle(FXint x,FXint y,FXint w,FXint h,FXbool notify)1392 FXbool FXIconList::selectInRectangle(FXint x,FXint y,FXint w,FXint h,FXbool notify){
1393   FXint r,c,index;
1394   FXbool changed=false;
1395   if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1396     for(r=0; r<nrows; r++){
1397       for(c=0; c<ncols; c++){
1398         index=(options&ICONLIST_COLUMNS) ? ncols*r+c : nrows*c+r;
1399         if(index<items.no()){
1400           if(hitItem(index,x,y,w,h)){
1401             changed|=selectItem(index,notify);
1402             }
1403           }
1404         }
1405       }
1406     }
1407   else{
1408     for(index=0; index<items.no(); index++){
1409       if(hitItem(index,x,y,w,h)){
1410         changed|=selectItem(index,notify);
1411         }
1412       }
1413     }
1414   return changed;
1415   }
1416 
1417 
1418 // Extend selection
extendSelection(FXint index,FXbool notify)1419 FXbool FXIconList::extendSelection(FXint index,FXbool notify){
1420   FXbool changes=false;
1421   if(0<=index && 0<=anchor && 0<=extent){
1422     FXint i1,i2,i3,i;
1423 
1424     // Find segments
1425     i1=index;
1426     if(anchor<i1){i2=i1;i1=anchor;}
1427     else{i2=anchor;}
1428     if(extent<i1){i3=i2;i2=i1;i1=extent;}
1429     else if(extent<i2){i3=i2;i2=extent;}
1430     else{i3=extent;}
1431 
1432     // First segment
1433     for(i=i1; i<i2; i++){
1434 
1435       // item===extent---anchor
1436       // item===anchor---extent
1437       if(i1==index){
1438         if(!items[i]->isSelected()){
1439           items[i]->setSelected(true);
1440           updateItem(i);
1441           changes=true;
1442           if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)i);}
1443           }
1444         }
1445 
1446       // extent===anchor---item
1447       // extent===item-----anchor
1448       else if(i1==extent){
1449         if(items[i]->isSelected()){
1450           items[i]->setSelected(false);
1451           updateItem(i);
1452           changes=true;
1453           if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)i);}
1454           }
1455         }
1456       }
1457 
1458     // Second segment
1459     for(i=i2+1; i<=i3; i++){
1460 
1461       // extent---anchor===item
1462       // anchor---extent===item
1463       if(i3==index){
1464         if(!items[i]->isSelected()){
1465           items[i]->setSelected(true);
1466           updateItem(i);
1467           changes=true;
1468           if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)i);}
1469           }
1470         }
1471 
1472       // item-----anchor===extent
1473       // anchor---item=====extent
1474       else if(i3==extent){
1475         if(items[i]->isSelected()){
1476           items[i]->setSelected(false);
1477           updateItem(i);
1478           changes=true;
1479           if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)i);}
1480           }
1481         }
1482       }
1483     extent=index;
1484     }
1485   return changes;
1486   }
1487 
1488 
1489 // Select all items
selectAll(FXbool notify)1490 FXbool FXIconList::selectAll(FXbool notify){
1491   FXbool changes=false;
1492   for(FXint i=0; i<items.no(); i++){
1493     if(!items[i]->isSelected()){
1494       items[i]->setSelected(true);
1495       updateItem(i);
1496       changes=true;
1497       if(notify && target){target->tryHandle(this,FXSEL(SEL_SELECTED,message),(void*)(FXival)i);}
1498       }
1499     }
1500   return changes;
1501   }
1502 
1503 
1504 // Kill selection
killSelection(FXbool notify)1505 FXbool FXIconList::killSelection(FXbool notify){
1506   FXbool changes=false;
1507   for(FXint i=0; i<items.no(); i++){
1508     if(items[i]->isSelected()){
1509       items[i]->setSelected(false);
1510       updateItem(i);
1511       changes=true;
1512       if(notify && target){target->tryHandle(this,FXSEL(SEL_DESELECTED,message),(void*)(FXival)i);}
1513       }
1514     }
1515   return changes;
1516   }
1517 
1518 
1519 // Lasso changed, so select/unselect items based on difference between new and old lasso box
lassoChanged(FXint ox,FXint oy,FXint ow,FXint oh,FXint nx,FXint ny,FXint nw,FXint nh,FXbool notify)1520 void FXIconList::lassoChanged(FXint ox,FXint oy,FXint ow,FXint oh,FXint nx,FXint ny,FXint nw,FXint nh,FXbool notify){
1521   FXint x0,y0,x1,y1,rlo,rhi,clo,chi,r,c;
1522   FXint ohit,nhit,index;
1523   if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1524 
1525     // Union rectangle
1526     x0=FXMIN(ox,nx); x1=FXMAX(ox+ow,nx+nw);
1527     y0=FXMIN(oy,ny); y1=FXMAX(oy+oh,ny+nh);
1528 
1529     // Affected row range
1530     rlo=(y0-pos_y)/itemHeight; if(rlo<0) rlo=0;
1531     rhi=(y1-pos_y)/itemHeight; if(rhi>=nrows) rhi=nrows-1;
1532 
1533     // Affected column range
1534     clo=(x0-pos_x)/itemWidth; if(clo<0) clo=0;
1535     chi=(x1-pos_x)/itemWidth; if(chi>=ncols) chi=ncols-1;
1536 
1537     // Change selection
1538     for(r=rlo; r<=rhi; r++){
1539       for(c=clo; c<=chi; c++){
1540         index=(options&ICONLIST_COLUMNS) ? ncols*r+c : nrows*c+r;
1541         if(index<items.no()){
1542           ohit=hitItem(index,ox,oy,ow,oh);
1543           nhit=hitItem(index,nx,ny,nw,nh);
1544           if(ohit && !nhit){      // In old rectangle and not in new rectangle
1545             deselectItem(index,notify);
1546             }
1547           else if(!ohit && nhit){ // Not in old rectangle and in new rectangle
1548             selectItem(index,notify);
1549             }
1550           }
1551         }
1552       }
1553     }
1554   else{
1555 
1556     // Affected lines
1557     y0=FXMIN(oy,ny); y1=FXMAX(oy+oh,ny+nh);
1558 
1559     // Exposed rows
1560     rlo=(y0-pos_y-header->getDefaultHeight())/itemHeight; if(rlo<0) rlo=0;
1561     rhi=(y1-pos_y-header->getDefaultHeight())/itemHeight; if(rhi>=items.no()) rhi=items.no()-1;
1562 
1563     // Change selection
1564     for(index=rlo; index<=rhi; index++){
1565       ohit=hitItem(index,ox,oy,ow,oh);
1566       nhit=hitItem(index,nx,ny,nw,nh);
1567       if(ohit && !nhit){          // Was in old, not in new
1568         deselectItem(index,notify);
1569         }
1570       else if(!ohit && nhit){     // Not in old, but in new
1571         selectItem(index,notify);
1572         }
1573       }
1574     }
1575   }
1576 
1577 
1578 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)1579 long FXIconList::onCmdSetValue(FXObject*,FXSelector,void* ptr){
1580   setCurrentItem((FXint)(FXival)ptr);
1581   return 1;
1582   }
1583 
1584 
1585 // Obtain value from list
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)1586 long FXIconList::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
1587   *((FXint*)ptr)=getCurrentItem();
1588   return 1;
1589   }
1590 
1591 
1592 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)1593 long FXIconList::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
1594   setCurrentItem(*((FXint*)ptr));
1595   return 1;
1596   }
1597 
1598 
1599 // Start motion timer while in this window
onEnter(FXObject * sender,FXSelector sel,void * ptr)1600 long FXIconList::onEnter(FXObject* sender,FXSelector sel,void* ptr){
1601   FXScrollArea::onEnter(sender,sel,ptr);
1602   getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
1603   return 1;
1604   }
1605 
1606 
1607 // Stop motion timer when leaving window
onLeave(FXObject * sender,FXSelector sel,void * ptr)1608 long FXIconList::onLeave(FXObject* sender,FXSelector sel,void* ptr){
1609   FXScrollArea::onLeave(sender,sel,ptr);
1610   getApp()->removeTimeout(this,ID_TIPTIMER);
1611   return 1;
1612   }
1613 
1614 
1615 // We timed out, i.e. the user didn't move for a while
onTipTimer(FXObject *,FXSelector,void *)1616 long FXIconList::onTipTimer(FXObject*,FXSelector,void*){
1617   FXTRACE((250,"%s::onTipTimer %p\n",getClassName(),this));
1618   flags|=FLAG_TIP;
1619   return 1;
1620   }
1621 
1622 
1623 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)1624 long FXIconList::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
1625   FXint index,cx,cy; FXuint btns;
1626   if(FXScrollArea::onQueryTip(sender,sel,ptr)) return 1;
1627   if(flags&FLAG_TIP){
1628     if(getCursorPosition(cx,cy,btns) && (index=getItemAt(cx,cy))>=0){
1629       FXString string=getItem(index)->getTipText();
1630       sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&string);
1631       return 1;
1632       }
1633     }
1634   return 0;
1635   }
1636 
1637 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)1638 long FXIconList::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
1639   if(FXScrollArea::onQueryHelp(sender,sel,ptr)) return 1;
1640   if((flags&FLAG_HELP) && !help.empty()){
1641     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
1642     return 1;
1643     }
1644   return 0;
1645   }
1646 
1647 
1648 // Gained focus
onFocusIn(FXObject * sender,FXSelector sel,void * ptr)1649 long FXIconList::onFocusIn(FXObject* sender,FXSelector sel,void* ptr){
1650   FXScrollArea::onFocusIn(sender,sel,ptr);
1651   if(0<=current){
1652     FXASSERT(current<items.no());
1653     items[current]->setFocus(true);
1654     updateItem(current);
1655     }
1656   return 1;
1657   }
1658 
1659 
1660 // Lost focus
onFocusOut(FXObject * sender,FXSelector sel,void * ptr)1661 long FXIconList::onFocusOut(FXObject* sender,FXSelector sel,void* ptr){
1662   FXScrollArea::onFocusOut(sender,sel,ptr);
1663   if(0<=current){
1664     FXASSERT(current<items.no());
1665     items[current]->setFocus(false);
1666     updateItem(current);
1667     }
1668   return 1;
1669   }
1670 
1671 
1672 // Draw item list
onPaint(FXObject *,FXSelector,void * ptr)1673 long FXIconList::onPaint(FXObject*,FXSelector,void* ptr){
1674   FXint rlo,rhi,clo,chi,x,y,w,h,r,c,index;
1675   FXEvent* event=(FXEvent*)ptr;
1676   FXDCWindow dc(this,event);
1677 
1678   // Set font
1679   dc.setFont(font);
1680 
1681   // Icon mode
1682   if(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS)){
1683 
1684     // Exposed rows
1685     rlo=(event->rect.y-pos_y)/itemHeight;
1686     rhi=(event->rect.y+event->rect.h-pos_y)/itemHeight;
1687     if(rlo<0) rlo=0;
1688     if(rhi>=nrows) rhi=nrows-1;
1689 
1690     // Exposed columns
1691     clo=(event->rect.x-pos_x)/itemWidth;
1692     chi=(event->rect.x+event->rect.w-pos_x)/itemWidth;
1693     if(clo<0) clo=0;
1694     if(chi>=ncols) chi=ncols-1;
1695 
1696     for(r=rlo; r<=rhi; r++){
1697       y=pos_y+r*itemHeight;
1698       for(c=clo; c<=chi; c++){
1699         x=pos_x+c*itemWidth;
1700         index=(options&ICONLIST_COLUMNS) ? ncols*r+c : nrows*c+r;
1701         dc.setForeground(backColor);
1702         if(index<items.no()){
1703           items[index]->draw(this,dc,x,y,itemWidth,itemHeight);
1704           }
1705         else{
1706           dc.fillRectangle(x,y,itemWidth,itemHeight);
1707           }
1708         }
1709       }
1710 
1711     // Background below
1712     y=pos_y+(rhi+1)*itemHeight;
1713     if(y<event->rect.y+event->rect.h){
1714       dc.setForeground(backColor);
1715       dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y);
1716       }
1717 
1718     // Background to the right
1719     x=pos_x+(chi+1)*itemWidth;
1720     if(x<event->rect.x+event->rect.w){
1721       dc.setForeground(backColor);
1722       dc.fillRectangle(x,event->rect.y,event->rect.x+event->rect.w-x,event->rect.h);
1723       }
1724     }
1725 
1726   // Detail mode
1727   else{
1728 
1729     // Exposed rows
1730     rlo=(event->rect.y-pos_y-header->getDefaultHeight())/itemHeight;
1731     rhi=(event->rect.y+event->rect.h-pos_y-header->getDefaultHeight())/itemHeight;
1732     if(rlo<0) rlo=0;
1733     if(rhi>=items.no()) rhi=items.no()-1;
1734 
1735     // Repaint the items
1736     y=pos_y+rlo*itemHeight+header->getDefaultHeight();
1737     for(index=rlo; index<=rhi; index++,y+=itemHeight){
1738       dc.setForeground(backColor);
1739       items[index]->draw(this,dc,pos_x,y,header->getTotalSize(),itemHeight);
1740       }
1741 
1742     // Background below
1743     y=pos_y+(rhi+1)*itemHeight+header->getDefaultHeight();
1744     if(y<event->rect.y+event->rect.h){
1745       dc.setForeground(backColor);
1746       dc.fillRectangle(event->rect.x,y,event->rect.w,event->rect.y+event->rect.h-y);
1747       }
1748 
1749     // Background to the right
1750     x=pos_x+header->getTotalSize();
1751     if(x<event->rect.x+event->rect.w){
1752       dc.setForeground(backColor);
1753       dc.fillRectangle(x,event->rect.y,event->rect.x+event->rect.w-x,event->rect.h);
1754       }
1755     }
1756 
1757   // Gray selection rectangle; look ma, no blending!
1758   if(flags&FLAG_LASSO){
1759     if(anchorx!=currentx && anchory!=currenty){
1760       FXMINMAX(x,w,anchorx,currentx); w-=x;
1761       FXMINMAX(y,h,anchory,currenty); h-=y;
1762       dc.setFunction(BLT_SRC_AND_DST);
1763       dc.setForeground(FXRGB(0xD5,0xD5,0xD5));
1764       dc.fillRectangle(x+pos_x,y+pos_y,w,h);
1765       dc.setForeground(FXRGB(0x55,0x55,0x55));
1766       dc.drawRectangle(x+pos_x,y+pos_y,w-1,h-1);
1767       }
1768     }
1769 
1770   return 1;
1771   }
1772 
1773 
1774 // Start lasso operation
startLasso(FXint ax,FXint ay)1775 void FXIconList::startLasso(FXint ax,FXint ay){
1776   anchorx=currentx=ax;
1777   anchory=currenty=ay;
1778   flags|=FLAG_LASSO;
1779   }
1780 
1781 
1782 // Update lasso area
updateLasso(FXint cx,FXint cy)1783 void FXIconList::updateLasso(FXint cx,FXint cy){
1784   FXint slx,shx,sly,shy,lx,hx,ly,hy;
1785   FXMINMAX(slx,shx,currentx,cx);
1786   FXMINMAX(sly,shy,currenty,cy);
1787   lx=FXMIN(anchorx,slx);
1788   ly=FXMIN(anchory,sly);
1789   hx=FXMAX(anchorx,shx);
1790   hy=FXMAX(anchory,shy);
1791   currentx=cx;
1792   currenty=cy;
1793   update(pos_x+lx,pos_y+sly-1,hx-lx,shy-sly+2);
1794 #ifdef WIN32
1795   repaint(pos_x+lx,pos_y+sly-1,hx-lx,shy-sly+2);
1796 #endif
1797   update(pos_x+slx-1,pos_y+ly,shx-slx+2,hy-ly);
1798 #ifdef WIN32
1799   repaint(pos_x+slx-1,pos_y+ly,shx-slx+2,hy-ly);
1800 #endif
1801   }
1802 
1803 
1804 // End lasso operation
endLasso()1805 void FXIconList::endLasso(){
1806   FXint lx,ly,hx,hy;
1807   FXMINMAX(lx,hx,anchorx,currentx);
1808   FXMINMAX(ly,hy,anchory,currenty);
1809   update(pos_x+lx,pos_y+ly,hx-lx,hy-ly);
1810   flags&=~FLAG_LASSO;
1811   }
1812 
1813 
1814 // Arrange by rows
onCmdArrangeByRows(FXObject *,FXSelector,void *)1815 long FXIconList::onCmdArrangeByRows(FXObject*,FXSelector,void*){
1816   options&=~ICONLIST_COLUMNS;
1817   recalc();
1818   return 1;
1819   }
1820 
1821 
1822 // Update sender
onUpdArrangeByRows(FXObject * sender,FXSelector,void *)1823 long FXIconList::onUpdArrangeByRows(FXObject* sender,FXSelector,void*){
1824   sender->handle(this,(options&ICONLIST_COLUMNS)?FXSEL(SEL_COMMAND,ID_UNCHECK):FXSEL(SEL_COMMAND,ID_CHECK),NULL);
1825   sender->handle(this,(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS))?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1826   return 1;
1827   }
1828 
1829 
1830 // Arrange by columns
onCmdArrangeByColumns(FXObject *,FXSelector,void *)1831 long FXIconList::onCmdArrangeByColumns(FXObject*,FXSelector,void*){
1832   options|=ICONLIST_COLUMNS;
1833   recalc();
1834   return 1;
1835   }
1836 
1837 
1838 // Update sender
onUpdArrangeByColumns(FXObject * sender,FXSelector,void *)1839 long FXIconList::onUpdArrangeByColumns(FXObject* sender,FXSelector,void*){
1840   sender->handle(this,(options&ICONLIST_COLUMNS)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1841   sender->handle(this,(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS))?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
1842   return 1;
1843   }
1844 
1845 
1846 // Show detailed list
onCmdShowDetails(FXObject *,FXSelector,void *)1847 long FXIconList::onCmdShowDetails(FXObject*,FXSelector,void*){
1848   options&=~ICONLIST_MINI_ICONS;
1849   options&=~ICONLIST_BIG_ICONS;
1850   recalc();
1851   return 1;
1852   }
1853 
1854 
1855 // Update sender
onUpdShowDetails(FXObject * sender,FXSelector,void *)1856 long FXIconList::onUpdShowDetails(FXObject* sender,FXSelector,void*){
1857   sender->handle(this,(options&(ICONLIST_MINI_ICONS|ICONLIST_BIG_ICONS))?FXSEL(SEL_COMMAND,ID_UNCHECK):FXSEL(SEL_COMMAND,ID_CHECK),NULL);
1858   return 1;
1859   }
1860 
1861 
1862 // Show big icons
onCmdShowBigIcons(FXObject *,FXSelector,void *)1863 long FXIconList::onCmdShowBigIcons(FXObject*,FXSelector,void*){
1864   options&=~ICONLIST_MINI_ICONS;
1865   options|=ICONLIST_BIG_ICONS;
1866   recalc();
1867   return 1;
1868   }
1869 
1870 
1871 // Update sender
onUpdShowBigIcons(FXObject * sender,FXSelector,void *)1872 long FXIconList::onUpdShowBigIcons(FXObject* sender,FXSelector,void*){
1873   sender->handle(this,(options&ICONLIST_BIG_ICONS)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1874   return 1;
1875   }
1876 
1877 
1878 // Show small icons
onCmdShowMiniIcons(FXObject *,FXSelector,void *)1879 long FXIconList::onCmdShowMiniIcons(FXObject*,FXSelector,void*){
1880   options|=ICONLIST_MINI_ICONS;
1881   options&=~ICONLIST_BIG_ICONS;
1882   recalc();
1883   return 1;
1884   }
1885 
1886 
1887 // Update sender
onUpdShowMiniIcons(FXObject * sender,FXSelector,void *)1888 long FXIconList::onUpdShowMiniIcons(FXObject* sender,FXSelector,void*){
1889   sender->handle(this,(options&ICONLIST_MINI_ICONS)?FXSEL(SEL_COMMAND,ID_CHECK):FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
1890   return 1;
1891   }
1892 
1893 
1894 // Select all items
onCmdSelectAll(FXObject *,FXSelector,void *)1895 long FXIconList::onCmdSelectAll(FXObject*,FXSelector,void*){
1896   for(int i=0; i<items.no(); i++) selectItem(i,true);
1897   return 1;
1898   }
1899 
1900 
1901 // Deselect all items
onCmdDeselectAll(FXObject *,FXSelector,void *)1902 long FXIconList::onCmdDeselectAll(FXObject*,FXSelector,void*){
1903   for(int i=0; i<items.no(); i++) deselectItem(i,true);
1904   return 1;
1905   }
1906 
1907 
1908 // Select inverse of current selection
onCmdSelectInverse(FXObject *,FXSelector,void *)1909 long FXIconList::onCmdSelectInverse(FXObject*,FXSelector,void*){
1910   for(int i=0; i<items.no(); i++) toggleItem(i,true);
1911   return 1;
1912   }
1913 
1914 
1915 // Compare sectioned strings
compareSection(const FXchar * p,const FXchar * q,FXint s)1916 FXint FXIconList::compareSection(const FXchar *p,const FXchar* q,FXint s){
1917   FXint c1,c2,x;
1918   for(x=s; x && *p; x-=(*p++=='\t')){}
1919   for(x=s; x && *q; x-=(*q++=='\t')){}
1920   do{
1921     c1=(FXuchar) *p++;
1922     c2=(FXuchar) *q++;
1923     }
1924   while('\t'<c1 && (c1==c2));
1925   return c1-c2;
1926   }
1927 
1928 
1929 // Compare sectioned strings, case-insensitive
compareSectionCase(const FXchar * p,const FXchar * q,FXint s)1930 FXint FXIconList::compareSectionCase(const FXchar *p,const FXchar* q,FXint s){
1931   FXint c1,c2,x;
1932   for(x=s; x && *p; x-=(*p++=='\t')){}
1933   for(x=s; x && *q; x-=(*q++=='\t')){}
1934   do{
1935     c1=Unicode::toLower(wc(p)); p=wcinc(p);
1936     c2=Unicode::toLower(wc(q)); q=wcinc(q);
1937     }
1938   while('\t'<c1 && (c1==c2));
1939   return c1-c2;
1940   }
1941 
1942 
1943 // Sort items in ascending order
ascending(const FXIconItem * a,const FXIconItem * b)1944 FXint FXIconList::ascending(const FXIconItem* a,const FXIconItem* b){
1945   return compareSection(a->getText().text(),b->getText().text(),0);
1946   }
1947 
1948 
1949 // Sort items in descending order
descending(const FXIconItem * a,const FXIconItem * b)1950 FXint FXIconList::descending(const FXIconItem* a,const FXIconItem* b){
1951   return compareSection(b->getText().text(),a->getText().text(),0);
1952   }
1953 
1954 
1955 // Sort items in ascending order, case insensitive
ascendingCase(const FXIconItem * a,const FXIconItem * b)1956 FXint FXIconList::ascendingCase(const FXIconItem* a,const FXIconItem* b){
1957   return compareSectionCase(a->getText().text(),b->getText().text(),0);
1958   }
1959 
1960 
1961 // Sort items in descending order, case insensitive
descendingCase(const FXIconItem * a,const FXIconItem * b)1962 FXint FXIconList::descendingCase(const FXIconItem* a,const FXIconItem* b){
1963   return compareSectionCase(b->getText().text(),a->getText().text(),0);
1964   }
1965 
1966 
1967 // Sort the items based on the sort function
sortItems()1968 void FXIconList::sortItems(){
1969   FXIconItem *v,*c=0;
1970   FXbool exch=false;
1971   FXint i,j,h;
1972   if(sortfunc){
1973     if(0<=current){
1974       c=items[current];
1975       }
1976     for(h=1; h<=items.no()/9; h=3*h+1){}
1977     for(; h>0; h/=3){
1978       for(i=h+1;i<=items.no();i++){
1979         v=items[i-1];
1980         j=i;
1981         while(j>h && sortfunc(items[j-h-1],v)>0){
1982           items[j-1]=items[j-h-1];
1983           exch=true;
1984           j-=h;
1985           }
1986         items[j-1]=v;
1987         }
1988       }
1989     if(0<=current){
1990       for(i=0; i<items.no(); i++){
1991         if(items[i]==c){ current=i; break; }
1992         }
1993       }
1994     if(exch) recalc();
1995     }
1996   }
1997 
1998 
1999 // Set current item
setCurrentItem(FXint index,FXbool notify)2000 void FXIconList::setCurrentItem(FXint index,FXbool notify){
2001   if(index<-1 || items.no()<=index){ fxerror("%s::setCurrentItem: index out of range.\n",getClassName()); }
2002   if(index!=current){
2003 
2004     // Deactivate old item
2005     if(0<=current){
2006 
2007       // No visible change if it doen't have the focus
2008       if(hasFocus()){
2009         items[current]->setFocus(false);
2010         updateItem(current);
2011         }
2012       }
2013 
2014     current=index;
2015 
2016     // Activate new item
2017     if(0<=current){
2018 
2019       // No visible change if it doen't have the focus
2020       if(hasFocus()){
2021         items[current]->setFocus(true);
2022         updateItem(current);
2023         }
2024       }
2025 
2026     // Notify item change
2027     if(notify && target){
2028       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);
2029       }
2030     }
2031 
2032   // In browse selection mode, select item
2033   if((options&SELECT_MASK)==ICONLIST_BROWSESELECT && 0<=current && items[current]->isEnabled()){
2034     selectItem(current,notify);
2035     }
2036   }
2037 
2038 
2039 // Set anchor item
setAnchorItem(FXint index)2040 void FXIconList::setAnchorItem(FXint index){
2041   if(index<-1 || items.no()<=index){ fxerror("%s::setAnchorItem: index out of range.\n",getClassName()); }
2042   anchor=index;
2043   extent=index;
2044   }
2045 
2046 
2047 // Zero out lookup string
onLookupTimer(FXObject *,FXSelector,void *)2048 long FXIconList::onLookupTimer(FXObject*,FXSelector,void*){
2049   lookup=FXString::null;
2050   return 1;
2051   }
2052 
2053 
2054 // Key Press
onKeyPress(FXObject *,FXSelector,void * ptr)2055 long FXIconList::onKeyPress(FXObject*,FXSelector,void* ptr){
2056   FXEvent* event=(FXEvent*)ptr;
2057   FXint index=current;
2058   flags&=~FLAG_TIP;
2059   if(!isEnabled()) return 0;
2060   if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
2061   switch(event->code){
2062     case KEY_Control_L:
2063     case KEY_Control_R:
2064     case KEY_Shift_L:
2065     case KEY_Shift_R:
2066     case KEY_Alt_L:
2067     case KEY_Alt_R:
2068       if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
2069       return 1;
2070     case KEY_Page_Up:
2071     case KEY_KP_Page_Up:
2072       lookup=FXString::null;
2073       setPosition(pos_x,pos_y+verticalScrollBar()->getPage());
2074       return 1;
2075     case KEY_Page_Down:
2076     case KEY_KP_Page_Down:
2077       lookup=FXString::null;
2078       setPosition(pos_x,pos_y-verticalScrollBar()->getPage());
2079       return 1;
2080     case KEY_Right:
2081     case KEY_KP_Right:
2082       if(!(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS))){
2083         setPosition(pos_x-10,pos_y);
2084         return 1;
2085         }
2086       if(options&ICONLIST_COLUMNS) index+=1; else index+=nrows;
2087       goto hop;
2088     case KEY_Left:
2089     case KEY_KP_Left:
2090       if(!(options&(ICONLIST_BIG_ICONS|ICONLIST_MINI_ICONS))){
2091         setPosition(pos_x+10,pos_y);
2092         return 1;
2093         }
2094       if(options&ICONLIST_COLUMNS) index-=1; else index-=nrows;
2095       goto hop;
2096     case KEY_Up:
2097     case KEY_KP_Up:
2098       if(options&ICONLIST_COLUMNS) index-=ncols; else index-=1;
2099       goto hop;
2100     case KEY_Down:
2101     case KEY_KP_Down:
2102       if(options&ICONLIST_COLUMNS) index+=ncols; else index+=1;
2103       goto hop;
2104     case KEY_Home:
2105     case KEY_KP_Home:
2106       index=0;
2107       goto hop;
2108     case KEY_End:
2109     case KEY_KP_End:
2110       index=items.no()-1;
2111 hop:  lookup=FXString::null;
2112       if(0<=index && index<items.no()){
2113         setCurrentItem(index,true);
2114         makeItemVisible(index);
2115         if(items[index]->isEnabled()){
2116           if((options&SELECT_MASK)==ICONLIST_EXTENDEDSELECT){
2117             if(event->state&SHIFTMASK){
2118               if(0<=anchor){
2119                 selectItem(anchor,true);
2120                 extendSelection(index,true);
2121                 }
2122               else{
2123                 selectItem(index,true);
2124                 }
2125               }
2126             else if(!(event->state&CONTROLMASK)){
2127               killSelection(true);
2128               selectItem(index,true);
2129               setAnchorItem(index);
2130               }
2131             }
2132           }
2133         }
2134       handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current);
2135       if(0<=current && items[current]->isEnabled()){
2136         handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current);
2137         }
2138       return 1;
2139     case KEY_space:
2140     case KEY_KP_Space:
2141       lookup=FXString::null;
2142       if(0<=current && items[current]->isEnabled()){
2143         switch(options&SELECT_MASK){
2144           case ICONLIST_EXTENDEDSELECT:
2145             if(event->state&SHIFTMASK){
2146               if(0<=anchor){
2147                 selectItem(anchor,true);
2148                 extendSelection(current,true);
2149                 }
2150               else{
2151                 selectItem(current,true);
2152                 }
2153               }
2154             else if(event->state&CONTROLMASK){
2155               toggleItem(current,true);
2156               }
2157             else{
2158               killSelection(true);
2159               selectItem(current,true);
2160               }
2161             break;
2162           case ICONLIST_MULTIPLESELECT:
2163           case ICONLIST_SINGLESELECT:
2164             toggleItem(current,true);
2165             break;
2166           }
2167         setAnchorItem(current);
2168         }
2169       handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current);
2170       if(0<=current && items[current]->isEnabled()){
2171         handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current);
2172         }
2173       return 1;
2174     case KEY_Return:
2175     case KEY_KP_Enter:
2176       lookup=FXString::null;
2177       handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)(FXival)current);
2178       if(0<=current && items[current]->isEnabled()){
2179         handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current);
2180         }
2181       return 1;
2182     default:
2183       if((FXuchar)event->text[0]<' ') return 0;
2184       if(event->state&(CONTROLMASK|ALTMASK)) return 0;
2185       if(!Ascii::isPrint(event->text[0])) return 0;
2186       lookup.append(event->text);
2187       getApp()->addTimeout(this,ID_LOOKUPTIMER,getApp()->getTypingSpeed());
2188       index=findItem(lookup,current,SEARCH_FORWARD|SEARCH_WRAP|SEARCH_PREFIX);
2189       if(0<=index){
2190 	setCurrentItem(index,true);
2191 	makeItemVisible(index);
2192 	if(items[index]->isEnabled()){
2193 	  if((options&SELECT_MASK)==ICONLIST_EXTENDEDSELECT){
2194 	    killSelection(true);
2195 	    selectItem(index,true);
2196 	    }
2197 	  setAnchorItem(index);
2198 	  }
2199         }
2200       handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current);
2201       if(0<=current && items[current]->isEnabled()){
2202         handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current);
2203         }
2204       return 1;
2205     }
2206   return 0;
2207   }
2208 
2209 
2210 // Key Release
onKeyRelease(FXObject *,FXSelector,void * ptr)2211 long FXIconList::onKeyRelease(FXObject*,FXSelector,void* ptr){
2212   FXEvent* event=(FXEvent*)ptr;
2213   if(!isEnabled()) return 0;
2214   if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
2215   switch(event->code){
2216     case KEY_Shift_L:
2217     case KEY_Shift_R:
2218     case KEY_Control_L:
2219     case KEY_Control_R:
2220     case KEY_Alt_L:
2221     case KEY_Alt_R:
2222       if(flags&FLAG_DODRAG){handle(this,FXSEL(SEL_DRAGGED,0),ptr);}
2223       return 1;
2224     }
2225   return 0;
2226   }
2227 
2228 
2229 // Autoscrolling timer
onAutoScroll(FXObject * sender,FXSelector sel,void * ptr)2230 long FXIconList::onAutoScroll(FXObject* sender,FXSelector sel,void* ptr){
2231   FXEvent* event=(FXEvent*)ptr;
2232   FXint olx,orx,oty,oby,nlx,nrx,nty,nby;
2233 
2234   // Scroll the content
2235   FXScrollArea::onAutoScroll(sender,sel,ptr);
2236 
2237   // Lasso mode
2238   if(flags&FLAG_LASSO){
2239 
2240     // Select items in lasso
2241     FXMINMAX(olx,orx,anchorx,currentx);
2242     FXMINMAX(oty,oby,anchory,currenty);
2243     updateLasso(event->win_x-pos_x,event->win_y-pos_y);
2244     FXMINMAX(nlx,nrx,anchorx,currentx);
2245     FXMINMAX(nty,nby,anchory,currenty);
2246     lassoChanged(pos_x+olx,pos_y+oty,orx-olx+1,oby-oty+1,pos_x+nlx,pos_y+nty,nrx-nlx+1,nby-nty+1,true);
2247     return 1;
2248     }
2249 
2250   // Content scrolled, so perhaps something else under cursor
2251   if(flags&FLAG_DODRAG){
2252     handle(this,FXSEL(SEL_DRAGGED,0),ptr);
2253     return 1;
2254     }
2255 
2256   return 0;
2257   }
2258 
2259 
2260 // Mouse moved
onMotion(FXObject *,FXSelector,void * ptr)2261 long FXIconList::onMotion(FXObject*,FXSelector,void* ptr){
2262   FXint olx,orx,oty,oby,nlx,nrx,nty,nby;
2263   FXEvent* event=(FXEvent*)ptr;
2264   FXuint flg=flags;
2265 
2266   // Kill the tip
2267   flags&=~FLAG_TIP;
2268 
2269   // Kill the tip timer
2270   getApp()->removeTimeout(this,ID_TIPTIMER);
2271 
2272   // Right mouse scrolling
2273   if(flags&FLAG_SCROLLING){
2274     setPosition(event->win_x-grabx,event->win_y-graby);
2275     return 1;
2276     }
2277 
2278   // Lasso selection mode
2279   if(flags&FLAG_LASSO){
2280     if(startAutoScroll(event,false)) return 1;
2281 
2282     // Select items in lasso
2283     FXMINMAX(olx,orx,anchorx,currentx);
2284     FXMINMAX(oty,oby,anchory,currenty);
2285     updateLasso(event->win_x-pos_x,event->win_y-pos_y);
2286     FXMINMAX(nlx,nrx,anchorx,currentx);
2287     FXMINMAX(nty,nby,anchory,currenty);
2288     lassoChanged(pos_x+olx,pos_y+oty,orx-olx+1,oby-oty+1,pos_x+nlx,pos_y+nty,nrx-nlx+1,nby-nty+1,true);
2289     return 1;
2290     }
2291 
2292   // Drag and drop mode
2293   if(flags&FLAG_DODRAG){
2294     if(startAutoScroll(event,true)) return 1;
2295     handle(this,FXSEL(SEL_DRAGGED,0),ptr);
2296     return 1;
2297     }
2298 
2299   // Tentative drag and drop
2300   if(flags&FLAG_TRYDRAG){
2301     if(event->moved){
2302       flags&=~FLAG_TRYDRAG;
2303       if(handle(this,FXSEL(SEL_BEGINDRAG,0),ptr)){
2304         flags|=FLAG_DODRAG;
2305         }
2306       }
2307     return 1;
2308     }
2309 
2310   // Reset tip timer if nothing's going on
2311   getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
2312 
2313   // Force GUI update only when needed
2314   return (flg&FLAG_TIP);
2315   }
2316 
2317 
2318 // Pressed a button
onLeftBtnPress(FXObject *,FXSelector,void * ptr)2319 long FXIconList::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
2320   FXEvent* event=(FXEvent*)ptr;
2321   FXint index;
2322   flags&=~FLAG_TIP;
2323   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
2324   if(isEnabled()){
2325     grab();
2326     flags&=~FLAG_UPDATE;
2327 
2328     // First change callback
2329     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
2330 
2331     // Locate item
2332     index=getItemAt(event->win_x,event->win_y);
2333 
2334     // No item
2335     if(index<0){
2336 
2337       // Start lasso
2338       if((options&SELECT_MASK)==ICONLIST_EXTENDEDSELECT){
2339 
2340         // Kill selection
2341         if(!(event->state&(SHIFTMASK|CONTROLMASK))){
2342           killSelection(true);
2343           }
2344 
2345         // Start lasso
2346         startLasso(event->win_x-pos_x,event->win_y-pos_y);
2347         }
2348       return 1;
2349       }
2350 
2351     // Previous selection state
2352     state=items[index]->isSelected();
2353 
2354     // Change current item
2355     setCurrentItem(index,true);
2356 
2357     // Change item selection
2358     switch(options&SELECT_MASK){
2359       case ICONLIST_EXTENDEDSELECT:
2360         if(event->state&SHIFTMASK){
2361           if(0<=anchor){
2362             if(items[anchor]->isEnabled()) selectItem(anchor,true);
2363             extendSelection(index,true);
2364             }
2365           else{
2366             if(items[index]->isEnabled()) selectItem(index,true);
2367             setAnchorItem(index);
2368             }
2369           }
2370         else if(event->state&CONTROLMASK){
2371           if(items[index]->isEnabled() && !state) selectItem(index,true);
2372           setAnchorItem(index);
2373           }
2374         else{
2375           if(items[index]->isEnabled() && !state){ killSelection(true); selectItem(index,true); }
2376           setAnchorItem(index);
2377           }
2378         break;
2379       case ICONLIST_MULTIPLESELECT:
2380       case ICONLIST_SINGLESELECT:
2381         if(items[index]->isEnabled() && !state) selectItem(index,true);
2382         break;
2383       }
2384 
2385     // Are we dragging?
2386     if(state && items[index]->isSelected() && items[index]->isDraggable()){
2387       flags|=FLAG_TRYDRAG;
2388       }
2389 
2390     flags|=FLAG_PRESSED;
2391     return 1;
2392     }
2393   return 0;
2394   }
2395 
2396 
2397 // Released button
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)2398 long FXIconList::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
2399   FXEvent* event=(FXEvent*)ptr;
2400   FXuint flg=flags;
2401   if(isEnabled()){
2402     ungrab();
2403     stopAutoScroll();
2404     flags|=FLAG_UPDATE;
2405     flags&=~(FLAG_PRESSED|FLAG_TRYDRAG|FLAG_LASSO|FLAG_DODRAG);
2406 
2407     // First chance callback
2408     if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
2409 
2410     // Was lassoing
2411     if(flg&FLAG_LASSO){
2412       endLasso();
2413       return 1;
2414       }
2415 
2416     // Was dragging
2417     if(flg&FLAG_DODRAG){
2418       handle(this,FXSEL(SEL_ENDDRAG,0),ptr);
2419       return 1;
2420       }
2421 
2422     // Must have pressed
2423     if(flg&FLAG_PRESSED){
2424 
2425       // Selection change
2426       switch(options&SELECT_MASK){
2427         case ICONLIST_EXTENDEDSELECT:
2428           if(0<=current && items[current]->isEnabled()){
2429             if(event->state&CONTROLMASK){
2430               if(state) deselectItem(current,true);
2431               }
2432             else if(!(event->state&SHIFTMASK)){
2433               if(state){ killSelection(true); selectItem(current,true); }
2434               }
2435             }
2436           break;
2437         case ICONLIST_MULTIPLESELECT:
2438         case ICONLIST_SINGLESELECT:
2439           if(0<=current && items[current]->isEnabled()){
2440             if(state) deselectItem(current,true);
2441             }
2442           break;
2443         }
2444 
2445       // Update anchor
2446       setAnchorItem(current);
2447 
2448       // Generate clicked callbacks
2449       if(event->click_count==1){
2450         handle(this,FXSEL(SEL_CLICKED,0),(void*)(FXival)current);
2451         }
2452       else if(event->click_count==2){
2453         handle(this,FXSEL(SEL_DOUBLECLICKED,0),(void*)(FXival)current);
2454         }
2455       else if(event->click_count==3){
2456         handle(this,FXSEL(SEL_TRIPLECLICKED,0),(void*)(FXival)current);
2457         }
2458 
2459       // Command callback only when clicked on item
2460       if(0<=current && items[current]->isEnabled()){
2461         handle(this,FXSEL(SEL_COMMAND,0),(void*)(FXival)current);
2462         }
2463       }
2464     return 1;
2465     }
2466   return 0;
2467   }
2468 
2469 
2470 // Pressed right button
onRightBtnPress(FXObject *,FXSelector,void * ptr)2471 long FXIconList::onRightBtnPress(FXObject*,FXSelector,void* ptr){
2472   FXEvent* event=(FXEvent*)ptr;
2473   flags&=~FLAG_TIP;
2474   handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
2475   if(isEnabled()){
2476     grab();
2477     flags&=~FLAG_UPDATE;
2478     if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONPRESS,message),ptr)) return 1;
2479     flags|=FLAG_SCROLLING;
2480     grabx=event->win_x-pos_x;
2481     graby=event->win_y-pos_y;
2482     return 1;
2483     }
2484   return 0;
2485   }
2486 
2487 
2488 // Released right button
onRightBtnRelease(FXObject *,FXSelector,void * ptr)2489 long FXIconList::onRightBtnRelease(FXObject*,FXSelector,void* ptr){
2490   if(isEnabled()){
2491     ungrab();
2492     flags&=~FLAG_SCROLLING;
2493     flags|=FLAG_UPDATE;
2494     if(target && target->tryHandle(this,FXSEL(SEL_RIGHTBUTTONRELEASE,message),ptr)) return 1;
2495     return 1;
2496     }
2497   return 0;
2498   }
2499 
2500 
2501 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)2502 long FXIconList::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
2503   FXScrollArea::onUngrabbed(sender,sel,ptr);
2504   flags&=~(FLAG_DODRAG|FLAG_LASSO|FLAG_TRYDRAG|FLAG_PRESSED|FLAG_CHANGED|FLAG_SCROLLING);
2505   flags|=FLAG_UPDATE;
2506   stopAutoScroll();
2507   return 1;
2508   }
2509 
2510 
2511 // Command message
onCommand(FXObject *,FXSelector,void * ptr)2512 long FXIconList::onCommand(FXObject*,FXSelector,void* ptr){
2513   return target && target->tryHandle(this,FXSEL(SEL_COMMAND,message),ptr);
2514   }
2515 
2516 
2517 // Clicked in list
onClicked(FXObject *,FXSelector,void * ptr)2518 long FXIconList::onClicked(FXObject*,FXSelector,void* ptr){
2519   return target && target->tryHandle(this,FXSEL(SEL_CLICKED,message),ptr);
2520   }
2521 
2522 
2523 // Double Clicked in list; ptr may or may not point to an item
onDoubleClicked(FXObject *,FXSelector,void * ptr)2524 long FXIconList::onDoubleClicked(FXObject*,FXSelector,void* ptr){
2525   return target && target->tryHandle(this,FXSEL(SEL_DOUBLECLICKED,message),ptr);
2526   }
2527 
2528 
2529 // Triple Clicked in list; ptr may or may not point to an item
onTripleClicked(FXObject *,FXSelector,void * ptr)2530 long FXIconList::onTripleClicked(FXObject*,FXSelector,void* ptr){
2531   return target && target->tryHandle(this,FXSEL(SEL_TRIPLECLICKED,message),ptr);
2532   }
2533 
2534 
2535 // Create custom item
createItem(const FXString & text,FXIcon * big,FXIcon * mini,FXptr ptr)2536 FXIconItem *FXIconList::createItem(const FXString& text,FXIcon *big,FXIcon* mini,FXptr ptr){
2537   return new FXIconItem(text,big,mini,ptr);
2538   }
2539 
2540 
2541 // Retrieve item
getItem(FXint index) const2542 FXIconItem *FXIconList::getItem(FXint index) const {
2543   if(index<0 || items.no()<=index){ fxerror("%s::getItem: index out of range.\n",getClassName()); }
2544   return items[index];
2545   }
2546 
2547 
2548 // Replace item with another
setItem(FXint index,FXIconItem * item,FXbool notify)2549 FXint FXIconList::setItem(FXint index,FXIconItem* item,FXbool notify){
2550   if(index<0 || items.no()<=index){ fxerror("%s::setItem: index out of range.\n",getClassName()); }
2551   if(items[index]!=item){
2552     FXIconItem *orig=items[index];
2553 
2554     // Must have item
2555     if(!item){ fxerror("%s::setItem: item is NULL.\n",getClassName()); }
2556 
2557     // Notify old item will be deleted
2558     if(notify && target){
2559       target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);
2560       }
2561 
2562     // Keep item state bits
2563     item->setFocus(orig->hasFocus());
2564     item->setSelected(orig->isSelected());
2565 
2566     // Add new
2567     items[index]=item;
2568 
2569     // Notify new item has been inserted
2570     if(notify && target){
2571       target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)(FXival)index);
2572       if(current==index){
2573         target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);
2574         }
2575       }
2576 
2577     // Delete old
2578     delete orig;
2579 
2580     // Redo layout
2581     recalc();
2582     }
2583   return index;
2584   }
2585 
2586 
2587 // Replace item with another
setItem(FXint index,const FXString & text,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2588 FXint FXIconList::setItem(FXint index,const FXString& text,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2589   return setItem(index,createItem(text,big,mini,ptr),notify);
2590   }
2591 
2592 
2593 // Insert item
insertItem(FXint index,FXIconItem * item,FXbool notify)2594 FXint FXIconList::insertItem(FXint index,FXIconItem* item,FXbool notify){
2595   FXint old=current;
2596 
2597   // Must have item
2598   if(!item){ fxerror("%s::insertItem: item is NULL.\n",getClassName()); }
2599 
2600   // Must be in range
2601   if(index<0 || items.no()<index){ fxerror("%s::insertItem: index out of range.\n",getClassName()); }
2602 
2603   // Add item to list
2604   items.insert(index,item);
2605 
2606   // Adjust indices
2607   if(anchor>=index)  anchor++;
2608   if(extent>=index)  extent++;
2609   if(current>=index) current++;
2610   if(viewable>=index) viewable++;
2611   if(current<0 && items.no()==1) current=0;
2612 
2613   // Notify item has been inserted
2614   if(notify && target){
2615     target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)(FXival)index);
2616     if(old!=current){
2617       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);
2618       }
2619     }
2620 
2621   // Was new item
2622   if(index==current){
2623     items[current]->setFocus(hasFocus());
2624     if((options&SELECT_MASK)==ICONLIST_BROWSESELECT && items[current]->isEnabled()){
2625       selectItem(current,notify);
2626       }
2627     }
2628 
2629   // Redo layout
2630   recalc();
2631   return index;
2632   }
2633 
2634 
2635 // Insert item
insertItem(FXint index,const FXString & text,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2636 FXint FXIconList::insertItem(FXint index,const FXString& text,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2637   return insertItem(index,createItem(text,big,mini,ptr),notify);
2638   }
2639 
2640 
2641 // Append item
appendItem(FXIconItem * item,FXbool notify)2642 FXint FXIconList::appendItem(FXIconItem* item,FXbool notify){
2643   return insertItem(items.no(),item,notify);
2644   }
2645 
2646 
2647 // Append item
appendItem(const FXString & text,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2648 FXint FXIconList::appendItem(const FXString& text,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2649   return insertItem(items.no(),createItem(text,big,mini,ptr),notify);
2650   }
2651 
2652 
2653 // Prepend item
prependItem(FXIconItem * item,FXbool notify)2654 FXint FXIconList::prependItem(FXIconItem* item,FXbool notify){
2655   return insertItem(0,item,notify);
2656   }
2657 
2658 
2659 // Prepend item
prependItem(const FXString & text,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2660 FXint FXIconList::prependItem(const FXString& text,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2661   return insertItem(0,createItem(text,big,mini,ptr),notify);
2662   }
2663 
2664 
2665 // Fill list by appending items from array of strings
fillItems(const FXchar ** strings,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2666 FXint FXIconList::fillItems(const FXchar** strings,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2667   FXint n=0;
2668   if(strings){
2669     while(strings[n]){
2670       appendItem(strings[n++],big,mini,ptr,notify);
2671       }
2672     }
2673   return n;
2674   }
2675 
2676 
2677 // Fill list by appending items from array of strings
fillItems(const FXString * strings,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2678 FXint FXIconList::fillItems(const FXString* strings,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2679   FXint n=0;
2680   if(strings){
2681     while(!strings[n].empty()){
2682       appendItem(strings[n++],big,mini,ptr,notify);
2683       }
2684     }
2685   return n;
2686   }
2687 
2688 
2689 // Fill list by appending items from newline separated strings
fillItems(const FXString & strings,FXIcon * big,FXIcon * mini,FXptr ptr,FXbool notify)2690 FXint FXIconList::fillItems(const FXString& strings,FXIcon *big,FXIcon* mini,FXptr ptr,FXbool notify){
2691   FXint beg=0,end=0,n=0;
2692   while(end<strings.length()){
2693     beg=end;
2694     while(end<strings.length() && strings[end]!='\n' && strings[end]!='\r') end++;
2695     appendItem(strings.mid(beg,end-beg),big,mini,ptr,notify);
2696     while(strings[end]=='\n' || strings[end]=='\r') end++;
2697     n++;
2698     }
2699   return n;
2700   }
2701 
2702 
2703 // Move item from oldindex to newindex
moveItem(FXint newindex,FXint oldindex,FXbool notify)2704 FXint FXIconList::moveItem(FXint newindex,FXint oldindex,FXbool notify){
2705   FXint old=current;
2706   FXIconItem *item;
2707 
2708   // Must be in range
2709   if(newindex<0 || oldindex<0 || items.no()<=newindex || items.no()<=oldindex){ fxerror("%s::moveItem: index out of range.\n",getClassName()); }
2710 
2711   // Did it change?
2712   if(oldindex!=newindex){
2713 
2714     // Move item
2715     item=items[oldindex];
2716     items.erase(oldindex);
2717     items.insert(newindex,item);
2718 
2719     // Move item down
2720     if(newindex<oldindex){
2721       if(newindex<=anchor && anchor<oldindex) anchor++;
2722       if(newindex<=extent && extent<oldindex) extent++;
2723       if(newindex<=current && current<oldindex) current++;
2724       if(newindex<=viewable && viewable<oldindex) viewable++;
2725       }
2726 
2727     // Move item up
2728     else{
2729       if(oldindex<anchor && anchor<=newindex) anchor--;
2730       if(oldindex<extent && extent<=newindex) extent--;
2731       if(oldindex<current && current<=newindex) current--;
2732       if(oldindex<viewable && viewable<=newindex) viewable--;
2733       }
2734 
2735     // Adjust if it was equal
2736     if(anchor==oldindex) anchor=newindex;
2737     if(extent==oldindex) extent=newindex;
2738     if(current==oldindex) current=newindex;
2739     if(viewable==oldindex) viewable=newindex;
2740 
2741     // Current item may have changed
2742     if(notify && target && old!=current){
2743       target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);
2744       }
2745 
2746     // Redo layout
2747     recalc();
2748     }
2749   return newindex;
2750   }
2751 
2752 
2753 // Extract node from list
extractItem(FXint index,FXbool notify)2754 FXIconItem* FXIconList::extractItem(FXint index,FXbool notify){
2755   FXIconItem *result;
2756   FXint old=current;
2757 
2758   // Must be in range
2759   if(index<0 || items.no()<=index){ fxerror("%s::extractItem: index out of range.\n",getClassName()); }
2760 
2761   // Notify item will be deleted
2762   if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);}
2763 
2764   // Extract item
2765   result=items[index];
2766 
2767   // Remove from list
2768   items.erase(index);
2769 
2770   // Adjust indices
2771   if(anchor>index || anchor>=items.no())  anchor--;
2772   if(extent>index || extent>=items.no())  extent--;
2773   if(current>index || current>=items.no()) current--;
2774   if(viewable>index || viewable>=items.no())  viewable--;
2775 
2776   // Current item has changed
2777   if(notify && target && index<=old){
2778     target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);
2779     }
2780 
2781   // Deleted current item
2782   if(0<=current && index==old){
2783     items[current]->setFocus(hasFocus());
2784     if((options&SELECT_MASK)==ICONLIST_BROWSESELECT && items[current]->isEnabled()){
2785       selectItem(current,notify);
2786       }
2787     }
2788 
2789   // Redo layout
2790   recalc();
2791 
2792   // Return item
2793   return result;
2794   }
2795 
2796 
2797 // Remove node from list
removeItem(FXint index,FXbool notify)2798 void FXIconList::removeItem(FXint index,FXbool notify){
2799   FXint old=current;
2800 
2801   // Must be in range
2802   if(index<0 || items.no()<=index){ fxerror("%s::removeItem: index out of range.\n",getClassName()); }
2803 
2804   // Notify item will be deleted
2805   if(notify && target){
2806     target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);
2807     }
2808 
2809   // Delete item
2810   delete items[index];
2811 
2812   // Remove from list
2813   items.erase(index);
2814 
2815   // Adjust indices
2816   if(anchor>index || anchor>=items.no())  anchor--;
2817   if(extent>index || extent>=items.no())  extent--;
2818   if(current>index || current>=items.no()) current--;
2819   if(viewable>index || viewable>=items.no())  viewable--;
2820 
2821   // Current item has changed
2822   if(notify && target && index<=old){
2823     target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)current);
2824     }
2825 
2826   // Deleted current item
2827   if(0<=current && index==old){
2828     items[current]->setFocus(hasFocus());
2829     if((options&SELECT_MASK)==ICONLIST_BROWSESELECT && items[current]->isEnabled()){
2830       selectItem(current,notify);
2831       }
2832     }
2833 
2834   // Redo layout
2835   recalc();
2836   }
2837 
2838 
2839 // Remove all items
clearItems(FXbool notify)2840 void FXIconList::clearItems(FXbool notify){
2841   FXint old=current;
2842 
2843   // Delete items
2844   for(FXint index=items.no()-1; 0<=index; index--){
2845     if(notify && target){
2846       target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);
2847       }
2848     delete items[index];
2849     }
2850 
2851   // Free array
2852   items.clear();
2853 
2854   // Adjust indices
2855   current=-1;
2856   anchor=-1;
2857   extent=-1;
2858   viewable=-1;
2859 
2860   // Current item has changed
2861   if(notify && target && old!=-1){
2862     target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)-1);
2863     }
2864 
2865   // Redo layout
2866   recalc();
2867   }
2868 
2869 
2870 // Change the font
setFont(FXFont * fnt)2871 void FXIconList::setFont(FXFont* fnt){
2872   if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); }
2873   if(font!=fnt){
2874     font=fnt;
2875     recalc();
2876     update();
2877     }
2878   }
2879 
2880 
2881 // Set text color
setTextColor(FXColor clr)2882 void FXIconList::setTextColor(FXColor clr){
2883   if(clr!=textColor){
2884     textColor=clr;
2885     update();
2886     }
2887   }
2888 
2889 
2890 // Set select background color
setSelBackColor(FXColor clr)2891 void FXIconList::setSelBackColor(FXColor clr){
2892   if(clr!=selbackColor){
2893     selbackColor=clr;
2894     update();
2895     }
2896   }
2897 
2898 
2899 // Set selected text color
setSelTextColor(FXColor clr)2900 void FXIconList::setSelTextColor(FXColor clr){
2901   if(clr!=seltextColor){
2902     seltextColor=clr;
2903     update();
2904     }
2905   }
2906 
2907 
2908 // Set text width
setItemSpace(FXint s)2909 void FXIconList::setItemSpace(FXint s){
2910   if(s<1) s=1;
2911   if(itemSpace!=s){
2912     itemSpace=s;
2913     recalc();
2914     }
2915   }
2916 
2917 
2918 // Change list style
setListStyle(FXuint style)2919 void FXIconList::setListStyle(FXuint style){
2920   FXuint opts=(options&~ICONLIST_MASK) | (style&ICONLIST_MASK);
2921   if(options!=opts){
2922     options=opts;
2923     recalc();
2924     }
2925   }
2926 
2927 
2928 // Get list style
getListStyle() const2929 FXuint FXIconList::getListStyle() const {
2930   return (options&ICONLIST_MASK);
2931   }
2932 
2933 
2934 // Change help text
setHelpText(const FXString & text)2935 void FXIconList::setHelpText(const FXString& text){
2936   help=text;
2937   }
2938 
2939 
2940 // Save data
save(FXStream & store) const2941 void FXIconList::save(FXStream& store) const {
2942   FXScrollArea::save(store);
2943   store << header;
2944   items.save(store);
2945   store << nrows;
2946   store << ncols;
2947   store << anchor;
2948   store << current;
2949   store << extent;
2950   store << font;
2951   store << textColor;
2952   store << selbackColor;
2953   store << seltextColor;
2954   store << itemSpace;
2955   store << itemWidth;
2956   store << itemHeight;
2957   store << help;
2958   }
2959 
2960 
2961 // Load data
load(FXStream & store)2962 void FXIconList::load(FXStream& store){
2963   FXScrollArea::load(store);
2964   store >> header;
2965   items.load(store);
2966   store >> nrows;
2967   store >> ncols;
2968   store >> anchor;
2969   store >> current;
2970   store >> extent;
2971   store >> font;
2972   store >> textColor;
2973   store >> selbackColor;
2974   store >> seltextColor;
2975   store >> itemSpace;
2976   store >> itemWidth;
2977   store >> itemHeight;
2978   store >> help;
2979   }
2980 
2981 
2982 // Cleanup
~FXIconList()2983 FXIconList::~FXIconList(){
2984   getApp()->removeTimeout(this,ID_TIPTIMER);
2985   getApp()->removeTimeout(this,ID_LOOKUPTIMER);
2986   clearItems(false);
2987   header=(FXHeader*)-1L;
2988   font=(FXFont*)-1L;
2989   }
2990 
2991 }
2992