/* Menu.cc Copyright © 2003 David Reveman. This file is part of Waimea. Waimea is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. Waimea is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Waimea; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef HAVE_CONFIG_H # include "../config.h" #endif // HAVE_CONFIG_H extern "C" { #ifdef HAVE_STDIO_H # include #endif // HAVE_STDIO_H #ifdef STDC_HEADERS # include #endif // STDC_HEADERS #ifdef HAVE_UNISTD_H # include # include # include # include #endif // HAVE_UNISTD_H } #include "Menu.hh" Menu::Menu(WaScreen *ws, char *n) : RootWindowObject(ws, 0, MenuType, new WaStringMap(WindowIDName, n), "frame") { XSetWindowAttributes attrib_set; name = (*(ids->begin())).second; width = height = 0; x = y = 0; a_id = focus = (Window) 0; rootitem_id = rootitemlnk_id = (Window) 0; special_items = 0; special_menu_type = NoSpecialMenuType; width_factor = 1.0; if ((! ws->windowlist_menu) && (! strcmp(name, "windowlist"))) { ws->windowlist_menu = this; special_menu_type = WindowListMenuType; } else if (! strcmp(name, "clonemergelist")) { special_menu_type = CloneMergeListMenuType; } else if (! strcmp(name, "vertmergelist")) { special_menu_type = VertMergeListMenuType; } else if (! strcmp(name, "horizmergelist")) { special_menu_type = HorizMergeListMenuType; } else special_menu_type = NoSpecialMenuType; int create_mask = CWOverrideRedirect | CWEventMask | CWColormap | CWDontPropagate; if (ws->screen_depth == 32) { attrib_set.background_pixmap = None; attrib_set.background_pixel = 0x00000000; attrib_set.border_pixel = 0x00000000; create_mask |= CWBackPixel | CWBorderPixel; } else { attrib_set.background_pixmap = ParentRelative; } create_mask |= CWBackPixmap; attrib_set.colormap = ws->colormap; attrib_set.override_redirect = true; attrib_set.event_mask = NoEventMask; attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask; id = XCreateWindow(ws->display, ws->id, x, y, 1, 1, 0, ws->screen_depth, CopyFromParent, ws->visual, create_mask, &attrib_set); ws->waimea->window_table.insert(make_pair(id, this)); setStacking(ws->config.menu_stacking); } Menu::~Menu(void) { clear(); } void Menu::applyAttributes(Parser *parser, Tst *attr) { char *value; value = parser->attrGetString(attr, "inherit", NULL); if (value) { Menu *inherit_menu = ws->getMenuNamed(value, false); if (inherit_menu == this) { parser->warning("menu %s cannot inherit itself\n", name); } else if (inherit_menu) { inheritAttributes(inherit_menu); } else { if (! parser->attrGetBool(attr, "ignore_missing", false)) parser->warning("undefined menu: %s\n", value); } } width_factor = parser->attrGetDouble(attr, "width_factor", width_factor); } void Menu::clear(void) { while (! items.empty()) { items.back()->unref(); items.pop_back(); } } void Menu::inheritContent(Menu *inherit) { list::iterator it = inherit->items.begin(); for (; it != inherit->items.end(); ++it) { items.push_back((MenuItem *) (*it)->ref()); } } void Menu::inheritAttributes(Menu *inherit) { width_factor = inherit->width_factor; } void Menu::update(void) { unsigned int newwidth = 1; unsigned int newheight = 0; unsigned int calc_tmp; double _width, _top_spacing, _bottom_spacing,_left_spacing, _right_spacing; unsigned int top_spacing, bottom_spacing, left_spacing, right_spacing; style->fcalcWidth(ws->width, ws->hdpi, &_width); calc_length(style->top_spacing, style->top_spacing_u, ws->vdpi, ws->height, &_top_spacing); calc_length(style->bottom_spacing, style->bottom_spacing_u, ws->vdpi, ws->height, &_bottom_spacing); calc_length(style->left_spacing, style->left_spacing_u, ws->hdpi, ws->width, &_left_spacing); calc_length(style->right_spacing, style->right_spacing_u, ws->hdpi, ws->width, &_right_spacing); top_spacing = WA_ROUND_U(_top_spacing); bottom_spacing = WA_ROUND_U(_bottom_spacing); left_spacing = WA_ROUND_U(_left_spacing); right_spacing = WA_ROUND_U(_right_spacing); newheight = top_spacing; newwidth = WA_ROUND_U(_width * width_factor); list::iterator it = items.begin(); for (; it != items.end(); ++it) { if ((*it)->icon_image) (*it)->setDynamicGroup(DynamicGroupMenuItemIconImageType, (*it)->icon_image); if ((*it)->icon_svg) (*it)->setDynamicGroup(DynamicGroupMenuItemIconSvgType, (*it)->icon_svg); (*it)->style->calcWidth(10, ws->hdpi, &calc_tmp); if (calc_tmp > newwidth) newwidth = calc_tmp; (*it)->newy = newheight; (*it)->newx = left_spacing; (*it)->style->calcHeight(10, ws->vdpi, &calc_tmp); newheight += calc_tmp; } it = items.begin(); for (; it != items.end(); ++it) { (*it)->style->calcHeight(10, ws->vdpi, &calc_tmp); if ((*it)->width != newwidth || (*it)->height != calc_tmp || (*it)->newy != (*it)->y || (*it)->newx != (*it)->x) { (*it)->y = (*it)->newy; (*it)->x = (*it)->newx; (*it)->width = newwidth; (*it)->height = calc_tmp; XMoveResizeWindow(ws->display, (*it)->id, (*it)->x, (*it)->y, ((*it)->width)? (*it)->width: 1, ((*it)->height)? (*it)->height: 1); } } newwidth += left_spacing + right_spacing; newheight += bottom_spacing; if (newwidth != width || newheight != height) { width = newwidth; height = newheight; XResizeWindow(ws->display, id, (width)? width: 1, (height)? height: 1); } draw(); } void Menu::draw(void) { pushRenderEvent(); list::iterator it = items.begin(); for (; it != items.end(); ++it) (*it)->pushRenderEvent(); } void Menu::move(int newx, int newy) { int diff_x = newx - x; int diff_y = newy - y; if (newx != x || newy != y) { x = newx; y = newy; XMoveWindow(ws->display, id, x, y); pushRenderEvent(); list::iterator it = items.begin(); for (; it != items.end(); ++it) { (*it)->pushRenderEvent(); if ((*it)->submenu && (*it)->submenu->rootitem_id == (*it)->id) { (*it)->submenu->move((*it)->submenu->x + diff_x, (*it)->submenu->y + diff_y); } } } } void Menu::map(AWindowObject *_awo, bool focus, bool force) { Window w; int i, px, py; unsigned int ui; int workx, worky; unsigned int workw, workh; if ((! force) && mapped) return; if (ws->waimea->eh->move_resize != EndMoveResizeType) return; a_id = _awo->id; ws->getWorkareaSize(&workx, &worky, &workw, &workh); if (XQueryPointer(ws->display, ws->id, &w, &w, &px, &py, &i, &i, &ui)) { if ((py + (int) height) > ((int) workh + worky)) py -= height; if ((px + (int)width) > ((int) workw + workx)) px -= (width); } map(px, py); if (focus) focusFirst(); } void Menu::map(int mapx, int mapy) { if (items.empty()) { ws->showWarningMessage("menu=%s does not contain any items.", name); return; } ws->raiseWindow(id); move(mapx, mapy); XUngrabPointer(ws->display, CurrentTime); if (! mapped) { WaWindow *ww = NULL; EventDetail ed; focus = (Window) 0; if (a_id) { AWindowObject *awo = (AWindowObject *) ws->waimea->findWin(a_id, ANY_ACTION_WINDOW_TYPE); if (awo) ww = awo->getWindow(); } if (special_menu_type != NoSpecialMenuType) { switch (special_menu_type) { case WindowListMenuType: addWindowListItems(); break; case CloneMergeListMenuType: case VertMergeListMenuType: case HorizMergeListMenuType: if (ww) addMergeListItems(ww, special_menu_type); break; default: break; } } XMapWindow (ws->display, id); if (ww) { list::iterator it = items.begin(); for (; it != items.end(); ++it) { ed.x = ed.y = INT_MAX; ed.x11mod = ed.wamod = 0; ed.type = MenuMapNotify; ws->waimea->eh->evAct(NULL, (*it)->id, &ed); ww->addMonitor(*it, (*it)->monitor_state); if (ww->wm_icon_image) (*it)->setDynamicGroup(DynamicGroupWmIconImageType, ww->wm_icon_image); if (ww->wm_icon_svg) (*it)->setDynamicGroup(DynamicGroupWmIconSvgType, ww->wm_icon_svg); (*it)->pushRenderEvent(); } } if (rootitemlnk_id) { ed.x = ed.y = INT_MAX; ed.x11mod = ed.wamod = 0; ed.type = SubmenuMapNotify; ws->waimea->eh->evAct(NULL, rootitemlnk_id, &ed); } } mapped = true; } void Menu::unmap(void) { bool was_mapped = mapped; unmapSubmenus(NULL); mapped = false; list::iterator it = items.begin(); for (; it != items.end(); ++it) { if ((*it)->focused && (*it)->id == ws->waimea->prefocus) { ws->waimea->focusRevertFrom(ws, (*it)->id); break; } } rootitem_id = (Window) 0; if (was_mapped) { EventDetail ed; WaWindow *ww = NULL; XUnmapWindow(ws->display, id); if (a_id) { AWindowObject *awo = (AWindowObject *) ws->waimea->findWin(a_id, WindowType); if (awo) ww = awo->getWindow(); } if (special_items) removeSpecialItems(); it = items.begin(); for (; it != items.end(); ++it) { ed.x = ed.y = INT_MAX; ed.x11mod = ed.wamod = 0; ed.type = MenuUnmapNotify; ws->waimea->eh->evAct(NULL, (*it)->id, &ed); if (ww) ww->removeMonitor(*it); } if (rootitemlnk_id) { ed.x = ed.y = INT_MAX; ed.x11mod = ed.wamod = 0; ed.type = SubmenuUnmapNotify; ws->waimea->eh->evAct(NULL, rootitemlnk_id, &ed); } } a_id = (Window) 0; } void Menu::unLink(void) { rootitem_id = (Window) 0; } void Menu::unmapSubmenus(MenuItem *mi) { list::iterator it = items.begin(); for (; it != items.end(); ++it) if (mi != *it) { if ((*it)->submenu && (*it)->submenu->rootitem_id == (*it)->id) (*it)->submenu->unmap(); } } void Menu::raiseSubmenus(void) { list::iterator it = items.begin(); for (; it != items.end(); ++it) { if ((*it)->submenu && (*it)->submenu->rootitem_id == (*it)->id) { ws->raiseWindow((*it)->submenu->id, false); (*it)->submenu->raiseSubmenus(); } } } void Menu::lowerSubmenus(void) { list::iterator it = items.begin(); for (; it != items.end(); ++it) { if ((*it)->submenu && (*it)->submenu->rootitem_id == (*it)->id) { (*it)->submenu->lowerSubmenus(); ws->lowerWindow((*it)->submenu->id, false); } } } void Menu::unmapTree(void) { if (rootitem_id) { MenuItem *rootitem = (MenuItem *) ws->waimea->findWin(rootitem_id, MenuItemType); if (rootitem) rootitem->menu->unmapTree(); else unmap(); } else unmap(); } void Menu::focusFirst(void) { if (! mapped) return; list::iterator it = items.begin(); for (; it != items.end(); ++it) if ((*it)->style->focusable) { ws->waimea->focusNew((*it)->id); break; } } void Menu::drawOutline(int ox, int oy) { if (outline_state) clearOutline(); outline_state = true; int diff_x = ox - x; int diff_y = oy - y; outl_x = ox; outl_y = oy; XDrawRectangle(ws->display, ws->id, ws->xor_gc, outl_x, outl_y, width, height); list::iterator it = items.begin(); for (; it != items.end(); ++it) { if ((*it)->submenu && (*it)->submenu->rootitem_id == (*it)->id) { (*it)->submenu->drawOutline((*it)->submenu->x + diff_x, (*it)->submenu->y + diff_y); } } } void Menu::clearOutline(void) { if (! outline_state) return; outline_state = false; XDrawRectangle(ws->display, ws->id, ws->xor_gc, outl_x, outl_y, width, height); list::iterator it = items.begin(); for (; it != items.end(); ++it) { if ((*it)->submenu && (*it)->submenu->rootitem_id == (*it)->id) { (*it)->submenu->clearOutline(); } } } void Menu::removeSpecialItems(void) { while (special_items) { items.back()->unref(); items.pop_back(); special_items--; } } void Menu::addWindowListItems(void) { list::iterator it = ws->wawindow_list.begin(); for (; it != ws->wawindow_list.end() && (! ((*it)->wstate & StateTasklistMask)); ++it); if (it == ws->wawindow_list.end()) return; WaWindow *next = NULL, *last = *it; while (next != last) { it++; if (it == ws->wawindow_list.end()) next = last; else next = *it; if (! (next->wstate & StateTasklistMask)) continue; MenuItem *mi = new MenuItem(ws, this, "item"); mi->commonStyleUpdate(); delete [] mi->str; mi->str = WA_STRDUP(next->name); MenuItemAction *mia = new MenuItemAction(); mia->func = &AWindowObject::windowRaiseFocus; mi->actions.push_back(mia); mi->a_id = next->id; if (next->wm_icon_image) mi->setDynamicGroup(DynamicGroupWmIconImageType, next->wm_icon_image); if (next->wm_icon_svg) mi->setDynamicGroup(DynamicGroupWmIconSvgType, next->wm_icon_svg); items.push_back(mi); special_items++; } update(); } void Menu::addMergeListItems(WaWindow *ww, SpecialMenuType smtype) { list::iterator it = ws->wawindow_list.begin(); for (; it != ws->wawindow_list.end() && (! ((*it)->wstate & StateTasklistMask)) && (*it)->id != ww->id && (! (*it)->master); ++it); if (it == ws->wawindow_list.end()) return; WaWindow *next = NULL, *last = *it; while (next != last) { it++; if (it == ws->wawindow_list.end()) next = last; else next = *it; if (! (next->functions & FunctionMergeMask)) continue; if (next->id == ww->id) continue; if (next->master) continue; MenuItem *mi; switch (smtype) { case CloneMergeListMenuType: mi = new MenuItem(ws, this, "item"); break; case VertMergeListMenuType: mi = new MenuItem(ws, this, "item"); break; default: mi = new MenuItem(ws, this, "item"); } mi->commonStyleUpdate(); delete [] mi->str; mi->str = WA_STRDUP(next->name); mi->a_id = next->id; if (next->wm_icon_image) mi->setDynamicGroup(DynamicGroupWmIconImageType, next->wm_icon_image); if (next->wm_icon_svg) mi->setDynamicGroup(DynamicGroupWmIconSvgType, next->wm_icon_svg); items.push_back(mi); special_items++; } update(); } void Menu::styleUpdate(bool, bool) { update(); } MenuItemAction::MenuItemAction(void) { func = NULL; param = NULL; } MenuItemAction::~MenuItemAction(void) { if (param) delete [] param; } bool MenuItemAction::applyAttributes(Parser *parser, Tst *attr) { char *value = parser->attrGetString(attr, "function", NULL); if (value) { Tst::iterator it = actionfunction_tst->find(value); if (it != actionfunction_tst->end()) func = *it; if (! func) { parser->warning("unknown action function `%s'\n", value); return false; } } else return false; value = parser->attrGetString(attr, "parameter", NULL); if (value) { if (param) delete [] param; param = WA_STRDUP(value); } return true; } MenuItem::MenuItem(WaScreen *_ws, Menu *m, char *n) : DWindowObject(_ws, 0, MenuItemType, m->ids->ref(), n) { XSetWindowAttributes attrib_set; str = WA_STRDUP(""); str2 = WA_STRDUP(""); str_dynamic = str2_dynamic = false; submenuname = NULL; menu = (Menu *) m->ref(); focused = mstate = hilited = false; submenu = NULL; height = width = 0; x = y = -1; monitor_state = 0; a_id = (Window) 0; icon_image = NULL; icon_svg = NULL; int create_mask = CWOverrideRedirect | CWEventMask; attrib_set.override_redirect = true; attrib_set.event_mask = ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | KeyPressMask | KeyReleaseMask | FocusChangeMask; id = XCreateWindow(ws->display, menu->id, 0, 0, 1, 1, 0, CopyFromParent, CopyFromParent, CopyFromParent, create_mask, &attrib_set); ws->waimea->window_table.insert(make_pair(id, this)); XMapWindow(ws->display, id); hidden = false; decor_root = menu; resetStyle(); } MenuItem::~MenuItem(void) { LISTDEL(actions); delete [] str; delete [] str2; if (submenuname) delete [] submenuname; if (icon_image) icon_image->unref(); if (icon_svg) icon_svg->unref(); ws->waimea->window_table.erase(id); ws->waimea->removeFromFocusHistory(id); destroySubwindows(); XDestroyWindow(ws->display, id); menu->unref(); } void MenuItem::applyAttributes(Parser *parser, Tst *attr) { MenuItemAction *mia = new MenuItemAction(); if (mia->applyAttributes(parser, attr)) actions.push_back(mia); else delete mia; char *value = parser->attrGetString(attr, "string", NULL); if (value) { delete [] str; str = preexpand(value, &str_dynamic); } value = parser->attrGetString(attr, "string2", NULL); if (value) { delete [] str2; str2 = preexpand(value, &str2_dynamic); } value = parser->attrGetString(attr, "image", NULL); if (value) { int image_width, image_height; unsigned char *rgba = NULL; char *filename = smartfile(value, parser->filename); if (filename) { rgba = read_image_to_rgba(filename, &image_width, &image_height); if (rgba) { WaSurface *image = ws->rgbaToWaSurface(rgba, image_width, image_height); if (image) { icon_image = new RenderGroup(ws, NULL); RenderOpImage *image_op = new RenderOpImage(); image_op->image = image; image_op->scale = ImageNormalScaleType; icon_image->operations.push_back(image_op); icon_image->_w = image_width; icon_image->_h = image_height; icon_image->_x = icon_image->_y = 0; icon_image->_xu = icon_image->_yu = icon_image->_wu = icon_image->_hu = PXLenghtUnitType; } } delete [] filename; } if (! icon_image) parser->warning("failed loading image=%s\n", value); } #ifdef SVG value = parser->attrGetString(attr, "svg", NULL); if (value) { char *filename = smartfile(value, parser->filename); if (filename) { FILE *file = fopen(filename, "r"); if (file) { svg_cairo_t *cairo_svg; svg_cairo_status status; status = svg_cairo_create(&cairo_svg); if (status == SVG_CAIRO_STATUS_SUCCESS) { status = svg_cairo_parse_file(cairo_svg, file); if (status == SVG_CAIRO_STATUS_SUCCESS) { RenderOpSvg *svg_op = new RenderOpSvg(); svg_op->cairo_svg = cairo_svg; icon_svg = new RenderGroup(ws, NULL); icon_svg->operations.push_back(svg_op); icon_svg->_w = 16; icon_svg->_h = 16; icon_svg->_x = icon_svg->_y = 0; icon_svg->_xu = icon_svg->_yu = icon_svg->_wu = icon_svg->_hu = PXLenghtUnitType; } else { parser->warning("parsing of svg file %s failed\n", value); } } else { parser->warning("creation of svg context failed\n"); } fclose(file); } delete [] filename; } else parser->warning("unable to open svg file: %s\n", value); } #endif // SVG value = parser->attrGetString(attr, "submenu", NULL); if (value) { submenu = ws->getMenuNamed(submenuname, false); if (! submenu) submenuname = WA_STRDUP(value); } value = parser->attrGetString(attr, "monitor_window_state", NULL); if (value) { Tst::iterator it = client_window_state_tst->find(value); if (it != client_window_state_tst->end()) monitor_state = *it; else parser->warning("unknown window state=%s\n", value); } } void MenuItem::mapSubmenu(char *param, bool force) { int workx, worky; unsigned int workw, workh; int xoff = 0; int yoff = 0; if (! submenu) { submenu = ws->getMenuNamed(submenuname, true); if (! submenu) return; } if ((! force) && submenu->mapped) return; ws->getWorkareaSize(&workx, &worky, &workw, &workh); if (param) { double value, value_return; LenghtUnitType unit; char *yoff_str = strchr(param, ','); if (yoff_str) { value = get_double_and_unit(yoff_str + 1, &unit); calc_length(value, unit, ws->vdpi, menu->height, &value_return); yoff = WA_ROUND(value_return); *yoff_str = '\0'; } value = get_double_and_unit(param, &unit); calc_length(value, unit, ws->hdpi, menu->width, &value_return); xoff = WA_ROUND(value_return); if (yoff_str) *yoff_str = ','; if (xoff > (int) menu->width) xoff = menu->width; if (xoff < - (int) menu->width) xoff = - (int) menu->width; if (yoff > (int) menu->height) yoff = menu->height; if (yoff < - (int) menu->height) yoff = - (int) menu->height; } int mapx = menu->x + menu->width + xoff; int mapy = menu->y + y; list::iterator it = submenu->items.begin(); for (; it != submenu->items.end(); ++it) if ((*it)->style->focusable) { mapy -= (*it)->y; break; } mapy += yoff; int diff = (mapy + submenu->height) - (workh + worky); if (diff > 0) mapy -= diff; if (mapy < 0) mapy = 0; if ((mapx + (int) submenu->width) > ((int) workw + workx)) mapx = menu->x - submenu->width - xoff; submenu->rootitem_id = submenu->rootitemlnk_id = id; submenu->a_id = menu->a_id; submenu->map(mapx, mapy); if (focused) submenu->focusFirst(); } void MenuItem::perform(XEvent *e) { AWindowObject *awo = NULL; if (a_id) { awo = (AWindowObject *) ws->waimea->findWin(a_id, ANY_ACTION_WINDOW_TYPE); if (! awo) return; } else if (menu->a_id) { awo = (AWindowObject *) ws->waimea->findWin(menu->a_id, ANY_ACTION_WINDOW_TYPE); if (! awo) return; } Action *action = new Action; action->param = NULL; if (! actions.empty()) { list::iterator it = actions.begin(); for (; it != actions.end(); it++) { if (action->param) delete [] action->param; if ((*it)->param) action->param = WA_STRDUP((*it)->param); else action->param = NULL; if (awo) ((*(awo)).*((*it)->func))(e, action); else ((*ws).*((*it)->func))(e, action); } } else if (menu->special_menu_type != NoSpecialMenuType && awo && awo->type == WindowType) { WaWindow *master = (WaWindow *) awo; WaWindow *child = NULL; AWindowObject *m_awo = (AWindowObject *) ws->waimea->findWin(menu->a_id, ANY_ACTION_WINDOW_TYPE); if (m_awo) child = m_awo->getWindow(); if (child) { switch (menu->special_menu_type) { case CloneMergeListMenuType: master->merge(child, CloneMergeType); break; case VertMergeListMenuType: master->merge(child, VertMergeType); break; case HorizMergeListMenuType: master->merge(child, HorizMergeType); break; default: break; } } } action->unref(); } void MenuItem::nextItem(void) { MenuItem *first = NULL; MenuItem *next = NULL; bool find_next = false; list::iterator it = menu->items.begin(); for (; it != menu->items.end(); ++it) { if (*it == this) find_next = true; else if ((! find_next) && (! first) && (*it)->style->focusable) first = *it; else if (find_next && (*it)->style->focusable) { next = *it; break; } } if (next) ws->waimea->focusNew(next->id); else if (first) ws->waimea->focusNew(first->id); } void MenuItem::previousItem(void) { MenuItem *last = NULL; MenuItem *prev = NULL; bool find_prev = true; list::iterator it = menu->items.begin(); for (; it != menu->items.end(); ++it) { if (*it == this) find_prev = false; else if (find_prev && (*it)->style->focusable) prev = *it; else if ((*it)->style->focusable) last = *it; } if (prev) ws->waimea->focusNew(prev->id); else if (last) ws->waimea->focusNew(last->id); } void MenuItem::move(void) { XEvent event, *map_ev; int px, py, i; list *maprequest_list; int nx = menu->x; int ny = menu->y; Window w; unsigned int ui; if (ws->waimea->eh->move_resize != EndMoveResizeType) return; ws->waimea->eh->move_resize = MoveType; XQueryPointer(ws->display, ws->id, &w, &w, &px, &py, &i, &i, &ui); maprequest_list = new list; if (XGrabPointer(ws->display, id, true, ButtonReleaseMask | ButtonPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, menu->ws->id, None, CurrentTime) != GrabSuccess) { ws->waimea->eh->move_resize = EndMoveResizeType; delete maprequest_list; return; } if (XGrabKeyboard(ws->display, id, true, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) { ws->waimea->eh->move_resize = EndMoveResizeType; delete maprequest_list; return; } for (;;) { ws->waimea->eh->eventLoop( &ws->waimea->eh->menu_viewport_move_return_mask, &event); switch (event.type) { case MotionNotify: while (XCheckTypedWindowEvent(ws->display, event.xmotion.window, MotionNotify, &event)); nx += event.xmotion.x_root - px; ny += event.xmotion.y_root - py; px = event.xmotion.x_root; py = event.xmotion.y_root; menu->drawOutline(nx, ny); break; case LeaveNotify: case EnterNotify: if (ws->west->id == event.xcrossing.window || ws->east->id == event.xcrossing.window || ws->north->id == event.xcrossing.window || ws->south->id == event.xcrossing.window) { menu->clearOutline(); ws->waimea->eh->handleEvent(&event); menu->drawOutline(nx, ny); } else if (event.type == EnterNotify && event.xany.window != id) { int cx, cy; XQueryPointer(ws->display, ws->id, &w, &w, &cx, &cy, &i, &i, &ui); nx += cx - px; ny += cy - py; px = cx; py = cy; menu->drawOutline(nx, ny); } break; case MapRequest: map_ev = new XEvent; *map_ev = event; maprequest_list->push_front(map_ev); break; case ButtonPress: case ButtonRelease: event.xbutton.window = id; case KeyPress: case KeyRelease: if (event.type == KeyPress || event.type == KeyRelease) event.xkey.window = id; menu->clearOutline(); ws->waimea->eh->handleEvent(&event); if (ws->waimea->eh->move_resize != EndMoveResizeType) { menu->drawOutline(nx, ny); break; } menu->move(nx, ny); while (! maprequest_list->empty()) { XPutBackEvent(ws->display, maprequest_list->front()); delete maprequest_list->front(); maprequest_list->pop_front(); } delete maprequest_list; XUngrabKeyboard(ws->display, CurrentTime); XUngrabPointer(ws->display, CurrentTime); return; } } } void MenuItem::moveOpaque(void) { XEvent event, *map_ev; int px, py, i; list *maprequest_list; int nx = menu->x; int ny = menu->y; Window w; unsigned int ui; if (ws->waimea->eh->move_resize != EndMoveResizeType) return; ws->waimea->eh->move_resize = MoveType; XQueryPointer(ws->display, ws->id, &w, &w, &px, &py, &i, &i, &ui); maprequest_list = new list; if (XGrabPointer(ws->display, id, true, ButtonReleaseMask | ButtonPressMask | PointerMotionMask | EnterWindowMask | LeaveWindowMask, GrabModeAsync, GrabModeAsync, menu->ws->id, None, CurrentTime) != GrabSuccess) { ws->waimea->eh->move_resize = EndMoveResizeType; delete maprequest_list; return; } if (XGrabKeyboard(ws->display, id, true, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) { ws->waimea->eh->move_resize = EndMoveResizeType; delete maprequest_list; return; } for (;;) { ws->waimea->eh->eventLoop( &ws->waimea->eh->menu_viewport_move_return_mask, &event); switch (event.type) { case MotionNotify: while (XCheckTypedWindowEvent(ws->display, event.xmotion.window, MotionNotify, &event)); nx += event.xmotion.x_root - px; ny += event.xmotion.y_root - py; px = event.xmotion.x_root; py = event.xmotion.y_root; menu->move(nx, ny); break; case LeaveNotify: case EnterNotify: if (ws->west->id == event.xcrossing.window || ws->east->id == event.xcrossing.window || ws->north->id == event.xcrossing.window || ws->south->id == event.xcrossing.window) { ws->waimea->eh->handleEvent(&event); } else if (event.type == EnterNotify && event.xany.window != id) { int cx, cy; XQueryPointer(ws->display, ws->id, &w, &w, &cx, &cy, &i, &i, &ui); nx += cx - px; ny += cy - py; px = cx; py = cy; menu->move(nx, ny); } break; case MapRequest: map_ev = new XEvent; *map_ev = event; maprequest_list->push_front(map_ev); break; case ButtonPress: case ButtonRelease: event.xbutton.window = id; case KeyPress: case KeyRelease: if (event.type == KeyPress || event.type == KeyRelease) event.xkey.window = id; ws->waimea->eh->handleEvent(&event); if (ws->waimea->eh->move_resize != EndMoveResizeType) break; while (! maprequest_list->empty()) { XPutBackEvent(ws->display, maprequest_list->front()); delete maprequest_list->front(); maprequest_list->pop_front(); } delete maprequest_list; XUngrabKeyboard(ws->display, CurrentTime); XUngrabPointer(ws->display, CurrentTime); return; } } } void MenuItem::currentPositionAndSize(int *ret_x, int *ret_y, unsigned int *ret_w, unsigned int *ret_h) { *ret_x = x; *ret_y = y; *ret_w = width; *ret_h = height; } void MenuItem::evalWhatToRender(bool, bool size_change, bool *render_texture, bool *render_alpha, bool *update_shape) { if ((! size_change) && (sb->x > (int) ws->width || sb->y > (int) ws->height || sb->x + (int) sb->width < 0 || sb->y + (int) sb->height < 0)) { *render_texture = false; *render_alpha = false; *update_shape = false; return; } Pixmap newbg = (menu->sb->surface)? menu->sb->surface->pixmap: None; if (sb->bg != newbg) { *render_alpha = true; sb->bg = newbg; } } WaSurface *MenuItem::getBgInfo(DWindowObject **return_dwo, int *return_x, int *return_y) { *return_x = x; *return_y = y; *return_dwo = menu; return menu->sb->surface; }