Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
MFXListIcon.cpp
Go to the documentation of this file.
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/****************************************************************************/
18//
19/****************************************************************************/
20
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
41FXDEFMAP(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),
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
68FXIMPLEMENT(MFXListIcon, FXScrollArea, MFXListIconMap, ARRAYNUMBER(MFXListIconMap))
69
70// ===========================================================================
71// member method definitions
72// ===========================================================================
73
74MFXListIcon::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
87 getApp()->removeTimeout(this, ID_TIPTIMER);
88 getApp()->removeTimeout(this, ID_LOOKUPTIMER);
89 clearItems(FALSE);
90 font = (FXFont*) - 1L;
91}
92
93
94void
96 FXScrollArea::create();
97 for (const auto& item : items) {
98 item->create();
99 }
100 font->create();
101}
102
103
104void
106 FXScrollArea::detach();
107 for (const auto& item : items) {
108 item->detach();
109 }
110 font->detach();
111}
112
113
114bool
116 return true;
117}
118
119
120void
122 FXScrollArea::setFocus();
123 setDefault(TRUE);
124}
125
126
127void
129 FXScrollArea::killFocus();
130 setDefault(MAYBE);
131}
132
133
134FXint
136 return FXScrollArea::getDefaultWidth();
137}
138
139
140FXint
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
150void
152 FXScrollArea::recalc();
153 flags |= FLAG_RECALC;
154 cursor = nullptr;
155}
156
157
158void
160 if (nvis < 0) {
161 nvis = 0;
162 }
163 if (visible != nvis) {
164 visible = nvis;
165 recalc();
166 }
167}
168
169
170FXint
172 if (flags & FLAG_RECALC) {
173 recompute();
174 }
175 return listWidth;
176}
177
178
179FXint
181 if (flags & FLAG_RECALC) {
182 recompute();
183 }
184 return listHeight;
185}
186
187
188void
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) {
201 }
202 // No more dirty
203 flags &= ~(FXuint)FLAG_DIRTY;
204}
205
206
207FXbool
208MFXListIcon::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
218FXbool
220 return (0 < (pos_y + item->y + item->getHeight(this))) && ((pos_y + item->y) < viewport_h);
221}
222
223
224void
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
251void
253 makeItemVisible(items[index]);
254}
255
256
257FXint
258MFXListIcon::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
266FXint
267MFXListIcon::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
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
296int
297MFXListIcon::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
307FXint
308MFXListIcon::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
321void
323 update(0, pos_y + item->y, viewport_w, item->getHeight(this));
324}
325
326
327FXbool
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
343FXbool
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
358FXbool
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
378FXbool
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
396long
397MFXListIcon::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
405long
406MFXListIcon::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
414long
415MFXListIcon::onFocusIn(FXObject* sender, FXSelector sel, void* ptr) {
416 FXScrollArea::onFocusIn(sender, sel, ptr);
417 if (currentItem) {
418 currentItem->setFocus(TRUE);
420 }
421 return 1;
422}
423
424
425long
426MFXListIcon::onTipTimer(FXObject*, FXSelector, void*) {
427 flags |= FLAG_TIP;
428 return 1;
429}
430
431
432long
433MFXListIcon::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
446long
447MFXListIcon::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
459long
460MFXListIcon::onFocusOut(FXObject* sender, FXSelector sel, void* ptr) {
461 FXScrollArea::onFocusOut(sender, sel, ptr);
462 if (currentItem) {
463 currentItem->setFocus(FALSE);
465 }
466 return 1;
467}
468
469
470long
471MFXListIcon::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
493long
494MFXListIcon::onLookupTimer(FXObject*, FXSelector, void*) {
495 lookup = FXString::null;
496 return 1;
497}
498
499
500long
501MFXListIcon::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;
547hop:
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);
559 }
560 }
561 handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentItem);
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;
570 toggleItem(currentItem, TRUE);
572 }
573 handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentItem);
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);
583 handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
584 }
585 return 1;
586 default:
587 return 1;
588 }
589}
590
591
592long
593MFXListIcon::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
617long
618MFXListIcon::onAutoScroll(FXObject*, FXSelector, void*) {
619 return 1;
620}
621
622
623long
624MFXListIcon::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
686long
687MFXListIcon::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
729long
730MFXListIcon::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 }
752 if (state) {
754 }
755 }
756 // Scroll to make item visibke
758 // Update anchor
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
770 handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
771 }
772 return 1;
773 }
774 return 0;
775}
776
777
778long
779MFXListIcon::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
798long
799MFXListIcon::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
813long
814MFXListIcon::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
823long
824MFXListIcon::onCommand(FXObject*, FXSelector, void* ptr) {
825 return target ? target->tryHandle(this, FXSEL(SEL_COMMAND, message), ptr) : 0;
826}
827
828
829long
830MFXListIcon::onClicked(FXObject*, FXSelector, void* ptr) {
831 return target ? target->tryHandle(this, FXSEL(SEL_CLICKED, message), ptr) : 0;
832}
833
834
835long
836MFXListIcon::onDoubleClicked(FXObject*, FXSelector, void* ptr) {
837 return target ? target->tryHandle(this, FXSEL(SEL_DOUBLECLICKED, message), ptr) : 0;
838}
839
840
841long
842MFXListIcon::onTripleClicked(FXObject*, FXSelector, void* ptr) {
843 return target ? target->tryHandle(this, FXSEL(SEL_TRIPLECLICKED, message), ptr) : 0;
844}
845
846
847void
849 if (item) {
850 // Deactivate old item
851 if (currentItem) {
852 currentItem->setFocus(FALSE);
854 }
855 currentItem = item;
856 // Activate new item
857 if (currentItem) {
858 currentItem->setFocus(TRUE);
860 }
861 // Notify item change
862 if (notify && target) {
863 target->tryHandle(this, FXSEL(SEL_CHANGED, message), (void*)currentItem);
864 }
865 }
866}
867
868
869FXint
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
880FXint
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
900void
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
922FXint
924 return anchor;
925}
926
927
930 return cursor;
931}
932
933
935MFXListIcon::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
943FXint
944MFXListIcon::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
969FXint
970MFXListIcon::editItem(FXint index, const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
971 return setItem(index, createItem(text, icon, ptr), notify);
972}
973
974
975FXint
976MFXListIcon::insertItem(FXint index, MFXListIconItem* item, FXbool notify) {
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
1026FXint
1027MFXListIcon::insertItem(FXint index, const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
1028 return insertItem(index, createItem(text, icon, ptr), notify);
1029}
1030
1031
1032FXint
1034 return insertItem((int)items.size(), item, notify);
1035}
1036
1037
1038FXint
1039MFXListIcon::appendItem(const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
1040 return insertItem((int)items.size(), createItem(text, icon, ptr), notify);
1041}
1042
1043
1044void
1045MFXListIcon::removeItem(FXint index, FXbool notify) {
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
1096void
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
1123void
1124MFXListIcon::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
1148void
1150 if (textColor != clr) {
1151 textColor = clr;
1152 update();
1153 }
1154}
1155
1156
1157void
1158MFXListIcon::setHelpText(const FXString& text) {
1159 help = text;
1160}
1161
1162
1163FXString
1164MFXListIcon::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
1174 flags |= FLAG_ENABLED;
1175 font = (FXFont*) - 1L;
1176}
1177
1178
1179void
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
1203MFXListIcon::createItem(const FXString& text, FXIcon* icon, void* ptr) {
1204 return new MFXListIconItem(text, icon, 0, ptr);
1205}
1206
1207
1208bool
1209MFXListIcon::showItem(const FXString& itemName) const {
1210 if (filter.empty()) {
1211 return true;
1212 } else {
1213 return tolowerString(itemName).find(tolowerString(filter)) != -1;
1214 }
1215}
#define ICON_SIZE
#define LINE_SPACING
FXDEFMAP(MFXListIcon) MFXListIconMap[]
A list item which allows for custom coloring.
Definition MFXListIcon.h:38
long onDoubleClicked(FXObject *, FXSelector, void *)
FXint listWidth
List width.
FXint getDefaultWidth()
Return default width.
void recalc()
Recalculate layout.
long onKeyRelease(FXObject *, FXSelector, void *)
long onRightBtnPress(FXObject *, FXSelector, void *)
long onCommand(FXObject *, FXSelector, void *)
FXint setItem(FXint index, MFXListIconItem *item, FXbool notify=FALSE)
Replace the item with a [possibly subclassed] item.
long onQueryTip(FXObject *, FXSelector, void *)
void setNumVisible(FXint nvis)
Change the number of visible items.
void makeItemVisible(MFXListIconItem *item)
Scroll to bring item into view.
long onAutoScroll(FXObject *, FXSelector, void *)
FXbool state
State of item.
long onClicked(FXObject *, FXSelector, void *)
FXint getDefaultHeight()
Return default height.
long onTripleClicked(FXObject *, FXSelector, void *)
FXint editItem(FXint index, const FXString &text, FXIcon *icon=NULL, void *ptr=NULL, FXbool notify=FALSE)
Replace items text, icon, and user-data pointer.
int findItem(const FXString &text) const
Search items by name (In all items)
MFXListIconItem * getCursorItem() const
Get item under the cursor, if any.
void updateItem(MFXListIconItem *item) const
Repaint item.
FXint getItemHeight(FXint index) const
Return item height.
FXString tolowerString(const FXString &str) const
tolower string
FXint getContentWidth()
Compute and return content width.
MFXListIconItem * cursor
Cursor item.
FXbool selectItem(MFXListIconItem *item, FXbool notify=FALSE)
Select item by index.
FXbool killSelection(FXbool notify=FALSE)
Deselect all items.
FXint appendItem(MFXListIconItem *item, FXbool notify=FALSE)
Append a [possibly subclassed] item to the list.
FXint getItemWidth(FXint index) const
Return item width.
std::vector< MFXListIconItem * > itemFiltered
Selected item list.
long onUngrabbed(FXObject *, FXSelector, void *)
bool canFocus() const
List widget can receive focus.
MFXListIconItem * createItem(const FXString &text, FXIcon *icon, void *ptr)
create item
void setFocus()
Move the focus to this window.
FXFont * font
Font.
FXbool isItemCurrent(FXint index) const
Return TRUE if item is current.
void killFocus()
Remove the focus from this window.
long onLeftBtnPress(FXObject *, FXSelector, void *)
FXint hitItem(MFXListIconItem *item, FXint x, FXint y) const
Return item hit code: 0 no hit; 1 hit the icon; 2 hit the text.
long onEnter(FXObject *, FXSelector, void *)
FXint getCurrentItemIndex() const
Return current item, if any.
bool showItem(const FXString &itemName) const
check if filter element
long onLookupTimer(FXObject *, FXSelector, void *)
FXint grabx
Grab point x.
FXint getContentHeight()
Return content height.
long onLeave(FXObject *, FXSelector, void *)
FXString help
Help text.
long onTipTimer(FXObject *, FXSelector, void *)
FXint listHeight
List height.
FXint getAnchorItem() const
Return anchor item, if any.
MFXListIconItem * currentItem
Current item.
void recompute()
recompute list
FXint visible
Number of rows high.
void setHelpText(const FXString &text)
Set the status line help text for this list.
FXint extent
Extent item.
FXString lookup
Lookup string.
FXbool toggleItem(MFXListIconItem *item, FXbool notify=FALSE)
Toggle item selection state.
MFXListIconItem * viewable
Viewable item.
void setAnchorItem(MFXListIconItem *item)
Change anchor item.
void setFilter(const FXString &value, FXLabel *label)
filter items in list
MFXListIconItem * getItem(FXint index) const
Return the item at the given index.
FXbool deselectItem(MFXListIconItem *item, FXbool notify=FALSE)
Deselect item.
MFXListIcon()
FOX need this.
std::vector< MFXListIconItem * > items
Item list.
FXString filter
filter
void detach()
Detach server-side resources.
long onPaint(FXObject *, FXSelector, void *)
long onRightBtnRelease(FXObject *, FXSelector, void *)
void setTextColor(FXColor clr)
Change normal text color.
void layout()
Perform layout.
~MFXListIcon()
Destructor.
long onKeyPress(FXObject *, FXSelector, void *)
void clearItems(FXbool notify=FALSE)
Remove all items from list.
FXbool isItemVisible(MFXListIconItem *item) const
Return TRUE if item is visible.
long onFocusOut(FXObject *, FXSelector, void *)
void removeItem(FXint index, FXbool notify=FALSE)
Remove node from list.
FXint anchor
Anchor item.
MFXListIconItem * getItemAt(FXint y) const
Return index of item at y, if any.
long onQueryHelp(FXObject *, FXSelector, void *)
FXint graby
Grab point y.
void create()
Create server-side resources.
void setCurrentItem(MFXListIconItem *item, FXbool notify=FALSE)
Change current item.
long onLeftBtnRelease(FXObject *, FXSelector, void *)
FXint getViewableItem() const
Return viewable item, if any.
FXint insertItem(FXint index, MFXListIconItem *item, FXbool notify=FALSE)
Insert a new [possibly subclassed] item at the give index.
long onMotion(FXObject *, FXSelector, void *)
long onFocusIn(FXObject *, FXSelector, void *)
FXColor textColor
Text color.
FXbool isEnabled() const
Return true if this item is enabled.
FXint getHeight(const MFXListIcon *list) const
Return height of item as drawn in list.
void setSelected(FXbool selected)
Select item.
const FXString & getText() const
Return item's text label.
FXint x
position
FXint hitItem(const MFXListIcon *list, FXint x, FXint y) const
hit item
FXbool isSelected() const
Return true if this item is selected.
void setFocus(FXbool focus)
Make item draw as focused.