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