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: 18 сент. 2017 г. 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 const w_class_t LSPMenu::metadata = { "LSPMenu", &LSPWidgetContainer::metadata }; 29 30 //----------------------------------------------------------------------------- 31 // LSPMenu::LSPMenuWindow implementation MenuWindow(LSPDisplay * dpy,LSPMenu * menu,size_t screen)32 LSPMenu::MenuWindow::MenuWindow(LSPDisplay *dpy, LSPMenu *menu, size_t screen): 33 LSPWindow(dpy, NULL, screen) 34 { 35 pMenu = menu; 36 } 37 ~MenuWindow()38 LSPMenu::MenuWindow::~MenuWindow() 39 { 40 } 41 find_widget(ssize_t x,ssize_t y)42 LSPWidget *LSPMenu::MenuWindow::find_widget(ssize_t x, ssize_t y) 43 { 44 return (pMenu != NULL) ? pMenu->find_widget(x, y) : NULL; 45 } 46 render(ISurface * s,bool force)47 void LSPMenu::MenuWindow::render(ISurface *s, bool force) 48 { 49 if (pMenu != NULL) 50 pMenu->render(s, force); 51 else 52 LSPWindow::render(s, force); 53 } 54 get_handler(ws_event_t * e)55 LSPMenu *LSPMenu::MenuWindow::get_handler(ws_event_t *e) 56 { 57 LSPMenu *handler = (pMenu != NULL) ? pMenu->check_inside_submenu(e) : NULL; 58 if (handler == NULL) 59 handler = pMenu; 60 return handler; 61 } 62 on_mouse_down(const ws_event_t * e)63 status_t LSPMenu::MenuWindow::on_mouse_down(const ws_event_t *e) 64 { 65 ws_event_t xev = *e; 66 LSPMenu *handler = get_handler(&xev); 67 return (handler != NULL) ? handler->on_mouse_down(&xev) : LSPWindow::on_mouse_down(&xev); 68 } 69 on_mouse_up(const ws_event_t * e)70 status_t LSPMenu::MenuWindow::on_mouse_up(const ws_event_t *e) 71 { 72 ws_event_t xev = *e; 73 LSPMenu *handler = get_handler(&xev); 74 return (handler != NULL) ? handler->on_mouse_up(&xev) : LSPWindow::on_mouse_up(&xev); 75 } 76 on_mouse_scroll(const ws_event_t * e)77 status_t LSPMenu::MenuWindow::on_mouse_scroll(const ws_event_t *e) 78 { 79 ws_event_t xev = *e; 80 LSPMenu *handler = get_handler(&xev); 81 return (handler != NULL) ? handler->on_mouse_scroll(e) : LSPWindow::on_mouse_scroll(e); 82 } 83 on_mouse_move(const ws_event_t * e)84 status_t LSPMenu::MenuWindow::on_mouse_move(const ws_event_t *e) 85 { 86 ws_event_t xev = *e; 87 LSPMenu *handler = get_handler(&xev); 88 return (handler != NULL) ? handler->on_mouse_move(&xev) : LSPWindow::on_mouse_move(&xev); 89 } 90 size_request(size_request_t * r)91 void LSPMenu::MenuWindow::size_request(size_request_t *r) 92 { 93 if (pMenu != NULL) 94 pMenu->size_request(r); 95 96 // Limit the size of window with the screen size 97 pDisplay->display()->screen_size(screen(), &r->nMaxWidth, &r->nMaxHeight); 98 if ((r->nMinWidth > 0) && (r->nMinWidth > r->nMaxWidth)) 99 r->nMinWidth = r->nMaxWidth; 100 if ((r->nMinHeight > 0) && (r->nMinHeight > r->nMaxHeight)) 101 r->nMinHeight = r->nMaxHeight; 102 } 103 104 //----------------------------------------------------------------------------- 105 // LSPMenu implementation LSPMenu(LSPDisplay * dpy)106 LSPMenu::LSPMenu(LSPDisplay *dpy): 107 LSPWidgetContainer(dpy), 108 sFont(this), 109 sSelColor(this), 110 sBorderColor(this) 111 { 112 pWindow = NULL; 113 pParentMenu = NULL; 114 pActiveMenu = NULL; 115 nPopupLeft = -1; 116 nPopupTop = -1; 117 nSelected = SEL_NONE; 118 nScroll = 0; 119 nScrollMax = 0; 120 nBorder = 1; 121 nSpacing = 6; 122 nMBState = 0; 123 124 sPadding.set(16, 16, 0, 0); 125 126 nFlags &= ~F_VISIBLE; 127 pClass = &metadata; 128 129 sScroll.bind(pDisplay); 130 sScroll.set_handler(timer_handler, this); 131 } 132 ~LSPMenu()133 LSPMenu::~LSPMenu() 134 { 135 do_destroy(); 136 } 137 init()138 status_t LSPMenu::init() 139 { 140 status_t result = LSPWidget::init(); 141 if (result != STATUS_OK) 142 return result; 143 144 if (pDisplay != NULL) 145 { 146 // Get theme 147 LSPTheme *theme = pDisplay->theme(); 148 if (theme != NULL) 149 sFont.init(theme->font()); 150 } 151 152 init_color(C_BACKGROUND, sFont.color()); 153 init_color(C_BACKGROUND, &sBorderColor); 154 init_color(C_LABEL_TEXT, &sBgColor); 155 init_color(C_KNOB_SCALE, &sSelColor); 156 157 return STATUS_OK; 158 } 159 destroy()160 void LSPMenu::destroy() 161 { 162 do_destroy(); 163 LSPWidgetContainer::destroy(); 164 } 165 do_destroy()166 void LSPMenu::do_destroy() 167 { 168 size_t n = vItems.size(); 169 for (size_t i=0; i<n; ++i) 170 { 171 LSPMenuItem *item = vItems.at(i); 172 if (item == NULL) 173 continue; 174 175 unlink_widget(item); 176 } 177 178 vItems.flush(); 179 180 if (pWindow != NULL) 181 { 182 pWindow->destroy(); 183 delete pWindow; 184 pWindow = NULL; 185 } 186 } 187 188 // LSPWidget *LSPMenu::find_widget(ssize_t x, ssize_t y) 189 // { 190 // size_t items = vItems.size(); 191 // for (size_t i=0; i<items; ++i) 192 // { 193 // LSPMenuItem *w = vItems.at(i); 194 // if ((w == NULL) || (w->hidden())) 195 // continue; 196 // if (w->inside(x, y)) 197 // return w; 198 // } 199 // 200 // return NULL; 201 // } 202 set_border(size_t value)203 void LSPMenu::set_border(size_t value) 204 { 205 if (nBorder == value) 206 return; 207 nBorder = value; 208 query_resize(); 209 } 210 set_spacing(size_t value)211 void LSPMenu::set_spacing(size_t value) 212 { 213 if (nSpacing == value) 214 return; 215 nSpacing = value; 216 query_resize(); 217 } 218 set_scroll(ssize_t scroll)219 void LSPMenu::set_scroll(ssize_t scroll) 220 { 221 if (scroll < 0) 222 scroll = 0; 223 else if (scroll > nScrollMax) 224 scroll = nScrollMax; 225 226 if (nScroll == scroll) 227 return; 228 nScroll = scroll; 229 230 query_draw(); 231 if (pWindow != NULL) 232 pWindow->query_draw(); 233 } 234 query_resize()235 void LSPMenu::query_resize() 236 { 237 LSPWidgetContainer::query_resize(); 238 if (pWindow != NULL) 239 pWindow->query_resize(); 240 } 241 add(LSPWidget * child)242 status_t LSPMenu::add(LSPWidget *child) 243 { 244 LSPMenuItem *item = widget_cast<LSPMenuItem>(child); 245 if (child == NULL) 246 return STATUS_BAD_ARGUMENTS; 247 248 if (!vItems.add(item)) 249 return STATUS_NO_MEM; 250 251 item->set_parent(this); 252 253 query_resize(); 254 return STATUS_SUCCESS; 255 } 256 remove(LSPWidget * child)257 status_t LSPMenu::remove(LSPWidget *child) 258 { 259 size_t n = vItems.size(); 260 for (size_t i=0; i<n; ++i) 261 { 262 LSPMenuItem *item = vItems.at(i); 263 if (item == child) 264 { 265 query_resize(); 266 return (vItems.remove(i)) ? STATUS_OK : STATUS_UNKNOWN_ERR; 267 } 268 } 269 270 return STATUS_NOT_FOUND; 271 } 272 check_inside_submenu(ws_event_t * ev)273 LSPMenu *LSPMenu::check_inside_submenu(ws_event_t *ev) 274 { 275 LSPMenu *handler = NULL; 276 if ((pActiveMenu != NULL) && 277 (pActiveMenu->pWindow != NULL) && 278 (pActiveMenu->pWindow->visible())) 279 { 280 // Get window geometry 281 realize_t r1, r2; 282 pWindow->get_absolute_geometry(&r1); 283 pActiveMenu->pWindow->get_absolute_geometry(&r2); 284 lsp_trace("wnd=%p, active=%p, ev={%d, %d}, r1={%d, %d}, r2={%d, %d}", 285 pWindow, pActiveMenu, 286 int(ev->nLeft), int(ev->nTop), 287 int(r1.nLeft), int(r1.nTop), 288 int(r2.nLeft), int(r2.nTop) 289 ); 290 291 ws_event_t xev = *ev; 292 xev.nLeft = r1.nLeft + ev->nLeft - r2.nLeft; 293 xev.nTop = r1.nTop + ev->nTop - r2.nTop; 294 295 if ((handler = pActiveMenu->check_inside_submenu(&xev)) != NULL) 296 { 297 *ev = xev; 298 return handler; 299 } 300 } 301 302 if ((pWindow != NULL) && 303 (pWindow->visible())) 304 { 305 lsp_trace("check ev {%d, %d} inside of wnd %p, {%d, %d, %d, %d} x { %d, %d }", 306 int(ev->nLeft), int(ev->nTop), 307 pWindow, 308 int(pWindow->left()), int(pWindow->top()), 309 int(pWindow->right()), int(pWindow->bottom()), 310 int(pWindow->width()), int(pWindow->height()) 311 ); 312 313 if ((ev->nLeft >= 0) && 314 (ev->nTop >= 0) && 315 (ev->nLeft < pWindow->width()) && 316 (ev->nTop < pWindow->height())) 317 return this; 318 } 319 320 return NULL; 321 } 322 find_item(ssize_t mx,ssize_t my,ssize_t * ry)323 ssize_t LSPMenu::find_item(ssize_t mx, ssize_t my, ssize_t *ry) 324 { 325 // // Are we inside of submenu? 326 // if (check_inside_submenu(mx, my)) 327 // return nSelected; 328 329 if ((mx < 0) || (mx >= sSize.nWidth)) 330 return SEL_NONE; 331 if ((my < 0) || (my >= sSize.nHeight)) 332 return SEL_NONE; 333 334 font_parameters_t fp; 335 sFont.get_parameters(&fp); 336 337 ssize_t separator = fp.Height * 0.5f + nSpacing; 338 fp.Height += nSpacing; 339 340 if (nScrollMax > 0) 341 { 342 if ((nScroll > 0) && (my < ssize_t(nBorder + separator))) // Top button 343 return SEL_TOP_SCROLL; 344 else if ((nScroll < nScrollMax) && (my > ssize_t(sSize.nHeight - nBorder - separator))) // Bottom button 345 return SEL_BOTTOM_SCROLL; 346 } 347 348 // Iterate over all menu items 349 ssize_t y = sPadding.top() + nBorder - nScroll; 350 size_t n = vItems.size(); 351 352 for (size_t i=0; i < n; ++i) 353 { 354 LSPMenuItem *item = vItems.get(i); 355 if ((item == NULL) || (!item->visible())) 356 continue; 357 358 if (item->is_separator()) 359 y += separator; 360 else 361 { 362 if ((my >= y) && (my < (y + fp.Height))) 363 { 364 if (ry != NULL) 365 *ry = y; 366 return i; 367 } 368 369 y += fp.Height; 370 } 371 } 372 373 return SEL_NONE; 374 } 375 draw(ISurface * s)376 void LSPMenu::draw(ISurface *s) 377 { 378 // Prepare palette 379 Color bg_color(sBgColor); 380 Color border(sBorderColor); 381 Color font(sFont.raw_color()); 382 Color sel(sSelColor); 383 Color tmp; 384 385 border.scale_lightness(brightness()); 386 font.scale_lightness(brightness()); 387 sel.scale_lightness(brightness()); 388 389 // Draw background 390 s->clear(bg_color); 391 392 font_parameters_t fp; 393 text_parameters_t tp; 394 sFont.get_parameters(s, &fp); 395 396 ssize_t separator = fp.Height * 0.5f + nSpacing; 397 ssize_t sep_len = sSize.nWidth - (nBorder + nSpacing) * 2; 398 ssize_t hspace = nSpacing >> 1; 399 400 fp.Height += nSpacing; 401 ssize_t y = sPadding.top() + nBorder - nScroll; 402 ssize_t x = sPadding.left() + nBorder; 403 size_t n = vItems.size(); 404 405 LSPString text; 406 407 for (size_t i=0; i < n; ++i) 408 { 409 LSPMenuItem *item = vItems.get(i); 410 if ((item == NULL) || (!item->visible())) 411 continue; 412 413 // lsp_trace("x,y = %d, %d", int(x), int(y)); 414 415 if (y >= sSize.nHeight) 416 break; 417 418 if (item->is_separator()) 419 { 420 if (y > (-separator)) 421 { 422 if (sep_len > 0) 423 s->fill_rect(x - sPadding.left() + nSpacing, y + (separator >> 1), sep_len, 1, border); 424 } 425 426 y += separator; 427 } 428 else 429 { 430 if (y > (-fp.Height)) 431 { 432 item->text()->format(&text); 433 if (nSelected == ssize_t(i)) 434 { 435 s->fill_rect(nBorder, y, sSize.nWidth - nBorder*2, fp.Height, sel); 436 tmp.copy(bg_color); 437 } 438 else 439 tmp.copy(font); 440 441 if (!text.is_empty()) 442 sFont.draw(s, x, y + fp.Ascent + hspace, tmp, &text); 443 444 if (item->has_submenu()) 445 { 446 sFont.get_text_parameters(s, &tp, "►"); 447 sFont.draw(s, sSize.nWidth - nBorder - nSpacing - tp.XAdvance - 2, y + fp.Ascent + hspace, tmp, "►"); 448 } 449 } 450 451 y += fp.Height; 452 } 453 } 454 455 if (nScrollMax > 0) 456 { 457 float cx = sSize.nWidth * 0.5f; 458 float aa = s->set_antialiasing(true); 459 460 // Top button 461 if (nScroll > 0) 462 { 463 s->fill_rect(nBorder, nBorder, sSize.nWidth - nBorder * 2, separator, bg_color); 464 if (nSelected == SEL_TOP_SCROLL) 465 { 466 tmp.copy(bg_color); 467 s->fill_rect(nBorder + 1, nBorder + 1, sSize.nWidth - (nBorder + 1)* 2, separator - 1, border); 468 } 469 else 470 tmp.copy(font); 471 472 // Draw arrow up 473 s->fill_triangle( 474 cx, nBorder + 3, 475 cx + separator, nBorder + separator - 2, 476 cx - separator, nBorder + separator - 2, 477 tmp); 478 } 479 else if (sPadding.top() > 0) 480 s->fill_rect(nBorder, nBorder, sSize.nWidth - nBorder * 2, sPadding.top(), bg_color); 481 482 // Bottom button 483 if (nScroll < nScrollMax) 484 { 485 s->fill_rect(nBorder, sSize.nHeight - nBorder - separator, 486 sSize.nWidth - nBorder * 2, separator, bg_color); 487 488 if (nSelected == SEL_BOTTOM_SCROLL) 489 { 490 tmp.copy(bg_color); 491 s->fill_rect(nBorder + 1, sSize.nHeight - nBorder - separator, 492 sSize.nWidth - (nBorder + 1) * 2, separator - 1, border); 493 } 494 else 495 tmp.copy(font); 496 497 // Draw arrow down 498 s->fill_triangle( 499 cx, sSize.nHeight - nBorder - 3, 500 cx + separator, sSize.nHeight - nBorder - separator + 2, 501 cx - separator, sSize.nHeight - nBorder - separator + 2, 502 tmp); 503 } 504 else if (sPadding.bottom() > 0) 505 s->fill_rect(nBorder, sSize.nHeight - nBorder - sPadding.bottom(), 506 sSize.nWidth - nBorder * 2, sPadding.bottom(), bg_color); 507 508 // Restore anti-aliasing 509 s->set_antialiasing(aa); 510 } 511 512 if (nBorder > 0) 513 s->fill_frame(0, 0, sSize.nWidth, sSize.nHeight, 514 nBorder, nBorder, sSize.nWidth - nBorder * 2, sSize.nHeight - nBorder * 2, border); 515 } 516 hide()517 bool LSPMenu::hide() 518 { 519 // Forget the parent menu 520 pParentMenu = NULL; 521 522 // Hide active submenu if present 523 if (pActiveMenu != NULL) 524 { 525 pActiveMenu->hide(); 526 pActiveMenu = NULL; 527 } 528 529 // Hide window showing menu 530 if (pWindow != NULL) 531 pWindow->hide(); 532 533 if (!is_visible()) 534 return false; 535 536 return LSPWidgetContainer::hide(); 537 } 538 show()539 bool LSPMenu::show() 540 { 541 if (is_visible()) 542 return false; 543 544 size_t screen = pDisplay->display()->default_screen(); 545 LSPWindow *top = widget_cast<LSPWindow>(toplevel()); 546 if (top != NULL) 547 screen = top->screen(); 548 549 return show(screen, nPopupLeft, nPopupTop); 550 } 551 show(size_t screen)552 bool LSPMenu::show(size_t screen) 553 { 554 return show(screen, nPopupLeft, nPopupTop); 555 } 556 show(LSPWidget * w)557 bool LSPMenu::show(LSPWidget *w) 558 { 559 return show(w, nPopupLeft, nPopupTop); 560 } 561 show(LSPWidget * w,ssize_t x,ssize_t y)562 bool LSPMenu::show(LSPWidget *w, ssize_t x, ssize_t y) 563 { 564 if (is_visible()) 565 return false; 566 567 size_t screen = pDisplay->display()->default_screen(); 568 LSPWindow *top = widget_cast<LSPWindow>(toplevel()); 569 if (top != NULL) 570 screen = top->screen(); 571 572 return show(w, screen, x, y); 573 } 574 show(LSPWidget * w,const ws_event_t * ev)575 bool LSPMenu::show(LSPWidget *w, const ws_event_t *ev) 576 { 577 if (ev == NULL) 578 return show(w, nPopupLeft, nPopupTop); 579 580 realize_t r; 581 r.nLeft = 0; 582 r.nTop = 0; 583 r.nWidth = 0; 584 r.nHeight = 0; 585 586 LSPWindow *parent = widget_cast<LSPWindow>(w->toplevel()); 587 if (parent != NULL) 588 parent->get_absolute_geometry(&r); 589 590 return show(w, r.nLeft + ev->nLeft, r.nTop + ev->nTop); 591 } 592 show(size_t screen,ssize_t left,ssize_t top)593 bool LSPMenu::show(size_t screen, ssize_t left, ssize_t top) 594 { 595 return show(NULL, screen, left, top); 596 } 597 show(LSPWidget * w,size_t screen,ssize_t left,ssize_t top)598 bool LSPMenu::show(LSPWidget *w, size_t screen, ssize_t left, ssize_t top) 599 { 600 if (is_visible()) 601 return false; 602 603 // Determine what screen to use 604 IDisplay *dpy = pDisplay->display(); 605 if (screen >= dpy->screens()) 606 screen = dpy->default_screen(); 607 608 // Now we are ready to create window 609 if (pWindow == NULL) 610 { 611 // Create window 612 pWindow = new MenuWindow(pDisplay, this, screen); 613 if (pWindow == NULL) 614 return false; 615 616 // Initialize window 617 status_t result = pWindow->init(); 618 if (result != STATUS_OK) 619 { 620 pWindow->destroy(); 621 delete pWindow; 622 pWindow = NULL; 623 return false; 624 } 625 626 pWindow->set_border_style(BS_POPUP); 627 pWindow->actions()->set_actions(WA_POPUP); 628 } 629 630 // Get initial window geometry 631 realize_t wr; 632 pWindow->get_geometry(&wr); 633 if (left >= 0) 634 wr.nLeft = left; 635 else if (wr.nLeft < 0) 636 wr.nLeft = 0; 637 if (top >= 0) 638 wr.nTop = top; 639 else if (wr.nTop < 0) 640 wr.nTop = 0; 641 642 643 // Now request size and adjust location 644 size_request_t sr; 645 pWindow->size_request(&sr); 646 647 ssize_t sw = 0, sh = 0; 648 dpy->screen_size(pWindow->screen(), &sw, &sh); 649 ssize_t xlast = wr.nLeft + sr.nMinWidth, ylast = wr.nTop + sr.nMinHeight; 650 651 if (xlast > sw) 652 wr.nLeft -= (xlast - sw); 653 if (ylast > sh) 654 wr.nTop -= (ylast - sh); 655 wr.nWidth = sr.nMinWidth; 656 wr.nHeight = sr.nMinHeight; 657 658 // Now we can set the geometry and show window 659 pWindow->set_geometry(&wr); 660 wr.nLeft = 0; 661 wr.nTop = 0; 662 realize(&wr); 663 nSelected = SEL_NONE; 664 665 pWindow->show(w); 666 667 // Need to perform grabbing? 668 pParentMenu = widget_cast<LSPMenu>(w); 669 if (pParentMenu == NULL) 670 pWindow->grab_events(GRAB_MENU); 671 672 return LSPWidgetContainer::show(); 673 } 674 size_request(size_request_t * r)675 void LSPMenu::size_request(size_request_t *r) 676 { 677 r->nMinWidth = 0; 678 r->nMinHeight = 0; 679 r->nMaxWidth = -1; 680 r->nMaxHeight = -1; 681 682 // Create surface 683 ISurface *s = pDisplay->create_surface(1, 1); 684 if (s == NULL) 685 return; 686 687 // Estimate the size of menu 688 font_parameters_t fp; 689 text_parameters_t tp; 690 sFont.get_parameters(s, &fp); 691 size_t n = vItems.size(); 692 ssize_t separator = fp.Height * 0.5f; 693 ssize_t subitem = 0; 694 695 LSPString text; 696 for (size_t i=0; i<n; ++i) 697 { 698 LSPMenuItem *mi = vItems.at(i); 699 if ((mi == NULL) || (!mi->visible())) 700 continue; 701 702 if (mi->is_separator()) 703 { 704 r->nMinHeight += separator + nSpacing; 705 if (r->nMinWidth < fp.Height) 706 r->nMinWidth = fp.Height; 707 } 708 else 709 { 710 r->nMinHeight += fp.Height + nSpacing; 711 ssize_t width = (mi->submenu() != NULL) ? separator : 0; 712 713 mi->text()->format(&text); 714 if (!text.is_empty()) 715 { 716 sFont.get_text_parameters(s, &tp, &text); 717 width += tp.XAdvance; 718 } 719 720 if ((subitem <= 0) && (mi->has_submenu())) 721 { 722 sFont.get_text_parameters(s, &tp, "►"); 723 subitem += tp.XAdvance + 2; 724 } 725 726 if (r->nMinWidth < width) 727 r->nMinWidth = width; 728 } 729 } 730 731 r->nMinWidth += nBorder * 2 + subitem + sPadding.horizontal(); 732 r->nMinHeight += nBorder * 2 + sPadding.vertical(); 733 734 // Destroy surface 735 s->destroy(); 736 delete s; 737 } 738 realize(const realize_t * r)739 void LSPMenu::realize(const realize_t *r) 740 { 741 LSPWidgetContainer::realize(r); 742 743 size_request_t sr; 744 size_request(&sr); 745 746 nScrollMax = sr.nMinHeight - r->nHeight; 747 set_scroll(nScroll); 748 // lsp_trace("scroll_max = %d, scroll = %d", int(nScrollMax), int(nScroll)); 749 750 query_draw(); 751 if (pWindow != NULL) 752 pWindow->query_draw(); 753 } 754 on_mouse_down(const ws_event_t * e)755 status_t LSPMenu::on_mouse_down(const ws_event_t *e) 756 { 757 if (nMBState == 0) 758 { 759 if (!inside(e->nLeft, e->nTop)) 760 { 761 hide(); 762 return STATUS_OK; 763 } 764 } 765 766 nMBState |= (1 << e->nCode); 767 ssize_t iy = 0; 768 ssize_t sel = find_item(e->nLeft, e->nTop, &iy); 769 selection_changed(sel, iy); 770 771 return STATUS_OK; 772 } 773 on_mouse_up(const ws_event_t * e)774 status_t LSPMenu::on_mouse_up(const ws_event_t *e) 775 { 776 if ((nMBState == (1 << MCB_LEFT)) && (e->nCode == MCB_LEFT)) 777 { 778 LSPMenu *parent = this; 779 while (parent->pParentMenu != NULL) 780 parent = parent->pParentMenu; 781 782 // Cleanup mouse button state flag 783 nMBState &= ~ (1 << e->nCode); 784 785 // Selection was found ? 786 LSPMenuItem *item = NULL; 787 ssize_t iy = 0; 788 ssize_t sel = find_item(e->nLeft, e->nTop, &iy); 789 790 // Notify that selection has changed 791 selection_changed(sel, iy); 792 793 if (sel >= 0) 794 { 795 item = vItems.get(sel); 796 if (item == NULL) 797 parent->hide(); 798 else if (item->hidden()) 799 { 800 item = NULL; 801 parent->hide(); 802 } 803 else if (!item->has_submenu()) 804 parent->hide(); 805 806 if (item != NULL) 807 { 808 ws_event_t ev = *e; 809 item->slots()->execute(LSPSLOT_SUBMIT, item, &ev); 810 } 811 } 812 else 813 { 814 if ((sel != SEL_TOP_SCROLL) && (sel != SEL_BOTTOM_SCROLL)) 815 parent->hide(); 816 } 817 } 818 else 819 { 820 // Cleanup mouse button state flag 821 nMBState &= ~ (1 << e->nCode); 822 if (nMBState == 0) 823 hide(); 824 } 825 826 return STATUS_OK; 827 } 828 on_mouse_scroll(const ws_event_t * e)829 status_t LSPMenu::on_mouse_scroll(const ws_event_t *e) 830 { 831 font_parameters_t fp; 832 sFont.get_parameters(&fp); 833 ssize_t amount = fp.Height + nSpacing; 834 if (amount < 1) 835 amount = 1; 836 837 ssize_t scroll = nScroll; 838 if (e->nCode == MCD_UP) 839 set_scroll(nScroll - amount); 840 else if (e->nCode == MCD_DOWN) 841 set_scroll(nScroll + amount); 842 843 if (scroll != nScroll) 844 { 845 ssize_t sel = nSelected, iy = 0; 846 nSelected = find_item(e->nLeft, e->nTop, &iy); 847 848 if (sel != nSelected) 849 { 850 // Notify that selection has changed 851 selection_changed(nSelected, iy); 852 853 // Query for draw 854 query_draw(); 855 if (pWindow != NULL) 856 pWindow->query_draw(); 857 } 858 } 859 860 return STATUS_OK; 861 } 862 selection_changed(ssize_t sel,ssize_t iy)863 void LSPMenu::selection_changed(ssize_t sel, ssize_t iy) 864 { 865 // Get menu item 866 LSPMenuItem *item = (sel >= 0) ? vItems.get(sel) : NULL; 867 if ((item != NULL) && (pActiveMenu == item->submenu())) 868 return; 869 870 if (pActiveMenu != NULL) 871 { 872 pActiveMenu->hide(); 873 pActiveMenu = NULL; 874 } 875 876 if (item == NULL) 877 return; 878 879 if ((pActiveMenu = item->submenu()) == NULL) 880 return; 881 882 // Get screen size 883 IDisplay *dpy = pDisplay->display(); 884 ssize_t sw = 0, sh = 0; 885 dpy->screen_size(pWindow->screen(), &sw, &sh); 886 887 // Get window geometry 888 realize_t wr; 889 pWindow->get_geometry(&wr); 890 ssize_t xlast = wr.nLeft + wr.nWidth; 891 892 // Estimate active window size 893 size_request_t sr; 894 pActiveMenu->size_request(&sr); 895 if (sr.nMinWidth < 0) 896 sr.nMinWidth = 0; 897 898 if ((xlast + sr.nMinWidth) < sw) 899 pActiveMenu->show(this, xlast, iy + wr.nTop); 900 else 901 pActiveMenu->show(this, wr.nLeft - sr.nMinWidth, iy + wr.nTop); 902 } 903 on_mouse_move(const ws_event_t * e)904 status_t LSPMenu::on_mouse_move(const ws_event_t *e) 905 { 906 lsp_trace("x=%d, y=%d", int(e->nLeft), int(e->nTop)); 907 ssize_t sel = nSelected; 908 ssize_t iy = 0; 909 nSelected = find_item(e->nLeft, e->nTop, &iy); 910 911 if (sel != nSelected) 912 { 913 // lsp_trace("selection changed. Was %d, now %d", int(sel), int(nSelected)); 914 // Update timer status 915 if ((nSelected == SEL_TOP_SCROLL) || (nSelected == SEL_BOTTOM_SCROLL)) 916 sScroll.launch(0, 25); 917 else 918 { 919 sScroll.cancel(); 920 selection_changed(nSelected, iy); 921 } 922 923 // Query for draw 924 query_draw(); 925 if (pWindow != NULL) 926 pWindow->query_draw(); 927 } 928 929 return STATUS_OK; 930 } 931 timer_handler(timestamp_t time,void * arg)932 status_t LSPMenu::timer_handler(timestamp_t time, void *arg) 933 { 934 LSPMenu *_this = static_cast<LSPMenu *>(arg); 935 if (_this == NULL) 936 return STATUS_BAD_ARGUMENTS; 937 _this->update_scroll(); 938 return STATUS_OK; 939 } 940 update_scroll()941 void LSPMenu::update_scroll() 942 { 943 font_parameters_t fp; 944 sFont.get_parameters(&fp); 945 ssize_t amount = fp.Height * 0.5f; 946 if (amount < 1) 947 amount = 1; 948 949 switch (nSelected) 950 { 951 case SEL_TOP_SCROLL: 952 set_scroll(nScroll - amount); 953 if (nScroll <= 0) 954 sScroll.cancel(); 955 break; 956 957 case SEL_BOTTOM_SCROLL: 958 set_scroll(nScroll + amount); 959 if (nScroll >= nScrollMax) 960 sScroll.cancel(); 961 break; 962 963 default: 964 sScroll.cancel(); 965 break; 966 } 967 } 968 969 } /* namespace tk */ 970 } /* namespace lsp */ 971