00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 #ifndef _GNU_SOURCE
00029 #define _GNU_SOURCE
00030 #endif // _GNU_SOURCE
00031
00032 #include "Menu.hh"
00033
00034 #include "MenuItem.hh"
00035 #include "ImageControl.hh"
00036 #include "MenuTheme.hh"
00037 #include "App.hh"
00038 #include "EventManager.hh"
00039 #include "Transparent.hh"
00040 #include "SimpleCommand.hh"
00041
00042 #include <X11/Xatom.h>
00043 #include <X11/keysym.h>
00044
00045 #include <cstdio>
00046 #include <cstdlib>
00047 #include <cstring>
00048 #include <iostream>
00049
00050 using namespace std;
00051
00052 namespace FbTk {
00053
00054 static Menu *shown = 0;
00055
00056 Menu *Menu::s_focused = 0;
00057
00058 Menu::Menu(MenuTheme &tm, ImageControl &imgctrl):
00059 m_theme(tm),
00060 m_parent(0),
00061 m_image_ctrl(imgctrl),
00062 m_screen_width(DisplayWidth(FbTk::App::instance()->display(), tm.screenNum())),
00063 m_screen_height(DisplayHeight(FbTk::App::instance()->display(), tm.screenNum())),
00064 m_alignment(ALIGNDONTCARE),
00065 m_border_width(0),
00066 m_need_update(true) {
00067
00068
00069
00070 RefCount<Command> show_cmd(new SimpleCommand<Menu>(*this, &Menu::openSubmenu));
00071 m_submenu_timer.setCommand(show_cmd);
00072 m_submenu_timer.fireOnce(true);
00073
00074
00075 RefCount<Command> hide_cmd(new SimpleCommand<Menu>(*this, &Menu::closeMenu));
00076 m_hide_timer.setCommand(hide_cmd);
00077 m_hide_timer.fireOnce(true);
00078
00079
00080 tm.reconfigSig().attach(this);
00081
00082 title_vis =
00083 movable =
00084 hide_tree = true;
00085
00086 shifted =
00087 internal_menu =
00088 moving =
00089 torn =
00090 visible = false;
00091
00092
00093
00094 menu.x_shift =
00095 menu.y_shift =
00096 menu.x_move =
00097 menu.y_move = 0;
00098
00099 which_sub =
00100 which_press =
00101 which_sbl = -1;
00102
00103 menu.frame_pixmap =
00104 menu.title_pixmap =
00105 menu.hilite_pixmap =
00106 menu.sel_pixmap = None;
00107
00108 menu.bevel_w = 2;
00109
00110 menu.title_h = menu.item_w = menu.frame_h =
00111 m_theme.titleFont().height() + menu.bevel_w * 2;
00112
00113 menu.sublevels =
00114 menu.persub =
00115 menu.minsub = 0;
00116
00117 menu.item_h = m_theme.frameFont().height() + menu.bevel_w;
00118
00119 long event_mask = ButtonPressMask | ButtonReleaseMask |
00120 ButtonMotionMask | KeyPressMask | ExposureMask | FocusChangeMask;
00121
00122 menu.window = FbTk::FbWindow(tm.screenNum(),
00123 0, 0, 10, 10,
00124 event_mask,
00125 true);
00126
00127
00128 event_mask ^= FocusChangeMask;
00129
00130 FbTk::EventManager &evm = *FbTk::EventManager::instance();
00131 evm.add(*this, menu.window);
00132
00133
00134 event_mask |= EnterWindowMask | LeaveWindowMask;
00135
00136 menu.title = FbTk::FbWindow(menu.window,
00137 0, 0, width(), menu.title_h,
00138 event_mask);
00139
00140 evm.add(*this, menu.title);
00141
00142 event_mask |= PointerMotionMask;
00143 menu.frame = FbTk::FbWindow(menu.window,
00144 0, menu.title_h,
00145 width(), menu.frame_h ? menu.frame_h : 1,
00146 event_mask);
00147 evm.add(*this, menu.frame);
00148
00149 reconfigure();
00150 }
00151
00152 Menu::~Menu() {
00153 menu.window.hide();
00154
00155 if (shown && shown->window() == window())
00156 shown = 0;
00157
00158 removeAll();
00159
00160 if (menu.title_pixmap)
00161 m_image_ctrl.removeImage(menu.title_pixmap);
00162
00163 if (menu.frame_pixmap)
00164 m_image_ctrl.removeImage(menu.frame_pixmap);
00165
00166 if (menu.hilite_pixmap)
00167 m_image_ctrl.removeImage(menu.hilite_pixmap);
00168
00169 if (menu.sel_pixmap)
00170 m_image_ctrl.removeImage(menu.sel_pixmap);
00171
00172 FbTk::EventManager &evm = *FbTk::EventManager::instance();
00173 evm.remove(menu.title);
00174 evm.remove(menu.frame);
00175 evm.remove(menu.window);
00176 if (s_focused == this)
00177 s_focused = 0;
00178 }
00179
00180 int Menu::insert(const char *label, RefCount<Command> &cmd, int pos) {
00181 return insert(new MenuItem(label, cmd), pos);
00182 }
00183
00184 int Menu::insert(const char *label, int pos) {
00185 return insert(new MenuItem(label), pos);
00186 }
00187
00188 int Menu::insert(const char *label, Menu *submenu, int pos) {
00189 submenu->m_parent = this;
00190 return insert(new MenuItem(label, submenu), pos);
00191 }
00192
00193 int Menu::insert(MenuItem *item, int pos) {
00194 if (pos == -1) {
00195 menuitems.push_back(item);
00196 } else {
00197 menuitems.insert(menuitems.begin() + pos, item);
00198 }
00199 m_need_update = true;
00200 return menuitems.size();
00201 }
00202
00203 int Menu::remove(unsigned int index) {
00204 if (index >= menuitems.size()) {
00205 #ifdef DEBUG
00206 std::cout << "Bad index (" << index << ") given to Menu::remove()"
00207 << " -- should be between 0 and " << menuitems.size()
00208 << " inclusive." << std::endl;
00209 #endif // DEBUG
00210 return -1;
00211 }
00212
00213 Menuitems::iterator it = menuitems.begin() + index;
00214 MenuItem *item = (*it);
00215
00216 if (item) {
00217 menuitems.erase(it);
00218 if ((! internal_menu) && (item->submenu())) {
00219 Menu *tmp = item->submenu();
00220
00221
00222 if (! tmp->internal_menu) {
00223 delete tmp;
00224 } else
00225 tmp->internal_hide();
00226 }
00227
00228
00229 delete item;
00230 }
00231
00232 if (static_cast<unsigned int>(which_sub) == index)
00233 which_sub = -1;
00234 else if (static_cast<unsigned int>(which_sub) > index)
00235 which_sub--;
00236 m_need_update = true;
00237 return menuitems.size();
00238 }
00239
00240 void Menu::removeAll() {
00241 while (!menuitems.empty()) {
00242 remove(0);
00243 }
00244 m_need_update = true;
00245 }
00246
00247 void Menu::raise() {
00248 menu.window.raise();
00249 }
00250
00251 void Menu::lower() {
00252 menu.window.lower();
00253 }
00254
00255 void Menu::nextItem() {
00256 if (which_press >= 0 && which_press == static_cast<signed>(menuitems.size() - 1))
00257 return;
00258
00259 int old_which_press = which_press;
00260
00261 if (old_which_press >= 0 &&
00262 old_which_press < static_cast<signed>(menuitems.size()) &&
00263 menuitems[old_which_press] != 0) {
00264 if (menuitems[old_which_press]->submenu()) {
00265
00266
00267 menuitems[old_which_press]->submenu()->menu.window.hide();
00268 }
00269 drawItem(old_which_press, false, true, true);
00270 }
00271
00272
00273 which_press = old_which_press;
00274 if (which_press < 0 || which_press >= static_cast<signed>(menuitems.size()))
00275 which_press = 0;
00276 else if (which_press > 0 && which_press < static_cast<signed>(menuitems.size() - 1))
00277 which_press++;
00278
00279
00280 if (menuitems[which_press] == 0)
00281 return;
00282
00283 if (menuitems[which_press]->submenu())
00284 drawSubmenu(which_press);
00285 else
00286 drawItem(which_press, true, true, true);
00287
00288 }
00289
00290 void Menu::prevItem() {
00291
00292 int old_which_press = which_press;
00293
00294 if (old_which_press >= 0 && old_which_press < static_cast<signed>(menuitems.size())) {
00295 if (menuitems[old_which_press]->submenu()) {
00296
00297
00298 menuitems[old_which_press]->submenu()->menu.window.hide();
00299 }
00300 drawItem(old_which_press, false, true, true);
00301 }
00302
00303 which_press = old_which_press;
00304
00305 if (which_press < 0 || which_press >= static_cast<signed>(menuitems.size()))
00306 which_press = 0;
00307 else if (which_press - 1 >= 0)
00308 which_press--;
00309
00310 if (menuitems[which_press] != 0) {
00311 if (menuitems[which_press]->submenu())
00312 drawSubmenu(which_press);
00313 else
00314 drawItem(which_press, true, true, true);
00315 }
00316
00317 }
00318
00319 void Menu::enterSubmenu() {
00320 if (which_press < 0 || which_press >= static_cast<signed>(menuitems.size()))
00321 return;
00322
00323 Menu *submenu = menuitems[which_press]->submenu();
00324 if (submenu == 0)
00325 return;
00326
00327 submenu->grabInputFocus();
00328 submenu->which_press = -1;
00329 submenu->nextItem();
00330 }
00331
00332 void Menu::enterParent() {
00333 if (which_press < 0 || which_press >= static_cast<signed>(menuitems.size()) || parent() == 0)
00334 return;
00335
00336 Menu *submenu = menuitems[which_press]->submenu();
00337 if (submenu)
00338 submenu->menu.window.hide();
00339
00340 drawItem(which_press, false, true, true);
00341 which_press = -1;
00342
00343 parent()->grabInputFocus();
00344 }
00345
00346 void Menu::disableTitle() {
00347 setTitleVisibility(false);
00348 }
00349
00350 void Menu::enableTitle() {
00351 setTitleVisibility(true);
00352 }
00353
00354 void Menu::update(int active_index) {
00355
00356 if (menu.bevel_w > 10)
00357 menu.bevel_w = 10;
00358 if (m_border_width > 20)
00359 m_border_width = 20;
00360
00361 menu.item_h = m_theme.frameFont().height() + menu.bevel_w;
00362 menu.title_h = m_theme.titleFont().height() + menu.bevel_w*2;
00363
00364 if (title_vis) {
00365 menu.item_w = m_theme.frameFont().textWidth(menu.label.c_str(), menu.label.size());
00366
00367 menu.item_w += (menu.bevel_w * 2);
00368 } else
00369 menu.item_w = 1;
00370
00371 int ii = 0;
00372 Menuitems::iterator it = menuitems.begin();
00373 Menuitems::iterator it_end = menuitems.end();
00374 for (; it != it_end; ++it) {
00375 MenuItem *itmp = (*it);
00376
00377 const char *s = itmp->label().c_str();
00378 int l = itmp->label().size();
00379
00380 ii = m_theme.frameFont().textWidth(s, l);
00381
00382
00383 ii += (menu.bevel_w * 2) + (menu.item_h * 2);
00384
00385 menu.item_w = ((menu.item_w < (unsigned int) ii) ? ii : menu.item_w);
00386 }
00387
00388 if (menuitems.size()) {
00389 menu.sublevels = 1;
00390
00391 while (menu.item_h * (menuitems.size() + 1) / menu.sublevels +
00392 menu.title_h + m_border_width > m_screen_height) {
00393 menu.sublevels++;
00394 }
00395
00396 if (menu.sublevels < menu.minsub)
00397 menu.sublevels = menu.minsub;
00398
00399 menu.persub = menuitems.size() / menu.sublevels;
00400 if (menuitems.size() % menu.sublevels) menu.persub++;
00401 } else {
00402 menu.sublevels = 0;
00403 menu.persub = 0;
00404 }
00405
00406 int itmp = (menu.item_h * menu.persub);
00407 menu.frame_h = itmp < 0 ? 0 : itmp;
00408
00409 int new_width = (menu.sublevels * menu.item_w);
00410 int new_height = menu.frame_h;
00411
00412 if (title_vis)
00413 new_height += menu.title_h + ((menu.frame_h>0)?menu.title.borderWidth():0);
00414
00415
00416 if (new_width < 1)
00417 new_width = menu.item_w;
00418
00419 if (new_height < 1)
00420 new_height = 1;
00421
00422 menu.window.resize(new_width, new_height);
00423
00424 Pixmap tmp;
00425 if (title_vis) {
00426 tmp = menu.title_pixmap;
00427 const FbTk::Texture &tex = m_theme.titleTexture();
00428 if (!tex.usePixmap()) {
00429 menu.title_pixmap = None;
00430 menu.title.setBackgroundColor(tex.color());
00431 } else {
00432 menu.title_pixmap =
00433 m_image_ctrl.renderImage(width(), menu.title_h, tex);
00434 menu.title.setBackgroundPixmap(menu.title_pixmap);
00435 }
00436
00437 if (tmp)
00438 m_image_ctrl.removeImage(tmp);
00439
00440 }
00441
00442 tmp = menu.frame_pixmap;
00443 const FbTk::Texture &frame_tex = m_theme.frameTexture();
00444 if (!frame_tex.usePixmap()) {
00445 menu.frame_pixmap = None;
00446 } else {
00447 menu.frame_pixmap =
00448 m_image_ctrl.renderImage(width(), menu.frame_h, frame_tex);
00449 }
00450
00451 if (tmp)
00452 m_image_ctrl.removeImage(tmp);
00453
00454 tmp = menu.hilite_pixmap;
00455 const FbTk::Texture &hilite_tex = m_theme.hiliteTexture();
00456 if (!hilite_tex.usePixmap()) {
00457 menu.hilite_pixmap = None;
00458 } else
00459 menu.hilite_pixmap =
00460 m_image_ctrl.renderImage(menu.item_w, menu.item_h, hilite_tex);
00461 if (tmp)
00462 m_image_ctrl.removeImage(tmp);
00463
00464 tmp = menu.sel_pixmap;
00465 if (!hilite_tex.usePixmap()) {
00466 menu.sel_pixmap = None;
00467 } else {
00468 int hw = menu.item_h / 2;
00469 menu.sel_pixmap =
00470 m_image_ctrl.renderImage(hw, hw, hilite_tex);
00471 }
00472 if (tmp)
00473 m_image_ctrl.removeImage(tmp);
00474
00475
00476
00477 if (title_vis) {
00478 menu.title.moveResize(-menu.title.borderWidth(), -menu.title.borderWidth(),
00479 width() + menu.title.borderWidth(), menu.title_h);
00480 }
00481
00482 menu.frame.moveResize(0, ((title_vis) ? menu.title.y() + menu.title.height() +
00483 menu.title.borderWidth()*2 : 0),
00484 menu.window.width(), menu.frame_h);
00485
00486
00487 Display *disp = FbTk::App::instance()->display();
00488
00489 XWindowAttributes attr;
00490
00491 if (m_need_update && (m_frame_pm.width() != menu.frame.width() ||
00492 m_frame_pm.height() != menu.frame.height() )){
00493 XGetWindowAttributes(disp, menu.frame.window(), &attr);
00494 m_frame_pm = FbTk::FbPixmap(menu.frame.window(),
00495 menu.frame.width(), menu.frame.height(),
00496 attr.depth);
00497
00498 if (m_frame_pm.drawable() == 0) {
00499 cerr<<"FbTk::Menu: Warning: Failed to create pixmap ("<<
00500 menu.frame.window()<<", "<<menu.frame.width()<<", "<<
00501 menu.frame.height()<<
00502 ", "<<attr.depth<<") !"<<endl;
00503 }
00504
00505
00506 }
00507
00508 menu.frame.setBackgroundPixmap(m_frame_pm.drawable());
00509
00510 clearWindow();
00511
00512 if (title_vis && visible)
00513 redrawTitle();
00514
00515 if (active_index >= 0) {
00516 for (unsigned int i = 0; visible && i < menuitems.size(); i++) {
00517 if (i == (unsigned int)which_sub) {
00518 drawItem(i, true, true, false);
00519 } else
00520 drawItem(i, (static_cast<signed>(i) == active_index && isItemEnabled(i)), true, false);
00521 }
00522
00523 if (m_parent && visible)
00524 m_parent->drawSubmenu(m_parent->which_sub);
00525 }
00526
00527 menu.window.clear();
00528 renderTransFrame();
00529
00530 m_need_update = false;
00531 menu.window.showSubwindows();
00532 }
00533
00534
00535 void Menu::show() {
00536 if (m_need_update)
00537 update();
00538 menu.window.showSubwindows();
00539 menu.window.show();
00541 raise();
00542 visible = true;
00543
00544 if (! m_parent && shown != this) {
00545 if (shown && (! shown->torn))
00546 shown->hide();
00547
00548 shown = this;
00549 }
00550
00551 }
00552
00553
00554 void Menu::hide() {
00555 if ((! torn) && hide_tree && m_parent && m_parent->isVisible()) {
00556 Menu *p = m_parent;
00557
00558 while (p->isVisible() && (! p->torn) && p->m_parent)
00559 p = p->m_parent;
00560 p->internal_hide();
00561 } else
00562 internal_hide();
00563
00564 }
00565
00566 void Menu::grabInputFocus() {
00567 s_focused = this;
00568
00569
00570 menu.window.setInputFocus(RevertToPointerRoot, CurrentTime);
00571
00572 }
00573
00574
00575 void Menu::clearWindow() {
00576 menu.window.clear();
00577 menu.title.clear();
00578 menu.frame.clear();
00579 }
00580
00581 void Menu::internal_hide() {
00582 if (which_sub >= 0) {
00583 MenuItem *tmp = menuitems[which_sub];
00584 tmp->submenu()->internal_hide();
00585 }
00586
00587 if (m_parent && (! torn)) {
00588 m_parent->drawItem(m_parent->which_sub, false, true);
00589
00590 m_parent->which_sub = -1;
00591 } else if (shown && shown->menu.window == menu.window)
00592 shown = (Menu *) 0;
00593
00594 torn = visible = false;
00595 which_sub = which_press = which_sub = -1;
00596
00597 menu.window.hide();
00598 }
00599
00600
00601 void Menu::move(int x, int y) {
00602
00603 menu.window.move(x, y);
00604
00605 if (which_sub != -1)
00606 drawSubmenu(which_sub);
00607
00608 if (!(m_parent && m_parent->moving) && !torn) {
00609 redrawTitle();
00610 renderTransFrame();
00611 }
00612 }
00613
00614
00615 void Menu::redrawTitle() {
00616 const char *text = menu.label.c_str();
00617
00618 const FbTk::Font &font = m_theme.titleFont();
00619 int dx = menu.bevel_w, len = menu.label.size();
00620 unsigned int l = font.textWidth(text, len) + menu.bevel_w*2;
00621
00622 switch (m_theme.titleFontJustify()) {
00623 case FbTk::RIGHT:
00624 dx += width() - l;
00625 break;
00626
00627 case FbTk::CENTER:
00628 dx += (width() - l) / 2;
00629 break;
00630 default:
00631 break;
00632 }
00633 menu.title.clear();
00634 font.drawText(menu.title.window(),
00635 screenNumber(),
00636 m_theme.titleTextGC().gc(),
00637 text, len,
00638 dx, font.ascent() + menu.bevel_w);
00639
00640 menu.title.updateTransparent();
00641
00642 }
00643
00644
00645 void Menu::drawSubmenu(unsigned int index) {
00646 if (which_sub >= 0 && static_cast<unsigned int>(which_sub) != index &&
00647 static_cast<unsigned int>(which_sub) < menuitems.size()) {
00648 MenuItem *itmp = menuitems[which_sub];
00649
00650 if (! itmp->submenu()->isTorn())
00651 itmp->submenu()->internal_hide();
00652 }
00653
00654 if (index >= menuitems.size())
00655 return;
00656
00657 MenuItem *item = menuitems[index];
00658 if (item->submenu() && visible && (! item->submenu()->isTorn()) &&
00659 item->isEnabled()) {
00660
00661 if (item->submenu()->m_parent != this)
00662 item->submenu()->m_parent = this;
00663
00664 int sbl = index / menu.persub, i = index - (sbl * menu.persub);
00665 int new_x = x() + ((menu.item_w * (sbl + 1)) + menu.window.borderWidth());
00666 int new_y;
00667
00668 if (m_alignment == ALIGNTOP) {
00669 new_y = (((shifted) ? menu.y_shift : y()) +
00670 ((title_vis) ? menu.title_h + menu.title.borderWidth() : 0) -
00671 ((item->submenu()->title_vis) ?
00672 item->submenu()->menu.title_h + menu.window.borderWidth() : 0));
00673 } else {
00674 new_y = (((shifted) ? menu.y_shift : y()) +
00675 (menu.item_h * i) +
00676 ((title_vis) ? menu.title_h + menu.window.borderWidth() : 0) -
00677 ((item->submenu()->title_vis) ?
00678 item->submenu()->menu.title_h + menu.window.borderWidth() : 0));
00679 }
00680
00681 if (m_alignment == ALIGNBOTTOM &&
00682 (new_y + item->submenu()->height()) > ((shifted) ? menu.y_shift :
00683 y()) + height()) {
00684 new_y = (((shifted) ? menu.y_shift : y()) +
00685 height() - item->submenu()->height());
00686 }
00687
00688 if ((new_x + item->submenu()->width()) > m_screen_width) {
00689 new_x = ((shifted) ? menu.x_shift : x()) -
00690 item->submenu()->width() - menu.window.borderWidth();
00691 }
00692
00693 if (new_x < 0)
00694 new_x = 0;
00695
00696 if ((new_y + item->submenu()->height()) > m_screen_height) {
00697 new_y = m_screen_height - item->submenu()->height() -
00698 menu.window.borderWidth() * 2;
00699 }
00700
00701 if (new_y < 0)
00702 new_y = 0;
00703
00704 item->submenu()->move(new_x, new_y);
00705 if (! moving)
00706 drawItem(index, true);
00707
00708 if (! item->submenu()->isVisible()) {
00709 item->submenu()->show();
00710 item->submenu()->raise();
00711 }
00712
00713 item->submenu()->moving = moving;
00714 which_sub = index;
00715 } else
00716 which_sub = -1;
00717
00718 }
00719
00720
00721 bool Menu::hasSubmenu(unsigned int index) const {
00722 if (index >= menuitems.size())
00723 return false;
00724
00725 if (!menuitems[index]->submenu())
00726 return false;
00727
00728 return true;
00729 }
00730
00731
00732 void Menu::drawItem(unsigned int index, bool highlight, bool clear, bool render_trans,
00733 int x, int y, unsigned int w, unsigned int h) {
00734 if (index >= menuitems.size() || menuitems.size() == 0 ||
00735 menu.persub == 0)
00736 return;
00737
00738 MenuItem *item = menuitems[index];
00739 if (! item) return;
00740
00741 bool dotext = true, dohilite = true, dosel = true;
00742 const char *text = item->label().c_str();
00743 int sbl = index / menu.persub, i = index - (sbl * menu.persub);
00744 int item_x = (sbl * menu.item_w), item_y = (i * menu.item_h);
00745 int hilite_x = item_x, hilite_y = item_y, hoff_x = 0, hoff_y = 0;
00746 int text_x = 0, text_y = 0, len = strlen(text), sel_x = 0, sel_y = 0;
00747 unsigned int hilite_w = menu.item_w, hilite_h = menu.item_h, text_w = 0, text_h = 0;
00748 unsigned int half_w = menu.item_h / 2, quarter_w = menu.item_h / 4;
00749 const FbTk::Font &font = m_theme.frameFont();
00750 if (text) {
00751 text_w = font.textWidth(text, len);
00752
00753 text_y = item_y + menu.bevel_w/2 + font.ascent();
00754
00755 switch(m_theme.frameFontJustify()) {
00756 case FbTk::LEFT:
00757 text_x = item_x + menu.bevel_w + menu.item_h + 1;
00758 break;
00759
00760 case FbTk::RIGHT:
00761 text_x = item_x + menu.item_w - (menu.item_h + menu.bevel_w + text_w);
00762 break;
00763 default:
00764 text_x = item_x + ((menu.item_w + 1 - text_w) / 2);
00765 break;
00766 }
00767
00768 text_h = menu.item_h - menu.bevel_w;
00769 }
00770
00771 GC gc =
00772 ((highlight || item->isSelected()) ? m_theme.hiliteTextGC().gc() :
00773 m_theme.frameTextGC().gc());
00774 const GContext &tgc =
00775 (highlight ? m_theme.hiliteTextGC() :
00776 (item->isEnabled() ? m_theme.frameTextGC() : m_theme.disableTextGC() ) );
00777
00778 sel_x = item_x;
00779
00780 if (m_theme.bulletPos() == FbTk::RIGHT)
00781 sel_x += (menu.item_w - menu.item_h - menu.bevel_w);
00782
00783 sel_x += quarter_w;
00784 sel_y = item_y + quarter_w;
00785
00786 if (clear) {
00787 FbTk::GContext def_gc(menu.frame.window());
00788 if (menu.frame_pixmap == 0) {
00789 def_gc.setForeground(m_theme.frameTexture().color());
00790 m_frame_pm.fillRectangle(def_gc.gc(), item_x, item_y, menu.item_w, menu.item_h);
00791
00792 } else {
00793
00794 m_frame_pm.copyArea(menu.frame_pixmap, def_gc.gc(),
00795 item_x, item_y,
00796 item_x, item_y,
00797 menu.item_w, menu.item_h);
00798 }
00799 } else if (! (x == y && y == -1 && w == h && h == 0)) {
00800
00801 if (! (std::max(item_x, x) <= (signed) std::min(item_x + menu.item_w, x + w) &&
00802 std::max(item_y, y) <= (signed) std::min(item_y + menu.item_h, y + h))) {
00803 dohilite = False;
00804 } else {
00805 hilite_x = std::max(item_x, x);
00806 hilite_y = std::max(item_y, y);
00807 hilite_w = std::min(item_x + menu.item_w, x + w) - hilite_x;
00808 hilite_h = std::min(item_y + menu.item_h, y + h) - hilite_y;
00809 hoff_x = hilite_x % menu.item_w;
00810 hoff_y = hilite_y % menu.item_h;
00811 }
00812
00813
00814 int text_ry = item_y + (menu.bevel_w / 2);
00815 if (! (std::max(text_x, x) <= (signed) std::min(text_x + text_w, x + w) &&
00816 std::max(text_ry, y) <= (signed) std::min(text_ry + text_h, y + h)))
00817 dotext = false;
00818
00819
00820 if (! (std::max(sel_x, x) <= (signed) std::min(sel_x + half_w, x + w) &&
00821 std::max(sel_y, y) <= (signed) std::min(sel_y + half_w, y + h)))
00822 dosel = false;
00823
00824 }
00825
00826 if (dohilite && highlight && (menu.hilite_pixmap != ParentRelative)) {
00827 if (menu.hilite_pixmap) {
00828 m_frame_pm.copyArea(menu.hilite_pixmap,
00829 m_theme.hiliteGC().gc(), hoff_x, hoff_y,
00830 hilite_x, hilite_y,
00831 hilite_w, hilite_h);
00832 } else {
00833 m_frame_pm.fillRectangle(m_theme.hiliteGC().gc(),
00834 hilite_x, hilite_y, hilite_w, hilite_h);
00835 }
00836
00837 }
00838
00839
00840 if (item->isToggleItem() && item->isSelected() &&
00841 menu.sel_pixmap != ParentRelative) {
00842 if (m_theme.selectedPixmap().pixmap().drawable()) {
00843
00844 XSetClipMask(FbTk::App::instance()->display(),
00845 gc,
00846 m_theme.selectedPixmap().mask().drawable());
00847 XSetClipOrigin(FbTk::App::instance()->display(),
00848 gc, sel_x, item_y);
00849
00850 m_frame_pm.copyArea(m_theme.selectedPixmap().pixmap().drawable(),
00851 gc,
00852 0, 0,
00853 sel_x, item_y,
00854 m_theme.selectedPixmap().width(),
00855 m_theme.selectedPixmap().height());
00856
00857 XSetClipMask(FbTk::App::instance()->display(),
00858 gc,
00859 None);
00860 } else {
00861 if (menu.sel_pixmap) {
00862 m_frame_pm.copyArea(highlight ? menu.frame_pixmap : menu.sel_pixmap,
00863 m_theme.hiliteGC().gc(),
00864 0, 0,
00865 sel_x, sel_y,
00866 half_w, half_w);
00867 } else {
00868 m_frame_pm.fillRectangle(m_theme.hiliteGC().gc(),
00869 sel_x, sel_y, half_w, half_w);
00870 }
00871 }
00872
00873 } else if (item->isToggleItem() && m_theme.unselectedPixmap().pixmap().drawable() != 0) {
00874
00875 XSetClipMask(FbTk::App::instance()->display(),
00876 gc,
00877 m_theme.unselectedPixmap().mask().drawable());
00878 XSetClipOrigin(FbTk::App::instance()->display(),
00879 gc, sel_x, item_y);
00880
00881 m_frame_pm.copyArea(m_theme.unselectedPixmap().pixmap().drawable(),
00882 gc,
00883 0, 0,
00884 sel_x, item_y,
00885 m_theme.unselectedPixmap().width(),
00886 m_theme.unselectedPixmap().height());
00887
00888 XSetClipMask(FbTk::App::instance()->display(),
00889 gc,
00890 None);
00891 }
00892
00893 if (dotext && text) {
00895
00896 if (strcmp(text, "---") == 0){
00897 m_frame_pm.drawRectangle(tgc.gc(),
00898 item_x + menu.bevel_w + menu.item_h + 1, item_y + (menu.item_h / 2),
00899 menu.item_w - ((menu.bevel_w + menu.item_h) * 2) - 1, 0);
00900 } else {
00901 m_theme.frameFont().drawText(m_frame_pm.drawable(),
00902 screenNumber(),
00903 tgc.gc(),
00904 text, len,
00905 text_x, text_y);
00906 }
00907 }
00908
00909 if (dosel && item->submenu()) {
00910 if (m_theme.bulletPixmap().pixmap().drawable() != 0) {
00911
00912 XSetClipMask(FbTk::App::instance()->display(),
00913 gc,
00914 m_theme.bulletPixmap().mask().drawable());
00915 XSetClipOrigin(FbTk::App::instance()->display(),
00916 gc, sel_x, item_y);
00917
00918 m_frame_pm.copyArea(m_theme.bulletPixmap().pixmap().drawable(),
00919 gc,
00920 0, 0,
00921 sel_x, item_y,
00922 m_theme.bulletPixmap().width(),
00923 m_theme.bulletPixmap().height());
00924
00925 XSetClipMask(FbTk::App::instance()->display(),
00926 gc,
00927 None);
00928 } else {
00929 switch (m_theme.bullet()) {
00930 case MenuTheme::SQUARE:
00931 m_frame_pm.drawRectangle(gc, sel_x, sel_y, half_w, half_w);
00932 break;
00933
00934 case MenuTheme::TRIANGLE:
00935 XPoint tri[3];
00936
00937 if (m_theme.bulletPos() == FbTk::RIGHT) {
00938 tri[0].x = sel_x + quarter_w - 2;
00939 tri[0].y = sel_y + quarter_w - 2;
00940 tri[1].x = 4;
00941 tri[1].y = 2;
00942 tri[2].x = -4;
00943 tri[2].y = 2;
00944 } else {
00945 tri[0].x = sel_x + quarter_w - 2;
00946 tri[0].y = item_y + half_w;
00947 tri[1].x = 4;
00948 tri[1].y = 2;
00949 tri[2].x = 0;
00950 tri[2].y = -4;
00951 }
00952
00953 m_frame_pm.fillPolygon(gc, tri, 3, Convex,
00954 CoordModePrevious);
00955 break;
00956
00957 case MenuTheme::DIAMOND:
00958 XPoint dia[4];
00959
00960 dia[0].x = sel_x + quarter_w - 3;
00961 dia[0].y = item_y + half_w;
00962 dia[1].x = 3;
00963 dia[1].y = -3;
00964 dia[2].x = 3;
00965 dia[2].y = 3;
00966 dia[3].x = -3;
00967 dia[3].y = 3;
00968
00969 m_frame_pm.fillPolygon(gc, dia, 4, Convex,
00970 CoordModePrevious);
00971 break;
00972 default:
00973 break;
00974 }
00975 }
00976 }
00977
00978 menu.frame.clearArea(item_x, item_y,
00979 menu.item_w, menu.item_h, False);
00980
00981
00982 menu.frame.updateTransparent(item_x, item_y,
00983 menu.item_w, menu.item_h);
00984
00985 }
00986
00987 void Menu::setLabel(const char *labelstr) {
00988
00989 menu.label = (labelstr ? labelstr : "");
00990 reconfigure();
00991 }
00992
00993
00994 void Menu::setItemSelected(unsigned int index, bool sel) {
00995 if (index >= menuitems.size()) return;
00996
00997 MenuItem *item = find(index);
00998 if (! item) return;
00999
01000 item->setSelected(sel);
01001
01002 }
01003
01004
01005 bool Menu::isItemSelected(unsigned int index) const{
01006 if (index >= menuitems.size()) return false;
01007
01008 const MenuItem *item = find(index);
01009 if (!item)
01010 return false;
01011
01012 return item->isSelected();
01013 }
01014
01015
01016 void Menu::setItemEnabled(unsigned int index, bool enable) {
01017 if (index >= menuitems.size()) return;
01018
01019 MenuItem *item = find(index);
01020 if (! item) return;
01021
01022 item->setEnabled(enable);
01023
01024 }
01025
01026
01027 bool Menu::isItemEnabled(unsigned int index) const {
01028 if (index >= menuitems.size()) return false;
01029
01030 const MenuItem *item = find(index);
01031 if (!item)
01032 return false;
01033
01034 return item->isEnabled();
01035 }
01036
01037 void Menu::handleEvent(XEvent &event) {
01038 if (event.type == FocusOut) {
01039 if (s_focused == this)
01040 s_focused = 0;
01041 } else if (event.type == FocusIn) {
01042 if (s_focused != this)
01043 s_focused = this;
01044 }
01045 }
01046
01047 void Menu::buttonPressEvent(XButtonEvent &be) {
01048 if (be.window == menu.title)
01049 grabInputFocus();
01050
01051 if (be.window == menu.frame && menu.item_h != 0 && menu.item_w != 0) {
01052
01053 int sbl = (be.x / menu.item_w), i = (be.y / menu.item_h);
01054 int w = (sbl * menu.persub) + i;
01055
01056 if (w < static_cast<int>(menuitems.size()) && w >= 0) {
01057 which_press = i;
01058 which_sbl = sbl;
01059
01060 MenuItem *item = menuitems[w];
01061
01062 if (item->submenu()) {
01063 if (!item->submenu()->isVisible())
01064 drawSubmenu(w);
01065 } else
01066 drawItem(w, item->isEnabled(), true, true);
01067 }
01068 } else {
01069 menu.x_move = be.x_root - x();
01070 menu.y_move = be.y_root - y();
01071 }
01072 }
01073
01074
01075 void Menu::buttonReleaseEvent(XButtonEvent &re) {
01076 if (re.window == menu.title) {
01077 if (moving) {
01078 moving = false;
01079
01080 if (which_sub >= 0)
01081 drawSubmenu(which_sub);
01082 update();
01083 }
01084
01085 if (re.x >= 0 && re.x <= (signed) width() &&
01086 re.y >= 0 && re.y <= (signed) menu.title_h &&
01087 re.button == 3)
01088 hide();
01089
01090 } else if (re.window == menu.frame &&
01091 re.x >= 0 && re.x < (signed) width() &&
01092 re.y >= 0 && re.y < (signed) menu.frame_h) {
01093
01094 int sbl = (re.x / menu.item_w), i = (re.y / menu.item_h),
01095 ix = sbl * menu.item_w, iy = i * menu.item_h,
01096 w = (sbl * menu.persub) + i,
01097 p = (which_sbl * menu.persub) + which_press;
01098
01099 if (w < static_cast<int>(menuitems.size()) && w >= 0) {
01100 if (p == w && isItemEnabled(w)) {
01101 if (re.x > ix && re.x < (signed) (ix + menu.item_w) &&
01102 re.y > iy && re.y < (signed) (iy + menu.item_h)) {
01103 menuitems[w]->click(re.button, re.time);
01104 itemSelected(re.button, w);
01105
01106
01107 update(w);
01108 }
01109 } else {
01110 drawItem(p, isItemEnabled(p) && (p == which_sub), true, true);
01111 }
01112 } else
01113 drawItem(p, false, true, true);
01114 }
01115 }
01116
01117
01118 void Menu::motionNotifyEvent(XMotionEvent &me) {
01119
01120 if (me.window == menu.title && (me.state & Button1Mask)) {
01121 stopHide();
01122 if (movable) {
01123 if (! moving) {
01124 if (m_parent && (! torn)) {
01125 m_parent->drawItem(m_parent->which_sub, false, true, true);
01126 m_parent->which_sub = -1;
01127 }
01128
01129 moving = torn = true;
01130
01131 if (which_sub >= 0)
01132 drawSubmenu(which_sub);
01133 } else {
01134 menu.window.move(me.x_root - menu.x_move, me.y_root - menu.y_move);
01135
01136
01137
01138 }
01139 }
01140 } else if ((! (me.state & Button1Mask)) && me.window == menu.frame &&
01141 me.x >= 0 && me.x < (signed) width() &&
01142 me.y >= 0 && me.y < (signed) menu.frame_h) {
01143 stopHide();
01144 int sbl = (me.x / menu.item_w), i = (me.y / menu.item_h),
01145 w = (sbl * menu.persub) + i;
01146
01147 if ((i != which_press || sbl != which_sbl) &&
01148 (w < static_cast<int>(menuitems.size()) && w >= 0)) {
01149
01150 if (which_press != -1 && which_sbl != -1) {
01151
01152 int p = which_sbl * menu.persub + which_press;
01153 MenuItem *item = menuitems[p];
01154
01155 if (item != 0 && item->isEnabled()) {
01156
01157 drawItem(p, false, true, true);
01158
01159 if (item->submenu()) {
01160
01161 if (item->submenu()->isVisible() &&
01162 !item->submenu()->isTorn()) {
01163
01164 item->submenu()->startHide();
01165 }
01166 }
01167
01168 }
01169
01170 }
01171
01172 which_press = i;
01173 which_sbl = sbl;
01174
01175 MenuItem *itmp = menuitems[w];
01176
01177 if (itmp->submenu()) {
01178
01179 drawItem(w, true);
01180
01181 if (theme().menuMode() == MenuTheme::DELAY_OPEN) {
01182
01183 timeval timeout;
01184 timeout.tv_sec = 0;
01185 timeout.tv_usec = theme().delayOpen() * 1000;
01186 m_submenu_timer.setTimeout(timeout);
01187 m_submenu_timer.start();
01188
01189 }
01190
01191 } else {
01192 m_submenu_timer.stop();
01193 if (itmp->isEnabled())
01194 drawItem(w, true, true, true);
01195 }
01196 }
01197 }
01198 }
01199
01200
01201 void Menu::exposeEvent(XExposeEvent &ee) {
01202 if (ee.window == menu.title) {
01203 redrawTitle();
01204 } else if (ee.window == menu.frame) {
01205
01206
01207
01208 if (menu.item_w == 0)
01209 menu.item_w = 1;
01210 if (menu.item_h == 0)
01211 menu.item_h = 1;
01212 unsigned int sbl = (ee.x / menu.item_w), id = (ee.y / menu.item_h),
01213
01214 sbl_d = ((ee.x + ee.width) / menu.item_w),
01215
01216 id_d = ((ee.y + ee.height) / menu.item_h);
01217 if (static_cast<signed>(id_d) > menu.persub)
01218 id_d = menu.persub;
01219
01220
01221 unsigned int i, ii;
01222 for (i = sbl; i <= sbl_d; i++) {
01223
01224 unsigned int index = id + i * menu.persub;
01225 if (index < menuitems.size()) {
01226 Menuitems::iterator it = menuitems.begin() + index;
01227 Menuitems::iterator it_end = menuitems.end();
01228 for (ii = id; ii <= id_d && it != it_end; ++it, ii++) {
01229 unsigned int index = ii + (i * menu.persub);
01230 drawItem(index, (which_sub == static_cast<signed>(index)), true, true,
01231 ee.x, ee.y, ee.width, ee.height);
01232 }
01233 }
01234 }
01235 }
01236 }
01237
01238
01239 void Menu::enterNotifyEvent(XCrossingEvent &ce) {
01240
01241 if (menu.frame != ce.window)
01242 return;
01243
01244 menu.x_shift = x(), menu.y_shift = y();
01245 if (x() + width() > m_screen_width) {
01246 menu.x_shift = m_screen_width - width() - 2*m_border_width;
01247 shifted = true;
01248 } else if (x() < 0) {
01249 menu.x_shift = 0;
01250 shifted = true;
01251 }
01252
01253 if (y() + height() + 2*m_border_width > m_screen_height) {
01254 menu.y_shift = m_screen_height - height() - 2*m_border_width;
01255 shifted = true;
01256 } else if (y() + (signed) menu.title_h < 0) {
01257 menu.y_shift = 0;
01258 shifted = true;
01259 }
01260
01261
01262 if (shifted)
01263 menu.window.move(menu.x_shift, menu.y_shift);
01264
01265 if (which_sub >= 0 && static_cast<size_t>(which_sub) < menuitems.size()) {
01266 MenuItem *tmp = menuitems[which_sub];
01267 if (tmp->submenu()->isVisible()) {
01268 int sbl = (ce.x / menu.item_w), i = (ce.y / menu.item_h),
01269 w = (sbl * menu.persub) + i;
01270
01271 if (w != which_sub && (! tmp->submenu()->isTorn())) {
01272 tmp->submenu()->internal_hide();
01273
01274 drawItem(which_sub, false, true, true);
01275 which_sub = -1;
01276 }
01277 }
01278 }
01279 }
01280
01281 void Menu::leaveNotifyEvent(XCrossingEvent &ce) {
01282 if (menu.frame != ce.window)
01283 return;
01284
01285 if (which_press != -1 && which_sbl != -1 && menuitems.size() > 0) {
01286 int p = (which_sbl * menu.persub) + which_press;
01287
01288 drawItem(p, (p == which_sub), true, true);
01289
01290 which_sbl = which_press = -1;
01291 }
01292
01293 if (shifted) {
01294
01295 shifted = false;
01296 }
01297 }
01298
01299 void Menu::keyPressEvent(XKeyEvent &event) {
01300 KeySym ks;
01301 char keychar[1];
01302 XLookupString(&event, keychar, 1, &ks, 0);
01303
01304 if (IsModifierKey(ks))
01305 return;
01306 if (event.state)
01307 return;
01308
01309 switch (ks) {
01310 case XK_Up:
01311 prevItem();
01312 break;
01313 case XK_Down:
01314 nextItem();
01315 break;
01316 case XK_Left:
01317 enterParent();
01318 break;
01319 case XK_Right:
01320 enterSubmenu();
01321 break;
01322 case XK_Escape:
01323 hide();
01324 break;
01325 case XK_Return:
01326
01327 if (which_press >= 0 && which_press < static_cast<signed>(menuitems.size())) {
01328 menuitems[which_press]->click(1, event.time);
01329 itemSelected(1, which_press);
01330 m_need_update = true;
01331 update();
01332 }
01333 break;
01334 default:
01335 break;
01336 }
01337 }
01338
01339 void Menu::reconfigure() {
01340 m_need_update = true;
01341
01342 menu.bevel_w = m_theme.bevelWidth();
01343 m_border_width = m_theme.borderWidth();
01344
01345 if (menu.bevel_w > 10)
01346 menu.bevel_w = 10;
01347
01348 if (m_border_width > 20)
01349 m_border_width = 20;
01350 if (m_border_width < 0)
01351 m_border_width = 0;
01352
01353 menu.window.setBackgroundColor(m_theme.borderColor());
01354 menu.title.setBackgroundColor(m_theme.borderColor());
01355
01356 menu.window.setBorderColor(m_theme.borderColor());
01357 menu.title.setBorderColor(m_theme.borderColor());
01358 menu.frame.setBorderColor(m_theme.borderColor());
01359
01360 menu.window.setBorderWidth(m_border_width);
01361 menu.title.setBorderWidth(m_border_width);
01362
01363 menu.frame.setAlpha(alpha());
01364 menu.title.setAlpha(alpha());
01365 menu.window.setAlpha(alpha());
01366
01367 update();
01368 }
01369
01370 void Menu::renderTransFrame() {
01371 menu.frame.clear();
01372 menu.frame.updateTransparent();
01373 }
01374
01375 void Menu::openSubmenu() {
01376 if (!isVisible() || which_press < 0 || which_press >= static_cast<signed>(menuitems.size()) ||
01377 which_sbl < 0 || which_sbl >= static_cast<signed>(menuitems.size()))
01378 return;
01379
01380 int item = which_sbl * menu.persub + which_press;
01381 if (item < 0 || item >= static_cast<signed>(menuitems.size()))
01382 return;
01383
01384 drawItem(item, true);
01385 if (menuitems[item]->submenu() != 0 && !menuitems[item]->submenu()->isVisible())
01386 drawSubmenu(item);
01387
01388 }
01389
01390 void Menu::closeMenu() {
01391 if (isVisible() && !isTorn())
01392 internal_hide();
01393 }
01394
01395 void Menu::startHide() {
01396 timeval timeout;
01397 timeout.tv_sec = 0;
01398 timeout.tv_usec = theme().delayClose() * 1000;
01399 m_hide_timer.setTimeout(timeout);
01400 m_hide_timer.start();
01401 }
01402
01403 void Menu::stopHide() {
01404 m_hide_timer.stop();
01405 }
01406
01407 };