Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/foxtools/MFXListIcon.cpp
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2006-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file MFXListIcon.cpp
15
/// @author Pablo Alvarez Lopez
16
/// @date Feb 2023
17
///
18
//
19
/****************************************************************************/
20
21
#include <utils/common/UtilExceptions.h>
22
#include <fxkeys.h>
23
24
#include "MFXListIconItem.h"
25
#include "MFXListIcon.h"
26
27
// ===========================================================================
28
// Macross
29
// ===========================================================================
30
31
#define LINE_SPACING 4 // Line spacing between items
32
#define ICON_SIZE 16
33
34
#define LIST_MASK (SELECT_MASK | LIST_AUTOSELECT)
35
36
// ===========================================================================
37
// FOX callback mapping
38
// ===========================================================================
39
40
// Map
41
FXDEFMAP(MFXListIcon) MFXListIconMap[] = {
42
FXMAPFUNC(SEL_PAINT, 0, MFXListIcon::onPaint),
43
FXMAPFUNC(SEL_ENTER, 0, MFXListIcon::onEnter),
44
FXMAPFUNC(SEL_LEAVE, 0, MFXListIcon::onLeave),
45
FXMAPFUNC(SEL_MOTION, 0, MFXListIcon::onMotion),
46
FXMAPFUNC(SEL_TIMEOUT, FXWindow::ID_AUTOSCROLL, MFXListIcon::onAutoScroll),
47
FXMAPFUNC(SEL_TIMEOUT, MFXListIcon::ID_TIPTIMER, MFXListIcon::onTipTimer),
48
FXMAPFUNC(SEL_TIMEOUT, MFXListIcon::ID_LOOKUPTIMER, MFXListIcon::onLookupTimer),
49
FXMAPFUNC(SEL_UNGRABBED, 0, MFXListIcon::onUngrabbed),
50
FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, MFXListIcon::onLeftBtnPress),
51
FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, MFXListIcon::onLeftBtnRelease),
52
FXMAPFUNC(SEL_RIGHTBUTTONPRESS, 0, MFXListIcon::onRightBtnPress),
53
FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, MFXListIcon::onRightBtnRelease),
54
FXMAPFUNC(SEL_KEYPRESS, 0, MFXListIcon::onKeyPress),
55
FXMAPFUNC(SEL_KEYRELEASE, 0, MFXListIcon::onKeyRelease),
56
FXMAPFUNC(SEL_FOCUSIN, 0, MFXListIcon::onFocusIn),
57
FXMAPFUNC(SEL_FOCUSOUT, 0, MFXListIcon::onFocusOut),
58
FXMAPFUNC(SEL_CLICKED, 0, MFXListIcon::onClicked),
59
FXMAPFUNC(SEL_DOUBLECLICKED, 0, MFXListIcon::onDoubleClicked),
60
FXMAPFUNC(SEL_TRIPLECLICKED, 0, MFXListIcon::onTripleClicked),
61
FXMAPFUNC(SEL_COMMAND, 0, MFXListIcon::onCommand),
62
FXMAPFUNC(SEL_QUERY_TIP, 0, MFXListIcon::onQueryTip),
63
FXMAPFUNC(SEL_QUERY_HELP, 0, MFXListIcon::onQueryHelp),
64
};
65
66
67
// Object implementation
68
FXIMPLEMENT(MFXListIcon, FXScrollArea, MFXListIconMap, ARRAYNUMBER(MFXListIconMap))
69
70
// ===========================================================================
71
// member method definitions
72
// ===========================================================================
73
74
MFXListIcon::MFXListIcon(FXComposite* p, FXObject* tgt, FXSelector sel, FXuint opts, FXint x, FXint y, FXint w, FXint h):
75
FXScrollArea(p, opts, x, y, w, h) {
76
flags |= FLAG_ENABLED;
77
target = tgt;
78
message = sel;
79
font = getApp()->getNormalFont();
80
textColor = getApp()->getForeColor();
81
selbackColor = getApp()->getSelbackColor();
82
seltextColor = getApp()->getSelforeColor();
83
}
84
85
86
MFXListIcon::~MFXListIcon() {
87
getApp()->removeTimeout(this, ID_TIPTIMER);
88
getApp()->removeTimeout(this, ID_LOOKUPTIMER);
89
clearItems(FALSE);
90
font = (FXFont*) - 1L;
91
}
92
93
94
void
95
MFXListIcon::create() {
96
FXScrollArea::create();
97
for (const auto& item : items) {
98
item->create();
99
}
100
font->create();
101
}
102
103
104
void
105
MFXListIcon::detach() {
106
FXScrollArea::detach();
107
for (const auto& item : items) {
108
item->detach();
109
}
110
font->detach();
111
}
112
113
114
bool
115
MFXListIcon::canFocus() const {
116
return true;
117
}
118
119
120
void
121
MFXListIcon::setFocus() {
122
FXScrollArea::setFocus();
123
setDefault(TRUE);
124
}
125
126
127
void
128
MFXListIcon::killFocus() {
129
FXScrollArea::killFocus();
130
setDefault(MAYBE);
131
}
132
133
134
FXint
135
MFXListIcon::getDefaultWidth() {
136
return FXScrollArea::getDefaultWidth();
137
}
138
139
140
FXint
141
MFXListIcon::getDefaultHeight() {
142
if (visible > (int)itemFiltered.size()) {
143
return (int)itemFiltered.size() * (LINE_SPACING + FXMAX(font->getFontHeight(), ICON_SIZE));
144
} else {
145
return visible * (LINE_SPACING + FXMAX(font->getFontHeight(), ICON_SIZE));
146
}
147
}
148
149
150
void
151
MFXListIcon::recalc() {
152
FXScrollArea::recalc();
153
flags |= FLAG_RECALC;
154
cursor = nullptr;
155
}
156
157
158
void
159
MFXListIcon::setNumVisible(FXint nvis) {
160
if (nvis < 0) {
161
nvis = 0;
162
}
163
if (visible != nvis) {
164
visible = nvis;
165
recalc();
166
}
167
}
168
169
170
FXint
171
MFXListIcon::getContentWidth() {
172
if (flags & FLAG_RECALC) {
173
recompute();
174
}
175
return listWidth;
176
}
177
178
179
FXint
180
MFXListIcon::getContentHeight() {
181
if (flags & FLAG_RECALC) {
182
recompute();
183
}
184
return listHeight;
185
}
186
187
188
void
189
MFXListIcon::layout() {
190
// Calculate contents
191
FXScrollArea::layout();
192
// Determine line size for scroll bars
193
if (0 < (int)itemFiltered.size()) {
194
vertical->setLine(itemFiltered[0]->getHeight(this));
195
horizontal->setLine(itemFiltered[0]->getWidth(this) / 10);
196
}
197
update();
198
// We were supposed to make this item viewable
199
if (viewable) {
200
makeItemVisible(viewable);
201
}
202
// No more dirty
203
flags &= ~(FXuint)FLAG_DIRTY;
204
}
205
206
207
FXbool
208
MFXListIcon::isItemCurrent(FXint index) const {
209
for (int i = 0; i < (int)items.size(); i++) {
210
if (items[i] == currentItem) {
211
return i == index;
212
}
213
}
214
return false;
215
}
216
217
218
FXbool
219
MFXListIcon::isItemVisible(MFXListIconItem* item) const {
220
return (0 < (pos_y + item->y + item->getHeight(this))) && ((pos_y + item->y) < viewport_h);
221
}
222
223
224
void
225
MFXListIcon::makeItemVisible(MFXListIconItem* item) {
226
FXint y, h;
227
// Remember for later
228
viewable = item;
229
// Was realized
230
if (xid) {
231
// Force layout if dirty
232
if (flags & FLAG_RECALC) {
233
layout();
234
}
235
y = pos_y;
236
h = item->getHeight(this);
237
if (viewport_h <= y + item->y + h) {
238
y = viewport_h - item->y - h;
239
}
240
if (y + item->y <= 0) {
241
y = -item->y;
242
}
243
// Scroll into view
244
setPosition(pos_x, y);
245
// Done it
246
viewable = nullptr;
247
}
248
}
249
250
251
void
252
MFXListIcon::makeItemVisible(FXint index) {
253
makeItemVisible(items[index]);
254
}
255
256
257
FXint
258
MFXListIcon::getItemWidth(FXint index) const {
259
if ((index < 0) || ((int)itemFiltered.size() <= index)) {
260
fxerror("%s::isItemSelected: index out of range.\n", getClassName());
261
}
262
return itemFiltered[index]->getWidth(this);
263
}
264
265
266
FXint
267
MFXListIcon::getItemHeight(FXint index) const {
268
if ((index < 0) || ((int)itemFiltered.size() <= index)) {
269
fxerror("%s::isItemSelected: index out of range.\n", getClassName());
270
}
271
return itemFiltered[index]->getHeight(this);
272
}
273
274
275
MFXListIconItem*
276
MFXListIcon::getItemAt(FXint y) const {
277
y -= pos_y;
278
// continue depending if we're filtering
279
if (filter.empty()) {
280
for (int i = 0; i < (int)items.size(); i++) {
281
if (items[i]->y <= y && y < items[i]->y + items[i]->getHeight(this)) {
282
return items[i];
283
}
284
}
285
} else {
286
for (int i = 0; i < (int)itemFiltered.size(); i++) {
287
if ((itemFiltered[i]->y <= y) && (y < itemFiltered[i]->y + itemFiltered[i]->getHeight(this))) {
288
return itemFiltered[i];
289
}
290
}
291
}
292
return nullptr;
293
}
294
295
296
int
297
MFXListIcon::findItem(const FXString& text) const {
298
for (int i = 0; i < (int)items.size(); i++) {
299
if (items[i]->getText().text() == text) {
300
return i;
301
}
302
}
303
return -1;
304
}
305
306
307
FXint
308
MFXListIcon::hitItem(MFXListIconItem* item, FXint x, FXint y) const {
309
FXint ix, iy, hit = 0;
310
if (item) {
311
x -= pos_x;
312
y -= pos_y;
313
ix = item->x;
314
iy = item->y;
315
hit = item->hitItem(this, x - ix, y - iy);
316
}
317
return hit;
318
}
319
320
321
void
322
MFXListIcon::updateItem(MFXListIconItem* item) const {
323
update(0, pos_y + item->y, viewport_w, item->getHeight(this));
324
}
325
326
327
FXbool
328
MFXListIcon::selectItem(MFXListIconItem* item, FXbool notify) {
329
if (!item->isSelected()) {
330
killSelection(notify);
331
item->setSelected(TRUE);
332
updateItem(item);
333
if (notify && target) {
334
target->tryHandle(this, FXSEL(SEL_SELECTED, message), nullptr);
335
}
336
return TRUE;
337
} else {
338
return FALSE;
339
}
340
}
341
342
343
FXbool
344
MFXListIcon::deselectItem(MFXListIconItem* item, FXbool notify) {
345
if (item->isSelected()) {
346
item->setSelected(FALSE);
347
updateItem(item);
348
if (notify && target) {
349
target->tryHandle(this, FXSEL(SEL_DESELECTED, message), nullptr);
350
}
351
return TRUE;
352
} else {
353
return FALSE;
354
}
355
}
356
357
358
FXbool
359
MFXListIcon::toggleItem(MFXListIconItem* item, FXbool notify) {
360
if (!item->isSelected()) {
361
killSelection(notify);
362
item->setSelected(TRUE);
363
updateItem(item);
364
if (notify && target) {
365
target->tryHandle(this, FXSEL(SEL_SELECTED, message), nullptr);
366
}
367
} else {
368
item->setSelected(FALSE);
369
updateItem(item);
370
if (notify && target) {
371
target->tryHandle(this, FXSEL(SEL_DESELECTED, message), nullptr);
372
}
373
}
374
return TRUE;
375
}
376
377
378
FXbool
379
MFXListIcon::killSelection(FXbool notify) {
380
FXbool changes = FALSE;
381
FXint i;
382
for (i = 0; i < (int)items.size(); i++) {
383
if (items[i]->isSelected()) {
384
items[i]->setSelected(FALSE);
385
updateItem(items[i]);
386
changes = TRUE;
387
if (notify && target) {
388
target->tryHandle(this, FXSEL(SEL_DESELECTED, message), (void*)(FXival)i);
389
}
390
}
391
}
392
return changes;
393
}
394
395
396
long
397
MFXListIcon::onEnter(FXObject* sender, FXSelector sel, void* ptr) {
398
FXScrollArea::onEnter(sender, sel, ptr);
399
getApp()->addTimeout(this, ID_TIPTIMER, getApp()->getMenuPause());
400
cursor = nullptr;
401
return 1;
402
}
403
404
405
long
406
MFXListIcon::onLeave(FXObject* sender, FXSelector sel, void* ptr) {
407
FXScrollArea::onLeave(sender, sel, ptr);
408
getApp()->removeTimeout(this, ID_TIPTIMER);
409
cursor = nullptr;
410
return 1;
411
}
412
413
414
long
415
MFXListIcon::onFocusIn(FXObject* sender, FXSelector sel, void* ptr) {
416
FXScrollArea::onFocusIn(sender, sel, ptr);
417
if (currentItem) {
418
currentItem->setFocus(TRUE);
419
updateItem(currentItem);
420
}
421
return 1;
422
}
423
424
425
long
426
MFXListIcon::onTipTimer(FXObject*, FXSelector, void*) {
427
flags |= FLAG_TIP;
428
return 1;
429
}
430
431
432
long
433
MFXListIcon::onQueryTip(FXObject* sender, FXSelector sel, void* ptr) {
434
if (FXWindow::onQueryTip(sender, sel, ptr)) {
435
return 1;
436
}
437
if (cursor && (flags & FLAG_TIP) && !(options & LIST_AUTOSELECT)) { // No tip when autoselect!
438
FXString string = cursor->getText();
439
sender->handle(this, FXSEL(SEL_COMMAND, ID_SETSTRINGVALUE), (void*) & string);
440
return 1;
441
}
442
return 0;
443
}
444
445
446
long
447
MFXListIcon::onQueryHelp(FXObject* sender, FXSelector sel, void* ptr) {
448
if (FXWindow::onQueryHelp(sender, sel, ptr)) {
449
return 1;
450
}
451
if ((flags & FLAG_HELP) && !help.empty()) {
452
sender->handle(this, FXSEL(SEL_COMMAND, ID_SETSTRINGVALUE), (void*) & help);
453
return 1;
454
}
455
return 0;
456
}
457
458
459
long
460
MFXListIcon::onFocusOut(FXObject* sender, FXSelector sel, void* ptr) {
461
FXScrollArea::onFocusOut(sender, sel, ptr);
462
if (currentItem) {
463
currentItem->setFocus(FALSE);
464
updateItem(currentItem);
465
}
466
return 1;
467
}
468
469
470
long
471
MFXListIcon::onPaint(FXObject*, FXSelector, void* ptr) {
472
FXEvent* event = (FXEvent*)ptr;
473
FXDCWindow dc(this, event);
474
FXint y, h;
475
// Paint items
476
y = pos_y;
477
for (int i = 0; i < (int)itemFiltered.size(); i++) {
478
h = itemFiltered[i]->getHeight(this);
479
if (event->rect.y <= (y + h) && y < (event->rect.y + event->rect.h)) {
480
itemFiltered[i]->draw(this, dc, pos_x, y, FXMAX(listWidth, viewport_w), h);
481
}
482
y += h;
483
}
484
// Paint blank area below items
485
if (y < (event->rect.y + event->rect.h)) {
486
dc.setForeground(backColor);
487
dc.fillRectangle(event->rect.x, y, event->rect.w, event->rect.y + event->rect.h - y);
488
}
489
return 1;
490
}
491
492
493
long
494
MFXListIcon::onLookupTimer(FXObject*, FXSelector, void*) {
495
lookup = FXString::null;
496
return 1;
497
}
498
499
500
long
501
MFXListIcon::onKeyPress(FXObject*, FXSelector, void* ptr) {
502
FXEvent* event = (FXEvent*)ptr;
503
FXint index = getCurrentItemIndex();
504
flags &= ~FLAG_TIP;
505
if (!isEnabled()) {
506
return 0;
507
}
508
if (target && target->tryHandle(this, FXSEL(SEL_KEYPRESS, message), ptr)) {
509
return 1;
510
}
511
switch (event->code) {
512
case KEY_Control_L:
513
case KEY_Control_R:
514
case KEY_Shift_L:
515
case KEY_Shift_R:
516
case KEY_Alt_L:
517
case KEY_Alt_R:
518
if (flags & FLAG_DODRAG) {
519
handle(this, FXSEL(SEL_DRAGGED, 0), ptr);
520
}
521
return 1;
522
case KEY_Page_Up:
523
case KEY_KP_Page_Up:
524
lookup = FXString::null;
525
setPosition(pos_x, pos_y + verticalScrollBar()->getPage());
526
return 1;
527
case KEY_Page_Down:
528
case KEY_KP_Page_Down:
529
lookup = FXString::null;
530
setPosition(pos_x, pos_y - verticalScrollBar()->getPage());
531
return 1;
532
case KEY_Up:
533
case KEY_KP_Up:
534
index -= 1;
535
goto hop;
536
case KEY_Down:
537
case KEY_KP_Down:
538
index += 1;
539
goto hop;
540
case KEY_Home:
541
case KEY_KP_Home:
542
index = 0;
543
goto hop;
544
case KEY_End:
545
case KEY_KP_End:
546
index = (int)itemFiltered.size() - 1;
547
hop:
548
lookup = FXString::null;
549
// continue depending of filter
550
if (filter.empty()) {
551
if (0 <= index && index < (int)items.size()) {
552
setCurrentItem(items[index], TRUE);
553
makeItemVisible(items[index]);
554
}
555
} else {
556
if ((0 <= index) && (index < (int)itemFiltered.size())) {
557
setCurrentItem(itemFiltered[index], TRUE);
558
makeItemVisible(itemFiltered[index]);
559
}
560
}
561
handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentItem);
562
if (currentItem && currentItem->isEnabled()) {
563
handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
564
}
565
return 1;
566
case KEY_space:
567
case KEY_KP_Space:
568
lookup = FXString::null;
569
if (currentItem && currentItem->isEnabled()) {
570
toggleItem(currentItem, TRUE);
571
setAnchorItem(currentItem);
572
}
573
handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentItem);
574
if (currentItem && currentItem->isEnabled()) {
575
handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
576
}
577
return 1;
578
case KEY_Return:
579
case KEY_KP_Enter:
580
lookup = FXString::null;
581
handle(this, FXSEL(SEL_DOUBLECLICKED, 0), (void*)currentItem);
582
if (currentItem && currentItem->isEnabled()) {
583
handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
584
}
585
return 1;
586
default:
587
return 1;
588
}
589
}
590
591
592
long
593
MFXListIcon::onKeyRelease(FXObject*, FXSelector, void* ptr) {
594
FXEvent* event = (FXEvent*)ptr;
595
if (!isEnabled()) {
596
return 0;
597
}
598
if (target && target->tryHandle(this, FXSEL(SEL_KEYRELEASE, message), ptr)) {
599
return 1;
600
}
601
switch (event->code) {
602
case KEY_Shift_L:
603
case KEY_Shift_R:
604
case KEY_Control_L:
605
case KEY_Control_R:
606
case KEY_Alt_L:
607
case KEY_Alt_R:
608
if (flags & FLAG_DODRAG) {
609
handle(this, FXSEL(SEL_DRAGGED, 0), ptr);
610
}
611
return 1;
612
}
613
return 0;
614
}
615
616
617
long
618
MFXListIcon::onAutoScroll(FXObject*, FXSelector, void*) {
619
return 1;
620
}
621
622
623
long
624
MFXListIcon::onMotion(FXObject*, FXSelector, void* ptr) {
625
FXEvent* event = (FXEvent*)ptr;
626
MFXListIconItem* oldcursor = cursor;
627
FXuint flg = flags;
628
629
// Kill the tip
630
flags &= ~FLAG_TIP;
631
632
// Kill the tip timer
633
getApp()->removeTimeout(this, ID_TIPTIMER);
634
635
// Right mouse scrolling
636
if (flags & FLAG_SCROLLING) {
637
setPosition(event->win_x - grabx, event->win_y - graby);
638
return 1;
639
}
640
641
// Drag and drop mode
642
if (flags & FLAG_DODRAG) {
643
if (startAutoScroll(event, TRUE)) {
644
return 1;
645
}
646
handle(this, FXSEL(SEL_DRAGGED, 0), ptr);
647
return 1;
648
}
649
650
// Tentative drag and drop
651
if ((flags & FLAG_TRYDRAG) && event->moved) {
652
flags &= ~FLAG_TRYDRAG;
653
if (handle(this, FXSEL(SEL_BEGINDRAG, 0), ptr)) {
654
flags |= FLAG_DODRAG;
655
}
656
return 1;
657
}
658
659
// Normal operation
660
if ((flags & FLAG_PRESSED) || (options & LIST_AUTOSELECT)) {
661
// Start auto scrolling?
662
if (startAutoScroll(event, FALSE)) {
663
return 1;
664
}
665
// Find item
666
auto element = getItemAt(event->win_y);
667
// Got an item different from before
668
if (element) {
669
// Make it the current item
670
setCurrentItem(element, TRUE);
671
return 1;
672
}
673
}
674
675
// Reset tip timer if nothing's going on
676
getApp()->addTimeout(this, ID_TIPTIMER, getApp()->getMenuPause());
677
678
// Get item we're over
679
cursor = getItemAt(event->win_y);
680
681
// Force GUI update only when needed
682
return (cursor != oldcursor) || (flg & FLAG_TIP);
683
}
684
685
686
long
687
MFXListIcon::onLeftBtnPress(FXObject*, FXSelector, void* ptr) {
688
FXEvent* event = (FXEvent*)ptr;
689
FXint code;
690
flags &= ~FLAG_TIP;
691
handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);
692
if (isEnabled()) {
693
grab();
694
flags &= ~FLAG_UPDATE;
695
// First change callback
696
if (target && target->tryHandle(this, FXSEL(SEL_LEFTBUTTONPRESS, message), ptr)) {
697
return 1;
698
}
699
// Autoselect mode
700
if (options & LIST_AUTOSELECT) {
701
return 1;
702
}
703
// Locate item
704
auto item = getItemAt(event->win_y);
705
// No item
706
if (item == nullptr) {
707
return 1;
708
}
709
// Find out where hit
710
code = hitItem(item, event->win_x, event->win_y);
711
// Change current item
712
setCurrentItem(item, TRUE);
713
// Change item selection
714
state = item->isSelected();
715
if (item->isEnabled() && !state) {
716
selectItem(item, TRUE);
717
}
718
// Start drag if actually pressed text or icon only
719
if (code && item->isSelected() && item->isDraggable()) {
720
flags |= FLAG_TRYDRAG;
721
}
722
flags |= FLAG_PRESSED;
723
return 1;
724
}
725
return 0;
726
}
727
728
729
long
730
MFXListIcon::onLeftBtnRelease(FXObject*, FXSelector, void* ptr) {
731
FXEvent* event = (FXEvent*)ptr;
732
FXuint flg = flags;
733
if (isEnabled()) {
734
ungrab();
735
stopAutoScroll();
736
flags |= FLAG_UPDATE;
737
flags &= ~(FLAG_PRESSED | FLAG_TRYDRAG | FLAG_DODRAG);
738
// First chance callback
739
if (target && target->tryHandle(this, FXSEL(SEL_LEFTBUTTONRELEASE, message), ptr)) {
740
return 1;
741
}
742
// No activity
743
if (!(flg & FLAG_PRESSED) && !(options & LIST_AUTOSELECT)) {
744
return 1;
745
}
746
// Was dragging
747
if (flg & FLAG_DODRAG) {
748
handle(this, FXSEL(SEL_ENDDRAG, 0), ptr);
749
return 1;
750
}
751
if (currentItem && currentItem->isEnabled()) {
752
if (state) {
753
deselectItem(currentItem, TRUE);
754
}
755
}
756
// Scroll to make item visibke
757
makeItemVisible(currentItem);
758
// Update anchor
759
setAnchorItem(currentItem);
760
// Generate clicked callbacks
761
if (event->click_count == 1) {
762
handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentItem);
763
} else if (event->click_count == 2) {
764
handle(this, FXSEL(SEL_DOUBLECLICKED, 0), (void*)currentItem);
765
} else if (event->click_count == 3) {
766
handle(this, FXSEL(SEL_TRIPLECLICKED, 0), (void*)currentItem);
767
}
768
// Command callback only when clicked on item
769
if (currentItem && currentItem->isEnabled()) {
770
handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
771
}
772
return 1;
773
}
774
return 0;
775
}
776
777
778
long
779
MFXListIcon::onRightBtnPress(FXObject*, FXSelector, void* ptr) {
780
FXEvent* event = (FXEvent*)ptr;
781
flags &= ~FLAG_TIP;
782
handle(this, FXSEL(SEL_FOCUS_SELF, 0), ptr);
783
if (isEnabled()) {
784
grab();
785
flags &= ~FLAG_UPDATE;
786
if (target && target->tryHandle(this, FXSEL(SEL_RIGHTBUTTONPRESS, message), ptr)) {
787
return 1;
788
}
789
flags |= FLAG_SCROLLING;
790
grabx = event->win_x - pos_x;
791
graby = event->win_y - pos_y;
792
return 1;
793
}
794
return 0;
795
}
796
797
798
long
799
MFXListIcon::onRightBtnRelease(FXObject*, FXSelector, void* ptr) {
800
if (isEnabled()) {
801
ungrab();
802
flags &= ~FLAG_SCROLLING;
803
flags |= FLAG_UPDATE;
804
if (target && target->tryHandle(this, FXSEL(SEL_RIGHTBUTTONRELEASE, message), ptr)) {
805
return 1;
806
}
807
return 1;
808
}
809
return 0;
810
}
811
812
813
long
814
MFXListIcon::onUngrabbed(FXObject* sender, FXSelector sel, void* ptr) {
815
FXScrollArea::onUngrabbed(sender, sel, ptr);
816
flags &= ~(FLAG_DODRAG | FLAG_TRYDRAG | FLAG_PRESSED | FLAG_CHANGED | FLAG_SCROLLING);
817
flags |= FLAG_UPDATE;
818
stopAutoScroll();
819
return 1;
820
}
821
822
823
long
824
MFXListIcon::onCommand(FXObject*, FXSelector, void* ptr) {
825
return target ? target->tryHandle(this, FXSEL(SEL_COMMAND, message), ptr) : 0;
826
}
827
828
829
long
830
MFXListIcon::onClicked(FXObject*, FXSelector, void* ptr) {
831
return target ? target->tryHandle(this, FXSEL(SEL_CLICKED, message), ptr) : 0;
832
}
833
834
835
long
836
MFXListIcon::onDoubleClicked(FXObject*, FXSelector, void* ptr) {
837
return target ? target->tryHandle(this, FXSEL(SEL_DOUBLECLICKED, message), ptr) : 0;
838
}
839
840
841
long
842
MFXListIcon::onTripleClicked(FXObject*, FXSelector, void* ptr) {
843
return target ? target->tryHandle(this, FXSEL(SEL_TRIPLECLICKED, message), ptr) : 0;
844
}
845
846
847
void
848
MFXListIcon::setCurrentItem(MFXListIconItem* item, FXbool notify) {
849
if (item) {
850
// Deactivate old item
851
if (currentItem) {
852
currentItem->setFocus(FALSE);
853
updateItem(currentItem);
854
}
855
currentItem = item;
856
// Activate new item
857
if (currentItem) {
858
currentItem->setFocus(TRUE);
859
updateItem(currentItem);
860
}
861
// Notify item change
862
if (notify && target) {
863
target->tryHandle(this, FXSEL(SEL_CHANGED, message), (void*)currentItem);
864
}
865
}
866
}
867
868
869
FXint
870
MFXListIcon::getCurrentItemIndex() const {
871
for (int i = 0; i < (int)items.size(); i++) {
872
if (items[i] == currentItem) {
873
return i;
874
}
875
}
876
return -1;
877
}
878
879
880
FXint
881
MFXListIcon::getViewableItem() const {
882
// continue depending if we're filtering
883
if (filter.empty()) {
884
for (int i = 0; i < (int)items.size(); i++) {
885
if (items[i] == viewable) {
886
return i;
887
}
888
}
889
} else {
890
for (int i = 0; i < (int)itemFiltered.size(); i++) {
891
if (itemFiltered[i] == viewable) {
892
return i;
893
}
894
}
895
}
896
return -1;
897
}
898
899
900
void
901
MFXListIcon::setAnchorItem(MFXListIconItem* item) {
902
int index = 0;
903
// continue depending if we're filtering
904
if (filter.empty()) {
905
for (int i = 0; i < (int)items.size(); i++) {
906
if (items[i] == item) {
907
index = i;
908
}
909
}
910
} else {
911
for (int i = 0; i < (int)itemFiltered.size(); i++) {
912
if (itemFiltered[i] == item) {
913
index = i;
914
}
915
}
916
}
917
anchor = index;
918
extent = index;
919
}
920
921
922
FXint
923
MFXListIcon::getAnchorItem() const {
924
return anchor;
925
}
926
927
928
MFXListIconItem*
929
MFXListIcon::getCursorItem() const {
930
return cursor;
931
}
932
933
934
MFXListIconItem*
935
MFXListIcon::getItem(FXint index) const {
936
if (index < 0 || (int)items.size() <= index) {
937
fxerror("%s::getItem: index out of range.\n", getClassName());
938
}
939
return items[index];
940
}
941
942
943
FXint
944
MFXListIcon::setItem(FXint index, MFXListIconItem* item, FXbool notify) {
945
// Must have item
946
if (!item) {
947
fxerror("%s::setItem: item is NULL.\n", getClassName());
948
}
949
// Must be in range
950
if (index < 0 || (int)items.size() <= index) {
951
fxerror("%s::setItem: index out of range.\n", getClassName());
952
}
953
// Notify item will be replaced
954
if (notify && target) {
955
target->tryHandle(this, FXSEL(SEL_REPLACED, message), (void*)(FXival)index);
956
}
957
// Copy the state over
958
item->state = items[index]->state;
959
// Delete old
960
delete items[index];
961
// Add new
962
items[index] = item;
963
// apply filter
964
setFilter(filter, nullptr);
965
return index;
966
}
967
968
969
FXint
970
MFXListIcon::editItem(FXint index, const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
971
return setItem(index, createItem(text, icon, ptr), notify);
972
}
973
974
975
FXint
976
MFXListIcon::insertItem(FXint index, MFXListIconItem* item, FXbool notify) {
977
MFXListIconItem* old = currentItem;
978
// Must have item
979
if (!item) {
980
fxerror("%s::insertItem: item is NULL.\n", getClassName());
981
}
982
// Must be in range
983
if (index < 0 || (int)items.size() < index) {
984
fxerror("%s::insertItem: index out of range.\n", getClassName());
985
}
986
// Add item to list
987
items.insert(items.begin() + index, item);
988
// Adjust indices
989
if (anchor >= index) {
990
anchor++;
991
}
992
if (extent >= index) {
993
extent++;
994
}
995
if (getCurrentItemIndex() >= index) {
996
currentItem = items[index];
997
}
998
if (getViewableItem() >= index) {
999
viewable = items[index];
1000
}
1001
if ((currentItem == nullptr) && ((int)items.size() == 1)) {
1002
currentItem = items[0];
1003
}
1004
// Notify item has been inserted
1005
if (notify && target) {
1006
target->tryHandle(this, FXSEL(SEL_INSERTED, message), (void*)(FXival)index);
1007
}
1008
// Current item may have changed
1009
if (old != currentItem) {
1010
if (notify && target) {
1011
target->tryHandle(this, FXSEL(SEL_CHANGED, message), (void*)currentItem);
1012
}
1013
}
1014
// Was new item
1015
if (currentItem && currentItem == items[index]) {
1016
if (hasFocus()) {
1017
currentItem->setFocus(TRUE);
1018
}
1019
}
1020
// apply filter
1021
setFilter(filter, nullptr);
1022
return index;
1023
}
1024
1025
1026
FXint
1027
MFXListIcon::insertItem(FXint index, const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
1028
return insertItem(index, createItem(text, icon, ptr), notify);
1029
}
1030
1031
1032
FXint
1033
MFXListIcon::appendItem(MFXListIconItem* item, FXbool notify) {
1034
return insertItem((int)items.size(), item, notify);
1035
}
1036
1037
1038
FXint
1039
MFXListIcon::appendItem(const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
1040
return insertItem((int)items.size(), createItem(text, icon, ptr), notify);
1041
}
1042
1043
1044
void
1045
MFXListIcon::removeItem(FXint index, FXbool notify) {
1046
MFXListIconItem* old = currentItem;
1047
// Must be in range
1048
if ((index < 0) || ((int)items.size() <= index)) {
1049
fxerror("%s::removeItem: index out of range.\n", getClassName());
1050
}
1051
// Notify item will be deleted
1052
if (notify && target) {
1053
target->tryHandle(this, FXSEL(SEL_DELETED, message), (void*)(FXival)index);
1054
}
1055
// Delete item
1056
delete items[index];
1057
// Remove item from list
1058
items.erase(items.begin() + index);
1059
// Adjust indices
1060
if (anchor >= index) {
1061
anchor++;
1062
}
1063
if (extent >= index) {
1064
extent++;
1065
}
1066
if (getCurrentItemIndex() >= index) {
1067
currentItem = items[index];
1068
}
1069
if (getViewableItem() >= index) {
1070
viewable = items[index];
1071
}
1072
if ((currentItem == nullptr) && ((int)items.size() == 1)) {
1073
currentItem = items[0];
1074
}
1075
// Notify item has been inserted
1076
if (notify && target) {
1077
target->tryHandle(this, FXSEL(SEL_INSERTED, message), (void*)(FXival)index);
1078
}
1079
// Current item may have changed
1080
if (old != currentItem) {
1081
if (notify && target) {
1082
target->tryHandle(this, FXSEL(SEL_CHANGED, message), (void*)currentItem);
1083
}
1084
}
1085
// Was new item
1086
if (currentItem && currentItem == items[index]) {
1087
if (hasFocus()) {
1088
currentItem->setFocus(TRUE);
1089
}
1090
}
1091
// apply filter
1092
setFilter(filter, nullptr);
1093
}
1094
1095
1096
void
1097
MFXListIcon::clearItems(FXbool notify) {
1098
// Delete items
1099
for (FXint index = (int)items.size() - 1; 0 <= index; index--) {
1100
if (notify && target) {
1101
target->tryHandle(this, FXSEL(SEL_DELETED, message), (void*)(FXival)index);
1102
}
1103
delete items[index];
1104
}
1105
// Free array
1106
items.clear();
1107
// Adjust indices
1108
anchor = -1;
1109
extent = -1;
1110
// Current item has changed
1111
if (currentItem) {
1112
if (notify && target) {
1113
target->tryHandle(this, FXSEL(SEL_CHANGED, message), (void*)(FXival) - 1);
1114
}
1115
currentItem = nullptr;
1116
}
1117
viewable = nullptr;
1118
// apply filter
1119
setFilter(filter, nullptr);
1120
}
1121
1122
1123
void
1124
MFXListIcon::setFilter(const FXString& value, FXLabel* label) {
1125
filter = value;
1126
// update item filtered
1127
itemFiltered.clear();
1128
for (int i = 0; i < (int)items.size(); i++) {
1129
items[i]->show = showItem(items[i]->getText());
1130
if (items[i]->show) {
1131
itemFiltered.push_back(items[i]);
1132
}
1133
}
1134
// check if show label
1135
if (label) {
1136
if (!value.empty() && ((int)itemFiltered.size() == 0)) {
1137
label->show();
1138
} else {
1139
label->hide();
1140
}
1141
}
1142
// recompute and recalc
1143
recompute();
1144
recalc();
1145
}
1146
1147
1148
void
1149
MFXListIcon::setTextColor(FXColor clr) {
1150
if (textColor != clr) {
1151
textColor = clr;
1152
update();
1153
}
1154
}
1155
1156
1157
void
1158
MFXListIcon::setHelpText(const FXString& text) {
1159
help = text;
1160
}
1161
1162
1163
FXString
1164
MFXListIcon::tolowerString(const FXString& str) const {
1165
FXString result;
1166
for (int i = 0; i < str.count(); i++) {
1167
result.append((char)::tolower(str[i]));
1168
}
1169
return result;
1170
}
1171
1172
1173
MFXListIcon::MFXListIcon() {
1174
flags |= FLAG_ENABLED;
1175
font = (FXFont*) - 1L;
1176
}
1177
1178
1179
void
1180
MFXListIcon::recompute() {
1181
FXint x, y, w, h;
1182
x = 0;
1183
y = 0;
1184
listWidth = 0;
1185
listHeight = 0;
1186
for (auto& item : itemFiltered) {
1187
// set position and size
1188
item->x = x;
1189
item->y = y;
1190
w = item->getWidth(this);
1191
h = item->getHeight(this);
1192
if (w > listWidth) {
1193
listWidth = w;
1194
}
1195
y += h;
1196
}
1197
listHeight = y;
1198
flags &= ~(FXuint)FLAG_RECALC;
1199
}
1200
1201
1202
MFXListIconItem*
1203
MFXListIcon::createItem(const FXString& text, FXIcon* icon, void* ptr) {
1204
return new MFXListIconItem(text, icon, 0, ptr);
1205
}
1206
1207
1208
bool
1209
MFXListIcon::showItem(const FXString& itemName) const {
1210
if (filter.empty()) {
1211
return true;
1212
} else {
1213
return tolowerString(itemName).find(tolowerString(filter)) != -1;
1214
}
1215
}
1216
1217