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