/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- AZMenuFrame.m for the Azalea window manager Copyright (c) 2006 Yen-Ju Chen menuframe.c for the Openbox window manager Copyright (c) 2004 Mikael Magnusson Copyright (c) 2003 Ben Jansens This program 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 of the License, or (at your option) any later version. This program 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. See the COPYING file for a copy of the GNU General Public License. */ #import "AZMenuFrame.h" #import "AZClient.h" #import "AZScreen.h" #import "AZMenu.h" #import "config.h" #import "openbox.h" #import "render/theme.h" #import "action.h" static NSMutableArray *menu_frame_visible = nil; #define PADDING 2 #define SEPARATOR_HEIGHT 3 #define MAX_MENU_WIDTH 400 #define FRAME_EVENTMASK (ButtonPressMask |ButtonMotionMask | EnterWindowMask |\ LeaveWindowMask) #define TITLE_EVENTMASK (ButtonPressMask | ButtonMotionMask) #define ENTRY_EVENTMASK (EnterWindowMask | LeaveWindowMask | \ ButtonPressMask | ButtonReleaseMask) static Window createWindow(Window parent, unsigned long mask, XSetWindowAttributes *attrib) { return XCreateWindow(ob_display, parent, 0, 0, 1, 1, 0, [ob_rr_inst depth], InputOutput, [ob_rr_inst visual], mask, attrib); } @implementation AZMenuEntryFrame - (id) initWithMenuEntry: (AZMenuEntry *) _entry menuFrame: (AZMenuFrame *) _frame { self = [super init]; XSetWindowAttributes attr; entry = _entry; frame = _frame; attr.event_mask = ENTRY_EVENTMASK; window = createWindow([frame items], CWEventMask, &attr); text = createWindow(window, 0, NULL); if ([entry type] != OB_MENU_ENTRY_TYPE_SEPARATOR) { icon = createWindow(window, 0, NULL); bullet = createWindow(window, 0, NULL); } XMapWindow(ob_display, window); XMapWindow(ob_display, text); a_normal = [ob_rr_theme->a_menu_normal copy]; a_disabled = [ob_rr_theme->a_menu_disabled copy]; a_selected = [ob_rr_theme->a_menu_selected copy]; if ([entry type] == OB_MENU_ENTRY_TYPE_SEPARATOR) { a_separator = [ob_rr_theme->a_clear_tex copy]; [a_separator texture][0].type = RR_TEXTURE_LINE_ART; } else { a_icon = [ob_rr_theme->a_clear_tex copy]; [a_icon texture][0].type = RR_TEXTURE_RGBA; a_mask = [ob_rr_theme->a_clear_tex copy]; [a_mask texture][0].type = RR_TEXTURE_MASK; a_bullet_normal = [ob_rr_theme->a_menu_bullet_normal copy]; a_bullet_selected = [ob_rr_theme->a_menu_bullet_selected copy]; } a_text_normal = [ob_rr_theme->a_menu_text_normal copy]; a_text_disabled = [ob_rr_theme->a_menu_text_disabled copy]; a_text_selected = [ob_rr_theme->a_menu_text_selected copy]; return self; } - (void) dealloc { XDestroyWindow(ob_display, text); XDestroyWindow(ob_display, window); if ([entry type] != OB_MENU_ENTRY_TYPE_SEPARATOR) { XDestroyWindow(ob_display, icon); XDestroyWindow(ob_display, bullet); } DESTROY(a_normal); DESTROY(a_disabled); DESTROY(a_selected); DESTROY(a_separator); DESTROY(a_icon); DESTROY(a_mask); DESTROY(a_text_normal); DESTROY(a_text_disabled); DESTROY(a_text_selected); DESTROY(a_bullet_normal); DESTROY(a_bullet_selected); [super dealloc]; } - (void) render { AZAppearance *item_a, *text_a; int th; /* temp */ AZMenu *sub; item_a = (([entry type] == OB_MENU_ENTRY_TYPE_NORMAL && ![(AZNormalMenuEntry *)entry enabled]) ? a_disabled : (self == [frame selected] ? a_selected : a_normal)); switch ([entry type]) { case OB_MENU_ENTRY_TYPE_NORMAL: case OB_MENU_ENTRY_TYPE_SUBMENU: th = [frame item_h]; break; case OB_MENU_ENTRY_TYPE_SEPARATOR: th = SEPARATOR_HEIGHT + 2*PADDING; break; default: NSLog(@"Internal Error: unknown type"); } RECT_SET_SIZE(area, [frame inner_w], th); XResizeWindow(ob_display, window, area.width, area.height); [item_a surfacePointer]->parent = [frame a_items]; [item_a surfacePointer]->parentx = area.x; [item_a surfacePointer]->parenty = area.y; [item_a paint: window width: area.width height: area.height]; RECT_SET_SIZE(area, [frame inner_w], th); text_a = (([entry type] == OB_MENU_ENTRY_TYPE_NORMAL && ![(AZNormalMenuEntry *)entry enabled]) ? a_text_disabled : (self == [frame selected] ? a_text_selected : a_text_normal)); switch ([entry type]) { case OB_MENU_ENTRY_TYPE_NORMAL: [text_a texture][0].data.text.string = [(AZNormalMenuEntry *)entry label]; break; case OB_MENU_ENTRY_TYPE_SUBMENU: sub = [(AZSubmenuMenuEntry *)entry submenu]; [text_a texture][0].data.text.string = sub ? [sub title] : @""; break; case OB_MENU_ENTRY_TYPE_SEPARATOR: break; } switch ([entry type]) { case OB_MENU_ENTRY_TYPE_NORMAL: XMoveResizeWindow(ob_display, text, [frame text_x], PADDING, [frame text_w], [frame item_h] - 2*PADDING); [text_a surfacePointer]->parent = item_a; [text_a surfacePointer]->parentx = [frame text_x]; [text_a surfacePointer]->parenty = PADDING; [text_a paint: text width: [frame text_w] height: [frame item_h] - 2*PADDING]; break; case OB_MENU_ENTRY_TYPE_SUBMENU: XMoveResizeWindow(ob_display, text, [frame text_x], PADDING, [frame text_w] - [frame item_h], [frame item_h] - 2*PADDING); [text_a surfacePointer]->parent = item_a; [text_a surfacePointer]->parentx = [frame text_x]; [text_a surfacePointer]->parenty = PADDING; [text_a paint: text width: [frame text_w] - [frame item_h] height: [frame item_h] - 2*PADDING]; break; case OB_MENU_ENTRY_TYPE_SEPARATOR: XMoveResizeWindow(ob_display, text, PADDING, PADDING, area.width - 2*PADDING, SEPARATOR_HEIGHT); [a_separator surfacePointer]->parent = item_a; [a_separator surfacePointer]->parentx = PADDING; [a_separator surfacePointer]->parenty = PADDING; [a_separator texture][0].data.lineart.color = [text_a texture][0].data.text.color; [a_separator texture][0].data.lineart.x1 = 2*PADDING; [a_separator texture][0].data.lineart.y1 = SEPARATOR_HEIGHT / 2; [a_separator texture][0].data.lineart.x2 = area.width - 4*PADDING; [a_separator texture][0].data.lineart.y2 = SEPARATOR_HEIGHT / 2; [a_separator paint: text width: area.width - 2*PADDING height: SEPARATOR_HEIGHT]; break; } if ([entry type] != OB_MENU_ENTRY_TYPE_SEPARATOR && [(AZIconMenuEntry *)entry icon_data]) { XMoveResizeWindow(ob_display, icon, PADDING, [frame item_margin].top, [frame item_h] - [frame item_margin].top - [frame item_margin].bottom, [frame item_h] - [frame item_margin].top - [frame item_margin].bottom); [a_icon texture][0].data.rgba.width = [(AZIconMenuEntry *)entry icon_width]; [a_icon texture][0].data.rgba.height = [(AZIconMenuEntry *)entry icon_height]; [a_icon texture][0].data.rgba.data = [(AZIconMenuEntry *)entry icon_data]; [a_icon surfacePointer]->parent = item_a; [a_icon surfacePointer]->parentx = PADDING; [a_icon surfacePointer]->parenty = [frame item_margin].top; [a_icon paint: icon width: [frame item_h] - [frame item_margin].top - [frame item_margin].bottom height: [frame item_h] - [frame item_margin].top - [frame item_margin].bottom]; XMapWindow(ob_display, icon); } else if ([entry type] != OB_MENU_ENTRY_TYPE_SEPARATOR && [(AZIconMenuEntry *)entry mask]) { RrColor *c; XMoveResizeWindow(ob_display, icon, PADDING, [frame item_margin].top, [frame item_h] - [frame item_margin].top - [frame item_margin].bottom, [frame item_h] - [frame item_margin].top - [frame item_margin].bottom); [a_mask texture][0].data.mask.mask = [(AZIconMenuEntry *)entry mask]; c = (([entry type] == OB_MENU_ENTRY_TYPE_NORMAL && ![(AZNormalMenuEntry *)entry enabled]) ? [(AZNormalMenuEntry *)entry mask_disabled_color] : (self == [frame selected] ? [(AZNormalMenuEntry *)entry mask_selected_color] : [(AZNormalMenuEntry *)entry mask_normal_color])); [a_mask texture][0].data.mask.color = c; [a_mask surfacePointer]->parent = item_a; [a_mask surfacePointer]->parentx = PADDING; [a_mask surfacePointer]->parenty = [frame item_margin].top; [a_mask paint: icon width: [frame item_h] - [frame item_margin].top - [frame item_margin].bottom height: [frame item_h] - [frame item_margin].top - [frame item_margin].bottom]; XMapWindow(ob_display, icon); } else XUnmapWindow(ob_display, icon); if ([entry type] == OB_MENU_ENTRY_TYPE_SUBMENU) { AZAppearance *bullet_a; XMoveResizeWindow(ob_display, bullet, [frame text_x] + [frame text_w] - [frame item_h] + PADDING, PADDING, [frame item_h] - 2*PADDING, [frame item_h] - 2*PADDING); bullet_a = (self == [frame selected] ? a_bullet_selected : a_bullet_normal); [bullet_a surfacePointer]->parent = item_a; [bullet_a surfacePointer]->parentx = [frame text_x] + [frame text_w] - [frame item_h] + PADDING; [bullet_a surfacePointer]->parenty = PADDING; [bullet_a paint: bullet width: [frame item_h] - 2*PADDING height: [frame item_h] - 2*PADDING]; XMapWindow(ob_display, bullet); } else XUnmapWindow(ob_display, bullet); XFlush(ob_display); } - (void) showSubmenu { AZMenuFrame *f; if (!(([entry type] == OB_MENU_ENTRY_TYPE_SUBMENU) && ([(AZSubmenuMenuEntry *)entry submenu] != NULL))) return; f = [[AZMenuFrame alloc] initWithMenu: [(AZSubmenuMenuEntry *)entry submenu] client: [frame client]]; [f moveToX: [frame area].x + [frame area].width - ob_rr_theme->menu_overlap - ob_rr_theme->bwidth y: [frame area].y + [frame title_h] + area.y + ob_rr_theme->menu_overlap]; [f showWithParent: frame]; } - (void) execute: (unsigned int) state { if ([entry type] == OB_MENU_ENTRY_TYPE_NORMAL && [(AZNormalMenuEntry *)entry enabled]) { /* grab all this shizzle, cuz when the menu gets hidden, 'self' gets freed */ NSArray *acts = [(AZNormalMenuEntry *)entry actions]; AZClient *client = [frame client]; /* release grabs before executing the shit */ if (!(state & ControlMask)) AZMenuFrameHideAll(); if ([[frame menu] execute: entry state: state] == NO) { action_run(acts, client, state); } } } - (Rect) area { return area; } - (void) set_area: (Rect) a { area = a; } - (AZAppearance *) a_text_normal { return a_text_normal; } - (AZAppearance *) a_text_disabled { return a_text_disabled; } - (AZAppearance *) a_text_selected { return a_text_selected; } - (AZAppearance *) a_normal { return a_normal; } - (AZAppearance *) a_selected { return a_selected; } - (AZAppearance *) a_disabled { return a_disabled; } - (Window) window { return window; } - (AZMenuEntry *) entry { return entry; } - (void) set_entry: (AZMenuEntry *) e { entry = e; } @end AZMenuEntryFrame* AZMenuEntryFrameUnder(int x, int y) { AZMenuFrame *frame; AZMenuEntryFrame *ret = nil; if ((frame = AZMenuFrameUnder(x, y))) { x -= ob_rr_theme->bwidth + [frame area].x; y -= [frame title_h] + ob_rr_theme->bwidth + [frame area].y; int i, count = [[frame entries] count]; for (i = 0; i < count; i++) { AZMenuEntryFrame *e = [[frame entries] objectAtIndex: i]; if (RECT_CONTAINS([e area], x, y)) { ret = e; break; } } } return ret; } @implementation AZMenuFrame + (NSMutableArray *) visibleFrames { if (menu_frame_visible == nil) { menu_frame_visible = [[NSMutableArray alloc] init]; } return menu_frame_visible; } - (id) initWithMenu: (AZMenu *) _menu client: (AZClient *) _client { self = [super init]; XSetWindowAttributes attr; menu = _menu; selected = nil; show_title = YES; client = _client; attr.event_mask = FRAME_EVENTMASK; attr.save_under = True; window = createWindow(RootWindow(ob_display, ob_screen), CWEventMask|CWSaveUnder, &attr); attr.event_mask = TITLE_EVENTMASK; title = createWindow(window, CWEventMask, &attr); items = createWindow(window, 0, NULL); XMapWindow(ob_display, items); a_title = [ob_rr_theme->a_menu_title copy]; a_items = [ob_rr_theme->a_menu copy]; [[AZStacking stacking] addWindow: self]; entries = [[NSMutableArray alloc] init]; return self; } - (void) dealloc { DESTROY(entries); [[AZStacking stacking] removeWindow: self]; XDestroyWindow(ob_display, items); XDestroyWindow(ob_display, title); XDestroyWindow(ob_display, window); DESTROY(a_items); DESTROY(a_title); [super dealloc]; } - (void) moveToX: (int) x y: (int) y { RECT_SET_POINT(area, x, y); XMoveWindow(ob_display, window, area.x, area.y); } - (void) moveOnScreen { Rect *a = NULL; int dx = 0, dy = 0; int pos, half; a = [[AZScreen defaultScreen] physicalAreaOfMonitor: monitor]; half = [entries count] / 2; pos = [entries indexOfObject: selected]; /* if in the bottom half then check this shit first, will keep the bottom edge of the menu visible */ if (pos > half) { dx = MAX(dx, a->x - area.x); dy = MAX(dy, a->y - area.y); } dx = MIN(dx, (a->x + a->width) - (area.x + area.width)); dy = MIN(dy, (a->y + a->height) - (area.y + area.height)); /* if in the top half then check this shit last, will keep the top edge of the menu visible */ if (pos <= half) { dx = MAX(dx, a->x - area.x); dy = MAX(dy, a->y - area.y); } if (dx || dy) { AZMenuFrame *f; /* move the current menu frame to fit, but dont touch parents yet */ [self moveToX: area.x + dx y: area.y + dy]; if (!config_menu_xorstyle) dy = 0; /* if we want to be like xor, move parents in y- * * and x-direction, otherwise just in x-dir */ for (f = parent; f; f = [f parent]) [f moveToX: [f area].x + dx y: [f area].y + dy]; for (f = child; f; f = [f child]) [f moveToX: [f area].x + dx y: [f area].y + dy]; if (config_menu_warppointer) XWarpPointer(ob_display, None, None, 0, 0, 0, 0, dx, dy); } } - (void) render { int w = 0, h = 0; int allitems_h = 0; int tw, th; /* temps */ BOOL has_icon = NO; AZMenu *sub; XSetWindowBorderWidth(ob_display, window, ob_rr_theme->bwidth); XSetWindowBorder(ob_display, window, RrColorPixel(ob_rr_theme->b_color)); if (!parent && show_title) { XMoveWindow(ob_display, title, -ob_rr_theme->bwidth, h - ob_rr_theme->bwidth); [a_title texture][0].data.text.string = [menu title]; [a_title minimalSizeWithWidth: &tw height: &th]; tw = MIN(tw, MAX_MENU_WIDTH) + ob_rr_theme->padding * 2; w = MAX(w, tw); th = ob_rr_theme->menu_title_height; h += (title_h = th + ob_rr_theme->bwidth); XSetWindowBorderWidth(ob_display, title, ob_rr_theme->bwidth); XSetWindowBorder(ob_display, title, RrColorPixel(ob_rr_theme->b_color)); } XMoveWindow(ob_display, items, 0, h); STRUT_SET(item_margin, 0, 0, 0, 0); if ([entries count]) { AZMenuEntryFrame *e = [entries objectAtIndex: 0]; int l, t, r, b; [[e a_text_normal] texture][0].data.text.string = @""; [[e a_text_normal] minimalSizeWithWidth: &tw height: &th]; tw += 2*PADDING; th += 2*PADDING; item_h = th; [[e a_normal] marginsWithLeft: &l top: &t right: &r bottom: &b]; STRUT_SET(item_margin, MAX(item_margin.left, l), MAX(item_margin.top, t), MAX(item_margin.right, r), MAX(item_margin.bottom, b)); [[e a_selected] marginsWithLeft: &l top: &t right: &r bottom: &b]; STRUT_SET(item_margin, MAX(item_margin.left, l), MAX(item_margin.top, t), MAX(item_margin.right, r), MAX(item_margin.bottom, b)); [[e a_disabled] marginsWithLeft: &l top: &t right: &r bottom: &b]; STRUT_SET(item_margin, MAX(item_margin.left, l), MAX(item_margin.top, t), MAX(item_margin.right, r), MAX(item_margin.bottom, b)); } else item_h = 0; int i, count = [entries count]; for (i = 0; i < count; i++) { AZAppearance *text_a; AZMenuEntryFrame *e = [entries objectAtIndex: i]; Rect _area = [e area]; RECT_SET_POINT(_area, 0, allitems_h); [e set_area: _area]; XMoveWindow(ob_display, [e window], 0, [e area].y); text_a = (([[e entry] type] == OB_MENU_ENTRY_TYPE_NORMAL && ![(AZNormalMenuEntry *)[e entry] enabled]) ? [e a_text_disabled] : (e == self->selected ? [e a_text_selected] : [e a_text_normal])); switch ([[e entry] type]) { case OB_MENU_ENTRY_TYPE_NORMAL: [text_a texture][0].data.text.string = [(AZNormalMenuEntry *)[e entry] label]; [text_a minimalSizeWithWidth: &tw height: &th]; tw = MIN(tw, MAX_MENU_WIDTH); if ([(AZIconMenuEntry *)[e entry] icon_data] || [(AZIconMenuEntry *)[e entry] mask]) has_icon = YES; break; case OB_MENU_ENTRY_TYPE_SUBMENU: sub = [(AZSubmenuMenuEntry *)[e entry] submenu]; [text_a texture][0].data.text.string = sub ? [sub title] : @""; [text_a minimalSizeWithWidth: &tw height: &th]; tw = MIN(tw, MAX_MENU_WIDTH); if ([(AZIconMenuEntry *)[e entry] icon_data] || [(AZIconMenuEntry *)[e entry] mask]) has_icon = YES; tw += self->item_h - PADDING; break; case OB_MENU_ENTRY_TYPE_SEPARATOR: tw = 0; th = SEPARATOR_HEIGHT; break; } tw += 2*PADDING; th += 2*PADDING; w = MAX(w, tw); h += th; allitems_h += th; } text_x = PADDING; text_w = w; if ([entries count]) { if (has_icon) { w += item_h + PADDING; text_x += item_h + PADDING; } } if (!w) w = 10; if (!allitems_h) { allitems_h = 3; h += 3; } XResizeWindow(ob_display, window, w, h); XResizeWindow(ob_display, items, w, allitems_h); inner_w = w; if (!parent && show_title) { XResizeWindow(ob_display, title, w, title_h - ob_rr_theme->bwidth); [a_title paint: title width: w height: title_h - ob_rr_theme->bwidth]; XMapWindow(ob_display, title); } else XUnmapWindow(ob_display, title); [a_items paint: items width: w height: allitems_h]; count = [entries count]; for (i = 0; i < count; i++) { [(AZMenuEntryFrame*)[entries objectAtIndex: i] render]; } w += ob_rr_theme->bwidth * 2; h += ob_rr_theme->bwidth * 2; RECT_SET_SIZE(area, w, h); XFlush(ob_display); } - (void) update { int fit, fcount; int mit, mcount; [menu pipeExecute]; [menu findSubmenus]; selected = nil; fcount = [entries count]; mcount = [[menu entries] count]; for (mit = 0, fit = 0; ((mit < mcount) && (fit < fcount)); mit++, fit++) { AZMenuEntryFrame *f = [entries objectAtIndex: fit]; [f set_entry: [[menu entries] objectAtIndex: mit]]; } for (; mit < mcount; mit++) { AZMenuEntryFrame *e = [[AZMenuEntryFrame alloc] initWithMenuEntry: [[menu entries] objectAtIndex: mit] menuFrame: self]; [entries addObject: e]; DESTROY(e); } for(; fit < fcount; fit++) { [entries removeObjectAtIndex: fit]; } [self render]; } - (BOOL) showWithParent: (AZMenuFrame *) p { NSMutableArray *visibles = [AZMenuFrame visibleFrames]; int i, count = [visibles count]; if ([visibles containsObject: self]) return YES; if (count == 0) { /* no menus shown yet */ if (!grab_pointer(YES, OB_CURSOR_NONE)) return NO; if (!grab_keyboard(YES)) { grab_pointer(NO, OB_CURSOR_NONE); return NO; } } if (p) { monitor = [p monitor]; if ([p child]) [[p child] hide]; [p set_child: self]; } parent = p; /* determine if the underlying menu is already visible */ int found = NSNotFound; for (i = 0; i < count; i++) { AZMenuFrame *f = [visibles objectAtIndex: i]; if ([f menu] == menu) { found = i; break; } } if (found == NSNotFound) { [menu update: self]; } [self update]; [visibles insertObject: self atIndex: 0]; [self moveOnScreen]; XMapWindow(ob_display, window); return YES; } - (void) hide { NSMutableArray *visibles = [AZMenuFrame visibleFrames]; if ([visibles containsObject: self] == NO) return; if (child) [child hide]; if (parent) [parent set_child: nil]; parent = nil; [visibles removeObject: self]; if ([visibles count] == 0) { /* last menu shown */ grab_pointer(NO, OB_CURSOR_NONE); grab_keyboard(NO); } XUnmapWindow(ob_display, window); // FIXME RELEASE(self); } - (void) selectMenuEntryFrame: (AZMenuEntryFrame *) entry { AZMenuEntryFrame *old = selected; AZMenuFrame *oldchild = child; if (entry && [[entry entry] type] == OB_MENU_ENTRY_TYPE_SEPARATOR) entry = old; if (old == entry) return; selected = entry; if (old) [old render]; if (oldchild) [oldchild hide]; if (selected) { [selected render]; if ([[selected entry] type] == OB_MENU_ENTRY_TYPE_SUBMENU) [selected showSubmenu]; } } - (void) selectPrevious { int start, it; if ([entries count]) { start = it = [entries indexOfObject: selected]; if (start == NSNotFound) start = 0; while (YES) { AZMenuEntryFrame *e; it = (it == NSNotFound) ? [entries count]-1 : it-1; if (it < 0) it = [entries count]-1; if (it == start) break; { e = (AZMenuEntryFrame*)[entries objectAtIndex: it]; if ([[e entry] type] == OB_MENU_ENTRY_TYPE_SUBMENU) break; if ([[e entry] type] == OB_MENU_ENTRY_TYPE_NORMAL && [(AZNormalMenuEntry *)[e entry] enabled]) break; } } } [self selectMenuEntryFrame: ((it == NSNotFound) ? nil : [entries objectAtIndex: it])]; } - (void) selectNext { int start, it; if (entries) { start = it = [entries indexOfObject: selected]; if (start == NSNotFound) start = [entries count]-1; while (YES) { AZMenuEntryFrame *e; it = (it == NSNotFound) ? 0 : it+1; if (it >= [entries count]) it = 0; if (it == start) break; { e = (AZMenuEntryFrame *)[entries objectAtIndex: it]; if ([[e entry] type] == OB_MENU_ENTRY_TYPE_SUBMENU) break; if ([[e entry] type] == OB_MENU_ENTRY_TYPE_NORMAL && [(AZNormalMenuEntry *)[e entry] enabled]) break; } } } [self selectMenuEntryFrame: ((it == NSNotFound) ? nil : [entries objectAtIndex: it])]; } /* Accessories */ - (Rect) area { return area; } - (AZMenuFrame *) parent { return parent; } - (AZMenuFrame *) child { return child; } - (AZMenuEntryFrame *) selected { return selected; } - (int) monitor { return monitor; } - (AZMenu *) menu { return menu; } - (AZClient *) client { return client; } - (void) set_child: (AZMenuFrame *) c { child = c; } - (void) set_parent: (AZMenuFrame *) p { parent = p; } - (void) set_monitor: (int) m { monitor = m; } - (Window) items { return items; } - (Window) window { return window; } - (int) item_h { return item_h; } - (int) title_h { return title_h; } - (int) inner_w { return inner_w; } - (AZAppearance *) a_items { return a_items; } - (int) text_x { return text_x; } - (int) text_w { return text_w; } - (Strut) item_margin { return item_margin; } - (NSArray *) entries { return entries; } - (void) set_show_title: (BOOL) b { show_title = b; } - (Window_InternalType) windowType { return Window_Menu; } - (int) windowLayer { return OB_STACKING_LAYER_INTERNAL; } - (Window) windowTop { return window; } @end void AZMenuFrameHideAll() { AZMenuFrame *last = [[AZMenuFrame visibleFrames] lastObject]; if (last) { [last hide]; } } void AZMenuFrameHideAllClient (AZClient *_client) { AZMenuFrame *f = [[AZMenuFrame visibleFrames] lastObject]; if (f) { if ([f client] == _client) [f hide]; } } AZMenuFrame *AZMenuFrameUnder(int x, int y) { NSArray *visibles = [AZMenuFrame visibleFrames]; int i, count = [visibles count]; for (i = 0; i < count; i++) { AZMenuFrame *f = [visibles objectAtIndex: i]; if (RECT_CONTAINS([f area], x, y)) { return f; } } return nil; }