1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 29 апр. 2018 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <ui/tk/tk.h>
23 
24 namespace lsp
25 {
26     namespace tk
27     {
28 
29         const w_class_t LSPComboGroup::metadata = { "LSPComboGroup", &LSPWidgetContainer::metadata };
30 
31         //---------------------------------------------------------------------
LSPComboList(LSPDisplay * dpy,LSPComboGroup * widget)32         LSPComboGroup::LSPComboList::LSPComboList(LSPDisplay *dpy, LSPComboGroup *widget): LSPListBox(dpy)
33         {
34             pWidget = widget;
35         }
36 
~LSPComboList()37         LSPComboGroup::LSPComboList::~LSPComboList()
38         {
39         }
40 
on_selection_change()41         void LSPComboGroup::LSPComboList::on_selection_change()
42         {
43             LSPListBox::on_selection_change();
44             pWidget->on_selection_change();
45         }
46 
on_item_change(ssize_t index,LSPItem * item)47         void LSPComboGroup::LSPComboList::on_item_change(ssize_t index, LSPItem *item)
48         {
49             LSPListBox::on_item_change(index, item);
50             pWidget->on_item_change(index, item);
51         }
52 
on_item_add(size_t index)53         void LSPComboGroup::LSPComboList::on_item_add(size_t index)
54         {
55             LSPListBox::on_item_add(index);
56             pWidget->on_item_add(index);
57         }
58 
on_item_remove(size_t index)59         void LSPComboGroup::LSPComboList::on_item_remove(size_t index)
60         {
61             LSPListBox::on_item_remove(index);
62             pWidget->on_item_remove(index);
63         }
64 
on_item_swap(size_t idx1,size_t idx2)65         void LSPComboGroup::LSPComboList::on_item_swap(size_t idx1, size_t idx2)
66         {
67             LSPListBox::on_item_swap(idx1, idx2);
68             pWidget->on_item_swap(idx1, idx2);
69         }
70 
on_item_clear()71         void LSPComboGroup::LSPComboList::on_item_clear()
72         {
73             LSPListBox::on_item_clear();
74             pWidget->on_item_clear();
75         }
76 
77         //---------------------------------------------------------------------
LSPComboPopup(LSPDisplay * dpy,LSPComboGroup * widget,ssize_t screen)78         LSPComboGroup::LSPComboPopup::LSPComboPopup(LSPDisplay *dpy, LSPComboGroup *widget, ssize_t screen):
79             LSPWindow(dpy, NULL, screen)
80         {
81             pWidget = widget;
82         }
83 
~LSPComboPopup()84         LSPComboGroup::LSPComboPopup::~LSPComboPopup()
85         {
86         }
87 
handle_event(const ws_event_t * e)88         status_t LSPComboGroup::LSPComboPopup::handle_event(const ws_event_t *e)
89         {
90             switch (e->nType)
91             {
92                 case UIE_KEY_DOWN:
93                     pWidget->on_grab_key_down(e);
94                     break;
95             }
96             return LSPWindow::handle_event(e);
97         }
98 
99         //---------------------------------------------------------------------
LSPComboGroup(LSPDisplay * dpy)100         LSPComboGroup::LSPComboGroup(LSPDisplay *dpy):
101             LSPWidgetContainer(dpy),
102             sColor(this),
103             sListBox(dpy, this),
104             sFont(this)
105         {
106             nRadius     = 10;
107             nBorder     = 0;
108             nCBFlags    = 0;
109             nMFlags     = 0;
110             pPopup      = NULL;
111             bEmbed      = false;
112 
113             sGroupHdr.nLeft     = 0;
114             sGroupHdr.nTop      = 0;
115             sGroupHdr.nWidth    = 0;
116             sGroupHdr.nHeight   = 0;
117 
118             pClass      = &metadata;
119         }
120 
~LSPComboGroup()121         LSPComboGroup::~LSPComboGroup()
122         {
123             do_destroy();
124         }
125 
init()126         status_t LSPComboGroup::init()
127         {
128             status_t result = LSPWidgetContainer::init();
129             if (result != STATUS_OK)
130                 return result;
131 
132             // Init list box
133             result = sListBox.init();
134             if (result != STATUS_OK)
135                 return result;
136 
137             sFont.init();
138             sFont.set_size(12.0f);
139             init_color(C_LABEL_TEXT, &sColor);
140             init_color(C_BACKGROUND, sFont.color());
141 
142             // Bind slots
143             ui_handler_id_t id = 0;
144             id = sSlots.add(LSPSLOT_CHANGE, slot_on_change, self());
145             if (id >= 0) id = sSlots.add(LSPSLOT_SUBMIT, slot_on_submit, self());
146             if (id >= 0) id = sListBox.slots()->bind(LSPSLOT_CHANGE, slot_on_list_change, self());
147             if (id >= 0) id = sListBox.slots()->bind(LSPSLOT_SUBMIT, slot_on_list_submit, self());
148 
149             return (id >= 0) ? STATUS_OK : -id;
150         }
151 
destroy()152         void LSPComboGroup::destroy()
153         {
154             do_destroy();
155             LSPWidgetContainer::destroy();
156         }
157 
current_widget()158         LSPWidget *LSPComboGroup::current_widget()
159         {
160             ssize_t idx = sListBox.selection()->value();
161             if (idx >= ssize_t(vWidgets.size()))
162                 idx = vWidgets.size() - 1;
163             LSPWidget *w = vWidgets.get(idx);
164             return ((w == NULL) || (w->invisible())) ? NULL : w;
165         }
166 
find_widget(ssize_t x,ssize_t y)167         LSPWidget *LSPComboGroup::find_widget(ssize_t x, ssize_t y)
168         {
169             LSPWidget *curr = current_widget();
170             return ((curr != NULL) && (curr->inside(x, y))) ? curr : NULL;
171         }
172 
query_dimensions(dimensions_t * d)173         void LSPComboGroup::query_dimensions(dimensions_t *d)
174         {
175             size_t bw       = (bEmbed) ? 1 : ::round(nRadius * M_SQRT2 * 0.5) + 1;
176             size_t dd       = bw + nBorder + 1;
177             d->nGapLeft     = dd;
178             d->nGapRight    = dd;
179             d->nGapTop      = dd;
180             d->nGapBottom   = dd;
181             d->nMinWidth    = nBorder*2;
182             d->nMinHeight   = nBorder*2;
183 
184             LSPString text;
185             const LSPLocalString *lctext = this->text();
186             if (lctext != NULL)
187                 lctext->format(&text, this);
188             if (text.length() > 0)
189             {
190                 // Create temporary surface
191                 ISurface *s = (pDisplay != NULL) ? pDisplay->create_surface(1, 1) : NULL;
192                 if (s == NULL)
193                     return;
194 
195                 font_parameters_t   fp;
196                 text_parameters_t   tp;
197 
198                 sFont.get_parameters(s, &fp);
199                 sFont.get_text_parameters(s, &tp, &text);
200 
201                 d->nMinWidth    += tp.Width + nRadius * 3;
202                 d->nMinHeight   += fp.Height + nRadius * 2;
203                 d->nGapTop      += fp.Height;
204 
205                 // Destroy surface
206                 s->destroy();
207                 delete s;
208             }
209         }
210 
do_destroy()211         void LSPComboGroup::do_destroy()
212         {
213             size_t count = vWidgets.size();
214             for (size_t i=0; i<count; ++i)
215             {
216                 LSPWidget *w = vWidgets.get(i);
217                 if (w != NULL)
218                     unlink_widget(w);
219             }
220             vWidgets.clear();
221         }
222 
check_mouse_over(ssize_t x,ssize_t y)223         bool LSPComboGroup::check_mouse_over(ssize_t x, ssize_t y)
224         {
225             x              -= sGroupHdr.nLeft;
226             y              -= sGroupHdr.nTop;
227 
228             return (x >= 0) && (y >= 0) && (x < sGroupHdr.nWidth) && (y < sGroupHdr.nHeight);
229         }
230 
set_radius(size_t value)231         void LSPComboGroup::set_radius(size_t value)
232         {
233             if (nRadius == value)
234                 return;
235             nRadius = value;
236             query_resize();
237         }
238 
set_border(size_t value)239         void LSPComboGroup::set_border(size_t value)
240         {
241             if (nBorder == value)
242                 return;
243             nBorder = value;
244             query_resize();
245         }
246 
render(ISurface * s,bool force)247         void LSPComboGroup::render(ISurface *s, bool force)
248         {
249             if (nFlags & REDRAW_SURFACE)
250                 force = true;
251 
252             // Prepare palette
253             Color bg_color(sBgColor);
254             Color color(sColor);
255             color.scale_lightness(brightness());
256 
257 //            lsp_trace("Rendering this=%p, force=%d", this, int(force));
258             LSPWidget *current = current_widget();
259 
260             // Draw child
261             if (current != NULL)
262             {
263                 if ((force) || (current->redraw_pending()))
264                 {
265                     current->render(s, force);
266                     current->commit_redraw();
267                 }
268             }
269 
270             if (force)
271             {
272                 // Get resource
273                 ssize_t cx  = sSize.nLeft + nBorder + 1;
274                 ssize_t cy  = sSize.nTop + nBorder + 1;
275                 ssize_t sx  = sSize.nWidth - (nBorder << 1) - 1;
276                 ssize_t sy  = sSize.nHeight - (nBorder << 1) - 1;
277 //                size_t bw   = round(nRadius * M_SQRT2 * 0.5f) + 1;
278 
279                 // Draw background
280                 if (current == NULL)
281                     s->fill_rect(sSize.nLeft, sSize.nTop, sSize.nWidth, sSize.nHeight, bg_color);
282                 else
283                 {
284                     realize_t r;
285                     current->get_dimensions(&r);
286 
287                     if ((bEmbed) && (nRadius > 1))
288                         s->fill_round_frame(
289                             sSize.nLeft, sSize.nTop, sSize.nWidth, sSize.nHeight,
290                             r.nLeft, r.nTop, r.nWidth, r.nHeight,
291                             nRadius-1, SURFMASK_B_CORNER,
292                             bg_color
293                         );
294                     else
295                         s->fill_frame(
296                                 sSize.nLeft, sSize.nTop, sSize.nWidth, sSize.nHeight,
297                                 r.nLeft, r.nTop, r.nWidth, r.nHeight,
298                                 bg_color
299                             );
300                 }
301 
302                 // Draw frame
303                 bool aa = s->set_antialiasing(true);
304                 s->wire_round_rect(cx, cy, sx-1, sy-1, nRadius, 0x0e, 2.0f, color);
305 
306                 ssize_t bwidth      = 12;
307                 sGroupHdr.nLeft     = cx;
308                 sGroupHdr.nTop      = cy;
309                 sGroupHdr.nWidth    = nRadius;
310                 sGroupHdr.nHeight   = nRadius;
311 
312                 // Draw text frame
313                 LSPString text;
314                 const LSPLocalString *lctext = this->text();
315                 if (lctext != NULL)
316                     lctext->format(&text, this);
317                 if (text.length() > 0)
318                 {
319                     // Draw text border
320                     font_parameters_t   fp;
321                     text_parameters_t   tp;
322                     sFont.get_parameters(s, &fp);
323                     sFont.get_text_parameters(s, &tp, &text);
324 
325                     sGroupHdr.nWidth    = 4 + nRadius + tp.Width + bwidth;
326                     sGroupHdr.nHeight   = fp.Height + 4;
327 
328                     s->fill_round_rect(cx - 1, cy-1, sGroupHdr.nWidth, sGroupHdr.nHeight, nRadius, 0x04, color);
329 
330                     // Show text
331                     Color font(sFont.raw_color());
332                     font.scale_lightness(brightness());
333 
334                     sFont.draw(s, cx + bwidth + 4 , cy + fp.Ascent + nBorder, font, &text);
335 
336                     // Draw buttons
337                     ssize_t half = sGroupHdr.nTop + (fp.Height * 0.5f);
338 
339                     s->fill_triangle(
340                             cx + 2, half - 2,
341                             cx + bwidth - 2, half - 2,
342                             cx + bwidth*0.5f, half - 6,
343                             font);
344 
345                     s->fill_triangle(
346                             cx + 2, half + 1,
347                             cx + bwidth - 2, half + 1,
348                             cx + bwidth*0.5f, half + 5,
349                             font);
350 
351                     s->set_antialiasing(false);
352                     s->line(cx + bwidth + 2, cy + 1, cx + bwidth + 2, cy + fp.Height + 1, 1, font);
353                 }
354 
355                 s->set_antialiasing(aa);
356             }
357         }
358 
add(LSPWidget * widget)359         status_t LSPComboGroup::add(LSPWidget *widget)
360         {
361             widget->set_parent(this);
362             vWidgets.add(widget);
363             query_resize();
364             return STATUS_OK;
365         }
366 
remove(LSPWidget * widget)367         status_t LSPComboGroup::remove(LSPWidget *widget)
368         {
369             if (vWidgets.remove(widget, false))
370                 return STATUS_NOT_FOUND;
371 
372             unlink_widget(widget);
373             return STATUS_OK;
374         }
375 
size_request(size_request_t * r)376         void LSPComboGroup::size_request(size_request_t *r)
377         {
378             LSPWidget *w = current_widget();
379             if (w != NULL)
380                 w->size_request(r);
381 
382             if (r->nMinWidth < 0)
383                 r->nMinWidth    = 0;
384             if (r->nMinHeight < 0)
385                 r->nMinHeight   = 0;
386 
387             if (w != NULL)
388             {
389                 r->nMinWidth   += w->padding()->horizontal();
390                 r->nMinHeight  += w->padding()->vertical();
391             }
392 
393             dimensions_t d;
394             query_dimensions(&d);
395 
396             if (r->nMinWidth >= 0)
397             {
398                 size_t  n = r->nMinWidth + d.nGapLeft + d.nGapRight;
399                 if (n < d.nMinWidth)
400                     r->nMinWidth    = d.nMinWidth;
401                 else
402                     r->nMinWidth    = n;
403             }
404             if (r->nMinHeight >= 0)
405             {
406                 size_t  n = r->nMinHeight + d.nGapTop + d.nGapBottom;
407                 if (n < d.nMinHeight)
408                     r->nMinHeight   = d.nMinHeight;
409                 else
410                     r->nMinHeight   = n;
411             }
412 
413             // Align to 8-pixel grid
414 //            r->nMinWidth    = ((r->nMinWidth  + 7) >> 3) << 3;
415 //            r->nMinHeight   = ((r->nMinHeight + 7) >> 3) << 3;
416 
417             if ((r->nMaxWidth >= 0) && (r->nMaxWidth < r->nMinWidth))
418                 r->nMaxWidth    = r->nMinWidth;
419             if ((r->nMaxHeight >= 0) && (r->nMaxHeight < r->nMinHeight))
420                 r->nMaxHeight   = r->nMinHeight;
421         }
422 
realize(const realize_t * r)423         void LSPComboGroup::realize(const realize_t *r)
424         {
425             LSPWidgetContainer::realize(r);
426             LSPWidget *w = current_widget();
427             if (w == NULL)
428                 return;
429 
430             dimensions_t d;
431             query_dimensions(&d);
432 
433             size_request_t sr;
434             w->size_request(&sr);
435 
436             realize_t rc;
437             rc.nLeft    = r->nLeft   + d.nGapLeft  + w->padding()->left();
438             rc.nTop     = r->nTop    + d.nGapTop   + w->padding()->top();
439             rc.nWidth   = r->nWidth  - d.nGapLeft  - d.nGapRight   - w->padding()->horizontal();
440             rc.nHeight  = r->nHeight - d.nGapTop   - d.nGapBottom  - w->padding()->vertical();
441 
442             if ((sr.nMaxWidth > 0) && (sr.nMaxWidth < rc.nWidth))
443             {
444                 rc.nLeft   += (rc.nWidth - sr.nMaxWidth) >> 1;
445                 rc.nWidth   = sr.nMaxWidth;
446             }
447 
448             if ((sr.nMaxHeight > 0) && (sr.nMaxHeight < rc.nHeight))
449             {
450                 rc.nTop    += (rc.nHeight - sr.nMaxHeight) >> 1;
451                 rc.nHeight  = sr.nMaxHeight;
452             }
453 
454             w->realize(&rc);
455         }
456 
on_mouse_down(const ws_event_t * e)457         status_t LSPComboGroup::on_mouse_down(const ws_event_t *e)
458         {
459             if (!check_mouse_over(e->nLeft, e->nTop))
460             {
461                 nCBFlags |= F_MOUSE_OUT;
462                 return STATUS_OK;
463             }
464 
465             take_focus();
466             nMFlags |= (1 << e->nCode);
467             return STATUS_OK;
468         }
469 
on_mouse_up(const ws_event_t * e)470         status_t LSPComboGroup::on_mouse_up(const ws_event_t *e)
471         {
472             size_t flags = nMFlags;
473             nMFlags &= ~(1 << e->nCode);
474 
475             if (nCBFlags & F_MOUSE_OUT)
476             {
477                 if (!nMFlags)
478                     nCBFlags &= ~F_MOUSE_OUT;
479                 return STATUS_OK;
480             }
481 
482             if ((e->nCode == MCB_LEFT) && (flags == size_t(1 << e->nCode)))
483             {
484                 if (inside(e->nLeft, e->nTop))
485                     toggle();
486             }
487 
488             return STATUS_OK;
489         }
490 
on_mouse_scroll(const ws_event_t * e)491         status_t LSPComboGroup::on_mouse_scroll(const ws_event_t *e)
492         {
493             if (!check_mouse_over(e->nLeft, e->nTop))
494                 return STATUS_OK;
495 
496             ssize_t selection = sListBox.selection()->value();
497             ssize_t old  = selection;
498             ssize_t last = sListBox.items()->size() - 1;
499 
500             if (e->nCode == MCD_UP)
501             {
502                 if (selection > 0)
503                     selection --;
504                 else if (selection == 0)
505                 {
506                     if (!(nCBFlags & F_CIRCULAR))
507                         return STATUS_OK;
508                     selection = last;
509                 }
510                 else
511                     selection = sListBox.items()->size() - 1;
512             }
513             else if (e->nCode == MCD_DOWN)
514             {
515                 if (selection >= 0)
516                 {
517                     if (selection < last)
518                         selection ++;
519                     else if (!(nCBFlags & F_CIRCULAR))
520                         return STATUS_OK;
521                     else
522                         selection = 0;
523                 }
524                 else
525                     selection = 0;
526             }
527             else
528                 return STATUS_OK;
529 
530             sListBox.selection()->set_value(selection);
531             if (sListBox.selection()->value() == old)
532                 return STATUS_OK;
533 
534             sSlots.execute(LSPSLOT_CHANGE, this);
535             return sSlots.execute(LSPSLOT_SUBMIT, this);
536         }
537 
on_change()538         status_t LSPComboGroup::on_change()
539         {
540             return STATUS_OK;
541         }
542 
on_submit()543         status_t LSPComboGroup::on_submit()
544         {
545             lsp_trace("on_submit");
546             close();
547             return STATUS_OK;
548         }
549 
text() const550         const LSPLocalString *LSPComboGroup::text() const
551         {
552             LSPComboList *lb = const_cast<LSPComboList *>(&sListBox);
553             ssize_t idx = lb->selection()->value();
554             LSPItem *itm = lb->items()->get(idx);
555             return (itm != NULL) ? itm->text() : NULL;
556         }
557 
selected() const558         ssize_t LSPComboGroup::selected() const
559         {
560             LSPComboList *lb = const_cast<LSPComboList *>(&sListBox);
561             return lb->selection()->value();
562         }
563 
set_selected(ssize_t value)564         status_t LSPComboGroup::set_selected(ssize_t value)
565         {
566             return sListBox.selection()->set_value(value);
567         }
568 
set_opened(bool open)569         status_t LSPComboGroup::set_opened(bool open)
570         {
571             if (open == bool(nCBFlags & F_OPENED))
572                 return STATUS_OK;
573 
574             // Check if we need to close combo box
575             if (!open)
576             {
577                 if (pPopup != NULL)
578                     pPopup->hide();
579                 sListBox.hide();
580 
581                 nCBFlags &= ~F_OPENED;
582                 return STATUS_OK;
583             }
584 
585             LSPWindow *parent = widget_cast<LSPWindow>(toplevel());
586 
587             // Now we need to open combo box
588             // Create popup window
589             if (pPopup == NULL)
590             {
591                 if (parent != NULL)
592                     pPopup  = new LSPComboPopup(pDisplay, this, parent->screen());
593                 else
594                     pPopup  = new LSPComboPopup(pDisplay, this);
595                 if (pPopup == NULL)
596                     return STATUS_NO_MEM;
597 
598                 status_t result = pPopup->init();
599                 if (result != STATUS_OK)
600                 {
601                     pPopup->destroy();
602                     delete pPopup;
603                     pPopup = NULL;
604                     return result;
605                 }
606 
607                 pPopup->set_border_style(BS_COMBO);
608                 pPopup->actions()->set_actions(WA_COMBO);
609                 pPopup->add(&sListBox);
610                 pPopup->slots()->bind(LSPSLOT_MOUSE_DOWN, slot_on_list_mouse_down, self());
611                 pPopup->slots()->intercept(LSPSLOT_KEY_DOWN, slot_on_list_key_down, self());
612                 pPopup->slots()->bind(LSPSLOT_SHOW, slot_on_list_show, self());
613             }
614 
615             // Calculate popup window size and location
616             realize_t r;
617             r.nLeft     = 0;
618             r.nTop      = 0;
619             r.nWidth    = 0;
620             r.nHeight   = 0;
621             if (parent != NULL)
622                 parent->get_absolute_geometry(&r);
623 
624             // Get the screen size
625             ssize_t sw, sh;
626             size_t screen = pDisplay->display()->default_screen();
627             LSPWindow *top = widget_cast<LSPWindow>(toplevel());
628             if (top != NULL)
629                 screen = top->screen();
630             pDisplay->screen_size(screen, &sw, &sh);
631 
632             // Get initial geometry of the window
633             size_request_t opr;
634             realize_t wr1, wr2;
635 
636             sListBox.optimal_size_request(&opr);
637 
638             wr1.nLeft       = r.nLeft + sGroupHdr.nLeft;
639             if (wr1.nLeft < 0)
640                 wr1.nLeft       = 0;
641             wr1.nWidth      = opr.nMaxWidth;
642 
643             if ((wr1.nWidth < sGroupHdr.nWidth) && (sGroupHdr.nHeight < sw))
644                 wr1.nWidth      = sGroupHdr.nWidth;
645             if (wr1.nWidth > sw)
646                 wr1.nWidth      = sw;
647             if (wr1.nHeight > sh)
648                 wr1.nHeight     = sh;
649             if ((wr1.nLeft + wr1.nWidth) >= sw)
650             {
651                 wr1.nLeft      = sw - wr1.nWidth;
652                 if (wr1.nLeft < 0)
653                     wr1.nLeft   = 0;
654             }
655             wr2.nLeft       = wr1.nLeft;
656             wr2.nWidth      = wr1.nWidth;
657 
658             wr1.nTop        = r.nTop + sGroupHdr.nTop + sGroupHdr.nHeight;
659             wr2.nTop        = 0;
660 
661             wr1.nHeight     = sh - wr1.nTop;
662             wr2.nHeight     = r.nTop + sGroupHdr.nTop - wr2.nTop;
663 
664             if (wr1.nHeight > opr.nMaxHeight)
665                 wr1.nHeight     = opr.nMaxHeight;
666             if (wr2.nHeight > opr.nMaxHeight)
667             {
668                 wr2.nTop       += wr2.nHeight - opr.nMaxHeight;
669                 wr2.nHeight     = opr.nMaxHeight;
670             }
671 
672             if ((wr1.nHeight < opr.nMinHeight) && (wr2.nHeight >= opr.nMinHeight))
673                 pPopup->set_geometry(&wr2);
674             else
675                 pPopup->set_geometry(&wr1);
676 
677             // Finally, show the popup window
678             sListBox.show();
679             sListBox.set_focus();
680             pPopup->show(this);
681             pPopup->grab_events(GRAB_DROPDOWN);
682             nCBFlags |= F_OPENED;
683 
684             return STATUS_OK;
685         }
686 
set_circular(bool circular)687         void LSPComboGroup::set_circular(bool circular)
688         {
689             if (circular)
690                 nCBFlags     |= F_CIRCULAR;
691             else
692                 nCBFlags     &= ~F_CIRCULAR;
693         }
694 
set_embed(bool embed)695         void LSPComboGroup::set_embed(bool embed)
696         {
697             if (bEmbed == embed)
698                 return;
699             bEmbed = embed;
700             query_resize();
701         }
702 
on_list_change()703         status_t LSPComboGroup::on_list_change()
704         {
705             return sSlots.execute(LSPSLOT_CHANGE, this);
706         }
707 
on_list_submit()708         status_t LSPComboGroup::on_list_submit()
709         {
710             if (!(nCBFlags & F_OPENED))
711                 return STATUS_OK;
712             close();
713             return sSlots.execute(LSPSLOT_SUBMIT, this);
714         }
715 
716 
slot_on_change(LSPWidget * sender,void * ptr,void * data)717         status_t LSPComboGroup::slot_on_change(LSPWidget *sender, void *ptr, void *data)
718         {
719             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
720             return (ptr != NULL) ? _this->on_change() : STATUS_BAD_ARGUMENTS;
721         }
722 
slot_on_submit(LSPWidget * sender,void * ptr,void * data)723         status_t LSPComboGroup::slot_on_submit(LSPWidget *sender, void *ptr, void *data)
724         {
725             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
726             return (ptr != NULL) ? _this->on_submit() : STATUS_BAD_ARGUMENTS;
727         }
728 
slot_on_list_change(LSPWidget * sender,void * ptr,void * data)729         status_t LSPComboGroup::slot_on_list_change(LSPWidget *sender, void *ptr, void *data)
730         {
731             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
732             return (ptr != NULL) ? _this->on_list_change() : STATUS_BAD_ARGUMENTS;
733         }
734 
slot_on_list_submit(LSPWidget * sender,void * ptr,void * data)735         status_t LSPComboGroup::slot_on_list_submit(LSPWidget *sender, void *ptr, void *data)
736         {
737             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
738             return (ptr != NULL) ? _this->on_list_submit() : STATUS_BAD_ARGUMENTS;
739         }
740 
slot_on_list_focus_out(LSPWidget * sender,void * ptr,void * data)741         status_t LSPComboGroup::slot_on_list_focus_out(LSPWidget *sender, void *ptr, void *data)
742         {
743             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
744             return (ptr != NULL) ? _this->on_list_focus_out() : STATUS_BAD_ARGUMENTS;
745         }
746 
slot_on_list_mouse_down(LSPWidget * sender,void * ptr,void * data)747         status_t LSPComboGroup::slot_on_list_mouse_down(LSPWidget *sender, void *ptr, void *data)
748         {
749             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
750             return (ptr != NULL) ? _this->on_grab_mouse_down(static_cast<ws_event_t *>(data)) : STATUS_BAD_ARGUMENTS;
751         }
752 
slot_on_list_key_down(LSPWidget * sender,void * ptr,void * data)753         status_t LSPComboGroup::slot_on_list_key_down(LSPWidget *sender, void *ptr, void *data)
754         {
755             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
756             return (ptr != NULL) ? _this->on_grab_key_down(static_cast<ws_event_t *>(data)) : STATUS_BAD_ARGUMENTS;
757         }
758 
slot_on_list_show(LSPWidget * sender,void * ptr,void * data)759         status_t LSPComboGroup::slot_on_list_show(LSPWidget *sender, void *ptr, void *data)
760         {
761             LSPComboGroup *_this = widget_ptrcast<LSPComboGroup>(ptr);
762             return (ptr != NULL) ? _this->on_list_show() : STATUS_BAD_ARGUMENTS;
763         }
764 
on_selection_change()765         void LSPComboGroup::on_selection_change()
766         {
767             query_resize();
768 //            query_draw();
769         }
770 
on_item_change(size_t index,LSPItem * item)771         void LSPComboGroup::on_item_change(size_t index, LSPItem *item)
772         {
773             ssize_t sel = sListBox.selection()->value();
774             if ((sel >= 0) && (index == size_t(sel)))
775                 query_draw();
776         }
777 
on_item_add(size_t index)778         void LSPComboGroup::on_item_add(size_t index)
779         {
780         }
781 
on_item_remove(size_t index)782         void LSPComboGroup::on_item_remove(size_t index)
783         {
784             ssize_t sel = sListBox.selection()->value();
785             if ((sel >= 0) && (index == size_t(sel)))
786                 query_draw();
787         }
788 
on_item_swap(size_t idx1,size_t idx2)789         void LSPComboGroup::on_item_swap(size_t idx1, size_t idx2)
790         {
791             ssize_t sel = sListBox.selection()->value();
792             if (sel < 0)
793                 return;
794             if ((idx1 == size_t(sel)) || (idx2 == size_t(sel)))
795                 query_draw();
796         }
797 
on_item_clear()798         void LSPComboGroup::on_item_clear()
799         {
800             query_draw();
801         }
802 
on_list_focus_out()803         status_t LSPComboGroup::on_list_focus_out()
804         {
805             lsp_trace("focus_out triggered");
806             return STATUS_OK;
807         }
808 
on_list_show()809         status_t LSPComboGroup::on_list_show()
810         {
811 //            if (pPopup != NULL)
812 //                pPopup->take_focus();
813             return STATUS_OK;
814         }
815 
on_grab_mouse_down(const ws_event_t * e)816         status_t LSPComboGroup::on_grab_mouse_down(const ws_event_t *e)
817         {
818             lsp_trace("mouse_down triggered left=%d, top=%d", int(e->nLeft), int(e->nTop));
819             if ((e->nLeft < 0) || (e->nTop < 0) || (e->nLeft > pPopup->width()) || (e->nTop > pPopup->height()))
820                 close();
821 
822             return STATUS_OK;
823         }
824 
on_grab_key_down(const ws_event_t * e)825         status_t LSPComboGroup::on_grab_key_down(const ws_event_t *e)
826         {
827             lsp_trace("key_down triggered left=%d, top=%d", int(e->nLeft), int(e->nTop));
828             close();
829             return STATUS_OK;
830         }
831     } /* namespace tk */
832 } /* namespace lsp */
833