Eclipse SUMO - Simulation of Urban MObility
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
44 FXDEFMAP(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
71 FXIMPLEMENT(MFXListIcon, FXScrollArea, MFXListIconMap, ARRAYNUMBER(MFXListIconMap))
72 
73 // ===========================================================================
74 // member method definitions
75 // ===========================================================================
76 
77 MFXListIcon::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 
97 void
99  FXScrollArea::create();
100  for (const auto& item : items) {
101  item->create();
102  }
103  font->create();
104 }
105 
106 
107 void
109  FXScrollArea::detach();
110  for (const auto& item : items) {
111  item->detach();
112  }
113  font->detach();
114 }
115 
116 
117 bool
119  return true;
120 }
121 
122 
123 void
125  FXScrollArea::setFocus();
126  setDefault(TRUE);
127 }
128 
129 
130 void
132  FXScrollArea::killFocus();
133  setDefault(MAYBE);
134 }
135 
136 
137 FXint
139  return FXScrollArea::getDefaultWidth();
140 }
141 
142 
143 FXint
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 
153 void
155  FXScrollArea::recalc();
156  flags |= FLAG_RECALC;
157  cursor = nullptr;
158 }
159 
160 
161 void
163  if (nvis < 0) {
164  nvis = 0;
165  }
166  if (visible != nvis) {
167  visible = nvis;
168  recalc();
169  }
170 }
171 
172 
173 FXint
175  if (flags & FLAG_RECALC) {
176  recompute();
177  }
178  return listWidth;
179 }
180 
181 
182 FXint
184  if (flags & FLAG_RECALC) {
185  recompute();
186  }
187  return listHeight;
188 }
189 
190 
191 void
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 
210 FXbool
211 MFXListIcon::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 
221 FXbool
223  return (0 < (pos_y + item->y + item->getHeight(this))) && ((pos_y + item->y) < viewport_h);
224 }
225 
226 
227 void
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 
254 void
256  makeItemVisible(items[index]);
257 }
258 
259 
260 FXint
261 MFXListIcon::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 
269 FXint
270 MFXListIcon::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 
279 MFXListIcon::getItemAt(FXint y) const {
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 
299 int
300 MFXListIcon::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 
310 FXint
311 MFXListIcon::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 
324 void
326  update(0, pos_y + item->y, viewport_w, item->getHeight(this));
327 }
328 
329 
330 FXbool
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 
346 FXbool
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 
361 FXbool
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 
381 FXbool
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 
399 long
400 MFXListIcon::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 
408 long
409 MFXListIcon::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 
417 long
418 MFXListIcon::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 
428 long
429 MFXListIcon::onTipTimer(FXObject*, FXSelector, void*) {
430  flags |= FLAG_TIP;
431  return 1;
432 }
433 
434 
435 long
436 MFXListIcon::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 
449 long
450 MFXListIcon::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 
462 long
463 MFXListIcon::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 
473 long
474 MFXListIcon::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 
496 long
497 MFXListIcon::onLookupTimer(FXObject*, FXSelector, void*) {
498  lookup = FXString::null;
499  return 1;
500 }
501 
502 
503 long
504 MFXListIcon::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;
550 hop:
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);
565  if (currentItem && currentItem->isEnabled()) {
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;
572  if (currentItem && currentItem->isEnabled()) {
573  toggleItem(currentItem, TRUE);
575  }
576  handle(this, FXSEL(SEL_CLICKED, 0), (void*)currentItem);
577  if (currentItem && currentItem->isEnabled()) {
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);
585  if (currentItem && currentItem->isEnabled()) {
586  handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
587  }
588  return 1;
589  default:
590  return 1;
591  }
592 }
593 
594 
595 long
596 MFXListIcon::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 
620 long
621 MFXListIcon::onAutoScroll(FXObject*, FXSelector, void*) {
622  return 1;
623 }
624 
625 
626 long
627 MFXListIcon::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 
689 long
690 MFXListIcon::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 
732 long
733 MFXListIcon::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  }
754  if (currentItem && currentItem->isEnabled()) {
755  if (state) {
756  deselectItem(currentItem, TRUE);
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
772  if (currentItem && currentItem->isEnabled()) {
773  handle(this, FXSEL(SEL_COMMAND, 0), (void*)currentItem);
774  }
775  return 1;
776  }
777  return 0;
778 }
779 
780 
781 long
782 MFXListIcon::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 
801 long
802 MFXListIcon::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 
816 long
817 MFXListIcon::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 
826 long
827 MFXListIcon::onCommand(FXObject*, FXSelector, void* ptr) {
828  return target ? target->tryHandle(this, FXSEL(SEL_COMMAND, message), ptr) : 0;
829 }
830 
831 
832 long
833 MFXListIcon::onClicked(FXObject*, FXSelector, void* ptr) {
834  return target ? target->tryHandle(this, FXSEL(SEL_CLICKED, message), ptr) : 0;
835 }
836 
837 
838 long
839 MFXListIcon::onDoubleClicked(FXObject*, FXSelector, void* ptr) {
840  return target ? target->tryHandle(this, FXSEL(SEL_DOUBLECLICKED, message), ptr) : 0;
841 }
842 
843 
844 long
845 MFXListIcon::onTripleClicked(FXObject*, FXSelector, void* ptr) {
846  return target ? target->tryHandle(this, FXSEL(SEL_TRIPLECLICKED, message), ptr) : 0;
847 }
848 
849 
850 void
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 
872 FXint
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 
883 FXint
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 
903 void
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 
925 FXint
927  return anchor;
928 }
929 
930 
933  return cursor;
934 }
935 
936 
938 MFXListIcon::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 
946 FXint
947 MFXListIcon::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 
972 FXint
973 MFXListIcon::editItem(FXint index, const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
974  return setItem(index, createItem(text, icon, ptr), notify);
975 }
976 
977 
978 FXint
979 MFXListIcon::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 
1029 FXint
1030 MFXListIcon::insertItem(FXint index, const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
1031  return insertItem(index, createItem(text, icon, ptr), notify);
1032 }
1033 
1034 
1035 FXint
1037  return insertItem((int)items.size(), item, notify);
1038 }
1039 
1040 
1041 FXint
1042 MFXListIcon::appendItem(const FXString& text, FXIcon* icon, void* ptr, FXbool notify) {
1043  return insertItem((int)items.size(), createItem(text, icon, ptr), notify);
1044 }
1045 
1046 
1047 void
1048 MFXListIcon::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 
1099 void
1100 MFXListIcon::clearItems(FXbool notify) {
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 
1126 void
1127 MFXListIcon::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 
1151 void
1153  if (textColor != clr) {
1154  textColor = clr;
1155  update();
1156  }
1157 }
1158 
1159 
1160 void
1161 MFXListIcon::setHelpText(const FXString& text) {
1162  help = text;
1163 }
1164 
1165 
1166 FXString
1167 MFXListIcon::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 
1182 void
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 
1206 MFXListIcon::createItem(const FXString& text, FXIcon* icon, void* ptr) {
1207  return new MFXListIconItem(text, icon, 0, ptr);
1208 }
1209 
1210 
1211 bool
1212 MFXListIcon::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 LINE_SPACING
Definition: MFXListIcon.cpp:34
FXDEFMAP(MFXListIcon) MFXListIconMap[]
#define ICON_SIZE
Definition: MFXListIcon.cpp:35
A list item which allows for custom coloring.
Definition: MFXListIcon.h:30
long onDoubleClicked(FXObject *, FXSelector, void *)
FXint listWidth
List width.
Definition: MFXListIcon.h:286
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.
Definition: MFXListIcon.h:307
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.
Definition: MFXListIcon.h:268
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.
Definition: MFXListIcon.h:256
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.
Definition: MFXListIcon.h:274
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.
Definition: MFXListIcon.h:298
FXint getContentHeight()
Return content height.
long onLeave(FXObject *, FXSelector, void *)
FXString help
Help text.
Definition: MFXListIcon.h:295
long onTipTimer(FXObject *, FXSelector, void *)
FXint listHeight
List height.
Definition: MFXListIcon.h:289
FXint getAnchorItem() const
Return anchor item, if any.
MFXListIconItem * currentItem
Current item.
Definition: MFXListIcon.h:262
void recompute()
recompute list
FXint visible
Number of rows high.
Definition: MFXListIcon.h:292
void setHelpText(const FXString &text)
Set the status line help text for this list.
FXint extent
Extent item.
Definition: MFXListIcon.h:265
FXString lookup
Lookup string.
Definition: MFXListIcon.h:304
FXbool toggleItem(MFXListIconItem *item, FXbool notify=FALSE)
Toggle item selection state.
MFXListIconItem * viewable
Viewable item.
Definition: MFXListIcon.h:271
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.
Definition: MFXListIcon.h:253
FXString filter
filter
Definition: MFXListIcon.h:310
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.
Definition: MFXListIcon.cpp:89
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.
Definition: MFXListIcon.h:259
MFXListIconItem * getItemAt(FXint y) const
Return index of item at y, if any.
long onQueryHelp(FXObject *, FXSelector, void *)
FXint graby
Grab point y.
Definition: MFXListIcon.h:301
void create()
Create server-side resources.
Definition: MFXListIcon.cpp:98
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.
Definition: MFXListIcon.h:277
FXuint state
state
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.