/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- action.c for the Azalea window manager Copyright (c) 2006 Yen-Ju Chen action.c for the Openbox window manager 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 "AZDock.h" #import "AZStacking.h" #import "AZClient.h" #import "AZMoveResizeHandler.h" #import "prop.h" #import "action.h" #import "openbox.h" #import "grab.h" #import "AZKeyboardHandler.h" #import "config.h" #import "AZMainLoop.h" #import "AZScreen.h" #import "AZEventHandler.h" #import "AZFocusManager.h" #import "AZMenuFrame.h" #import "AZMenuManager.h" #import inline void client_action_start(union ActionData *data) { if (config_focus_follow) if (data->any.context != OB_FRAME_CONTEXT_CLIENT && !data->any.button) grab_pointer(YES, OB_CURSOR_NONE); } inline void client_action_end(union ActionData *data) { if (config_focus_follow) if (data->any.context != OB_FRAME_CONTEXT_CLIENT) { if (!data->any.button) { grab_pointer(NO, OB_CURSOR_NONE); } else { AZClient *c; /* usually this is sorta redundant, but with a press action the enter event will come as a GrabNotify which is ignored, so this will handle that case */ if ((c = AZUnderPointer())) { [[AZEventHandler defaultHandler] enterClient: c]; } } } } typedef struct { NSString *name; void (*func)(union ActionData *); void (*setup)(AZAction **, ObUserAction uact); } ActionString; void setup_action_directional_focus_north(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_NORTH; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_east(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_EAST; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_south(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_SOUTH; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_west(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_WEST; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_northeast(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_NORTHEAST; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_southeast(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_SOUTHEAST; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_southwest(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_SOUTHWEST; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_directional_focus_northwest(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->interdiraction.inter.any.interactive = YES; [(*a) data_pointer]->interdiraction.direction = OB_DIRECTION_NORTHWEST; [(*a) data_pointer]->interdiraction.dialog = YES; } void setup_action_send_to_desktop(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendto.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendto.follow = YES; } void setup_action_send_to_desktop_prev(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendtodir.inter.any.interactive = YES; [(*a) data_pointer]->sendtodir.dir = OB_DIRECTION_WEST; [(*a) data_pointer]->sendtodir.linear = YES; [(*a) data_pointer]->sendtodir.wrap = YES; [(*a) data_pointer]->sendtodir.follow = YES; } void setup_action_send_to_desktop_next(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendtodir.inter.any.interactive = YES; [(*a) data_pointer]->sendtodir.dir = OB_DIRECTION_EAST; [(*a) data_pointer]->sendtodir.linear = YES; [(*a) data_pointer]->sendtodir.wrap = YES; [(*a) data_pointer]->sendtodir.follow = YES; } void setup_action_send_to_desktop_left(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendtodir.inter.any.interactive = YES; [(*a) data_pointer]->sendtodir.dir = OB_DIRECTION_WEST; [(*a) data_pointer]->sendtodir.linear = NO; [(*a) data_pointer]->sendtodir.wrap = YES; [(*a) data_pointer]->sendtodir.follow = YES; } void setup_action_send_to_desktop_right(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendtodir.inter.any.interactive = YES; [(*a) data_pointer]->sendtodir.dir = OB_DIRECTION_EAST; [(*a) data_pointer]->sendtodir.linear = NO; [(*a) data_pointer]->sendtodir.wrap = YES; [(*a) data_pointer]->sendtodir.follow = YES; } void setup_action_send_to_desktop_up(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendtodir.inter.any.interactive = YES; [(*a) data_pointer]->sendtodir.dir = OB_DIRECTION_NORTH; [(*a) data_pointer]->sendtodir.linear = NO; [(*a) data_pointer]->sendtodir.wrap = YES; [(*a) data_pointer]->sendtodir.follow = YES; } void setup_action_send_to_desktop_down(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->sendtodir.inter.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->sendtodir.inter.any.interactive = YES; [(*a) data_pointer]->sendtodir.dir = OB_DIRECTION_SOUTH; [(*a) data_pointer]->sendtodir.linear = NO; [(*a) data_pointer]->sendtodir.wrap = YES; [(*a) data_pointer]->sendtodir.follow = YES; } void setup_action_desktop(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktop.inter.any.interactive = NO; } void setup_action_desktop_prev(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktopdir.inter.any.interactive = YES; [(*a) data_pointer]->desktopdir.dir = OB_DIRECTION_WEST; [(*a) data_pointer]->desktopdir.linear = YES; [(*a) data_pointer]->desktopdir.wrap = YES; } void setup_action_desktop_next(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktopdir.inter.any.interactive = YES; [(*a) data_pointer]->desktopdir.dir = OB_DIRECTION_EAST; [(*a) data_pointer]->desktopdir.linear = YES; [(*a) data_pointer]->desktopdir.wrap = YES; } void setup_action_desktop_left(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktopdir.inter.any.interactive = YES; [(*a) data_pointer]->desktopdir.dir = OB_DIRECTION_WEST; [(*a) data_pointer]->desktopdir.linear = NO; [(*a) data_pointer]->desktopdir.wrap = YES; } void setup_action_desktop_right(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktopdir.inter.any.interactive = YES; [(*a) data_pointer]->desktopdir.dir = OB_DIRECTION_EAST; [(*a) data_pointer]->desktopdir.linear = NO; [(*a) data_pointer]->desktopdir.wrap = YES; } void setup_action_desktop_up(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktopdir.inter.any.interactive = YES; [(*a) data_pointer]->desktopdir.dir = OB_DIRECTION_NORTH; [(*a) data_pointer]->desktopdir.linear = NO; [(*a) data_pointer]->desktopdir.wrap = YES; } void setup_action_desktop_down(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->desktopdir.inter.any.interactive = YES; [(*a) data_pointer]->desktopdir.dir = OB_DIRECTION_SOUTH; [(*a) data_pointer]->desktopdir.linear = NO; [(*a) data_pointer]->desktopdir.wrap = YES; } void setup_action_cycle_windows_next(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->cycle.inter.any.interactive = YES; [(*a) data_pointer]->cycle.linear = NO; [(*a) data_pointer]->cycle.forward = YES; [(*a) data_pointer]->cycle.dialog = YES; } void setup_action_cycle_windows_previous(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->cycle.inter.any.interactive = YES; [(*a) data_pointer]->cycle.linear = NO; [(*a) data_pointer]->cycle.forward = NO; [(*a) data_pointer]->cycle.dialog = YES; } void setup_action_movetoedge_north(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_NORTH; } void setup_action_movetoedge_south(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_SOUTH; } void setup_action_movetoedge_east(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_EAST; } void setup_action_movetoedge_west(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_WEST; } void setup_action_growtoedge_north(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_NORTH; } void setup_action_growtoedge_south(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_SOUTH; } void setup_action_growtoedge_east(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_EAST; } void setup_action_growtoedge_west(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->diraction.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->diraction.direction = OB_DIRECTION_WEST; } void setup_action_top_layer(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->layer.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->layer.layer = 1; } void setup_action_normal_layer(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->layer.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->layer.layer = 0; } void setup_action_bottom_layer(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->layer.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->layer.layer = -1; } void setup_action_move(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->moveresize.move = YES; [(*a) data_pointer]->moveresize.keyboard = (uact == OB_USER_ACTION_NONE || uact == OB_USER_ACTION_KEYBOARD_KEY || uact == OB_USER_ACTION_MENU_SELECTION); } void setup_action_resize(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->moveresize.any.client_action = OB_CLIENT_ACTION_ALWAYS; [(*a) data_pointer]->moveresize.move = NO; [(*a) data_pointer]->moveresize.keyboard = (uact == OB_USER_ACTION_NONE || uact == OB_USER_ACTION_KEYBOARD_KEY || uact == OB_USER_ACTION_MENU_SELECTION); } void setup_action_showmenu(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->showmenu.any.client_action = OB_CLIENT_ACTION_OPTIONAL; /* you cannot call ShowMenu from inside a menu, cuz the menu code makes assumptions that there is only one menu (and submenus) open at a time! */ if (uact == OB_USER_ACTION_MENU_SELECTION) { *a = NULL; } } void setup_client_action(AZAction **a, ObUserAction uact) { [(*a) data_pointer]->any.client_action = OB_CLIENT_ACTION_ALWAYS; } ActionString actionstrings[] = { { @"execute", action_execute, NULL }, { @"directionalfocusnorth", action_directional_focus, setup_action_directional_focus_north }, { @"directionalfocuseast", action_directional_focus, setup_action_directional_focus_east }, { @"directionalfocussouth", action_directional_focus, setup_action_directional_focus_south }, { @"directionalfocuswest", action_directional_focus, setup_action_directional_focus_west }, { @"directionalfocusnortheast", action_directional_focus, setup_action_directional_focus_northeast }, { @"directionalfocussoutheast", action_directional_focus, setup_action_directional_focus_southeast }, { @"directionalfocussouthwest", action_directional_focus, setup_action_directional_focus_southwest }, { @"directionalfocusnorthwest", action_directional_focus, setup_action_directional_focus_northwest }, { @"activate", action_activate, setup_client_action }, { @"focus", action_focus, setup_client_action }, { @"unfocus", action_unfocus, setup_client_action }, { @"iconify", action_iconify, setup_client_action }, { @"focustobottom", action_focus_order_to_bottom, setup_client_action }, { @"raiselower", action_raiselower, setup_client_action }, { @"raise", action_raise, setup_client_action }, { @"lower", action_lower, setup_client_action }, { @"close", action_close, setup_client_action }, { @"kill", action_kill, setup_client_action }, { @"shadelower", action_shadelower, setup_client_action }, { @"unshaderaise", action_unshaderaise, setup_client_action }, { @"shade", action_shade, setup_client_action }, { @"unshade", action_unshade, setup_client_action }, { @"toggleshade", action_toggle_shade, setup_client_action }, { @"toggleomnipresent", action_toggle_omnipresent, setup_client_action }, { @"moverelativehorz", action_move_relative_horz, setup_client_action }, { @"moverelativevert", action_move_relative_vert, setup_client_action }, { @"movetocenter", action_move_to_center, setup_client_action }, { @"resizerelativehorz", action_resize_relative_horz, setup_client_action }, { @"resizerelativevert", action_resize_relative_vert, setup_client_action }, { @"moverelative", action_move_relative, setup_client_action }, { @"resizerelative", action_resize_relative, setup_client_action }, { @"maximizefull", action_maximize_full, setup_client_action }, { @"unmaximizefull", action_unmaximize_full, setup_client_action }, { @"togglemaximizefull", action_toggle_maximize_full, setup_client_action }, { @"maximizehorz", action_maximize_horz, setup_client_action }, { @"unmaximizehorz", action_unmaximize_horz, setup_client_action }, { @"togglemaximizehorz", action_toggle_maximize_horz, setup_client_action }, { @"maximizevert", action_maximize_vert, setup_client_action }, { @"unmaximizevert", action_unmaximize_vert, setup_client_action }, { @"togglemaximizevert", action_toggle_maximize_vert, setup_client_action }, { @"togglefullscreen", action_toggle_fullscreen, setup_client_action }, { @"sendtodesktop", action_send_to_desktop, setup_action_send_to_desktop }, { @"sendtodesktopnext", action_send_to_desktop_dir, setup_action_send_to_desktop_next }, { @"sendtodesktopprevious", action_send_to_desktop_dir, setup_action_send_to_desktop_prev }, { @"sendtodesktopright", action_send_to_desktop_dir, setup_action_send_to_desktop_right }, { @"sendtodesktopleft", action_send_to_desktop_dir, setup_action_send_to_desktop_left }, { @"sendtodesktopup", action_send_to_desktop_dir, setup_action_send_to_desktop_up }, { @"sendtodesktopdown", action_send_to_desktop_dir, setup_action_send_to_desktop_down }, { @"desktop", action_desktop, setup_action_desktop }, { @"desktopnext", action_desktop_dir, setup_action_desktop_next }, { @"desktopprevious", action_desktop_dir, setup_action_desktop_prev }, { @"desktopright", action_desktop_dir, setup_action_desktop_right }, { @"desktopleft", action_desktop_dir, setup_action_desktop_left }, { @"desktopup", action_desktop_dir, setup_action_desktop_up }, { @"desktopdown", action_desktop_dir, setup_action_desktop_down }, { @"toggledecorations", action_toggle_decorations, setup_client_action }, { @"move", action_moveresize, setup_action_move }, { @"resize", action_moveresize, setup_action_resize }, { @"toggledockautohide", action_toggle_dockautohide, NULL }, { @"toggleshowdesktop", action_toggle_show_desktop, NULL }, { @"showdesktop", action_show_desktop, NULL }, { @"unshowdesktop", action_unshow_desktop, NULL }, { @"desktoplast", action_desktop_last, NULL }, { @"reconfigure", action_reconfigure, NULL }, { @"restart", action_restart, NULL }, { @"exit", action_exit, NULL }, { @"showmenu", action_showmenu, setup_action_showmenu }, { @"sendtotoplayer", action_send_to_layer, setup_action_top_layer }, { @"togglealwaysontop", action_toggle_layer, setup_action_top_layer }, { @"sendtonormallayer", action_send_to_layer, setup_action_normal_layer }, { @"sendtobottomlayer", action_send_to_layer, setup_action_bottom_layer }, { @"togglealwaysonbottom", action_toggle_layer, setup_action_bottom_layer }, { @"nextwindow", action_cycle_windows, setup_action_cycle_windows_next }, { @"previouswindow", action_cycle_windows, setup_action_cycle_windows_previous }, { @"movetoedgenorth", action_movetoedge, setup_action_movetoedge_north }, { @"movetoedgesouth", action_movetoedge, setup_action_movetoedge_south }, { @"movetoedgewest", action_movetoedge, setup_action_movetoedge_west }, { @"movetoedgeeast", action_movetoedge, setup_action_movetoedge_east }, { @"growtoedgenorth", action_growtoedge, setup_action_growtoedge_north }, { @"growtoedgesouth", action_growtoedge, setup_action_growtoedge_south }, { @"growtoedgewest", action_growtoedge, setup_action_growtoedge_west }, { @"growtoedgeeast", action_growtoedge, setup_action_growtoedge_east }, { nil, NULL, NULL } }; /* only key bindings can be interactive. thus saith the xor. because of how the mouse is grabbed, mouse events dont even get read during interactive events, so no dice! >:) */ #define INTERACTIVE_LIMIT(a, uact) \ if (uact != OB_USER_ACTION_KEYBOARD_KEY) \ [a data_pointer]->any.interactive = NO; @implementation AZAction + (AZAction *) actionWithName: (NSString *) name userAction: (ObUserAction) uact { AZAction *a = nil; BOOL exist = NO; int i; for (i = 0; actionstrings[i].name; i++) if ([name compare: actionstrings[i].name options: NSCaseInsensitiveSearch] == NSOrderedSame) { exist = YES; a = [[AZAction alloc] initWithFunc: actionstrings[i].func]; if (actionstrings[i].setup) actionstrings[i].setup(&a, uact); if (a) INTERACTIVE_LIMIT(a, uact); break; } if (!exist) NSLog(@"Invalid action '%@' requested. No such action exists.", name); if (!a) NSLog(@"Invalid use of action '%@'. Action will be ignored.", name); return AUTORELEASE(a); } - (AZActionFunc) func { return func; } - (union ActionData) data { return data; } - (union ActionData *) data_pointer { return &data; } - (void) set_func: (AZActionFunc) f { func = f; } - (void) set_data: (union ActionData) d { data = d; } - (id) initWithFunc: (AZActionFunc) f { self = [super init]; func = f; return self; } - (void) dealloc { /* deal with pointers */ if (func == action_execute || func == action_restart) DESTROY(data.execute.path); else if (func == action_showmenu) DESTROY(data.showmenu.name); [super dealloc]; } - (id) copyWithZone: (NSZone *) zone { AZAction *a = [[AZAction allocWithZone: zone] initWithFunc: func]; [a set_data: data]; /* deal with pointers */ if ([a func] == action_execute || [a func] == action_restart) [a data_pointer]->execute.path = [data.execute.path copy]; else if ([a func] == action_showmenu) [a data_pointer]->showmenu.name = [data.showmenu.name copy]; return a; } @end AZAction *action_parse(xmlDocPtr doc, xmlNodePtr node, ObUserAction uact) { NSString *actname; AZAction *act = nil; xmlNodePtr n; if (parse_attr_string("name", node, &actname)) { if ((act = [AZAction actionWithName: actname userAction: uact])) { if ([act func] == action_execute || [act func] == action_restart) { if ((n = parse_find_node("execute", node->xmlChildrenNode))) { ASSIGN([act data_pointer]->execute.path, ([parse_string(doc, n) stringByExpandingTildeInPath])); } } else if ([act func] == action_showmenu) { if ((n = parse_find_node("menu", node->xmlChildrenNode))) ASSIGN([act data_pointer]->showmenu.name, parse_string(doc, n)); } else if ([act func] == action_move_relative_horz || [act func] == action_move_relative_vert || [act func] == action_resize_relative_horz || [act func] == action_resize_relative_vert) { if ((n = parse_find_node("delta", node->xmlChildrenNode))) [act data_pointer]->relative.deltax = parse_int(doc, n); } else if ([act func] == action_move_relative) { if ((n = parse_find_node("x", node->xmlChildrenNode))) [act data_pointer]->relative.deltax = parse_int(doc, n); if ((n = parse_find_node("y", node->xmlChildrenNode))) [act data_pointer]->relative.deltay = parse_int(doc, n); } else if ([act func] == action_resize_relative) { if ((n = parse_find_node("left", node->xmlChildrenNode))) [act data_pointer]->relative.deltaxl = parse_int(doc, n); if ((n = parse_find_node("up", node->xmlChildrenNode))) [act data_pointer]->relative.deltayu = parse_int(doc, n); if ((n = parse_find_node("right", node->xmlChildrenNode))) [act data_pointer]->relative.deltax = parse_int(doc, n); if ((n = parse_find_node("down", node->xmlChildrenNode))) [act data_pointer]->relative.deltay = parse_int(doc, n); } else if ([act func] == action_desktop) { if ((n = parse_find_node("desktop", node->xmlChildrenNode))) [act data_pointer]->desktop.desk = parse_int(doc, n); if ([act data].desktop.desk > 0) [act data_pointer]->desktop.desk--; if ((n = parse_find_node("dialog", node->xmlChildrenNode))) [act data_pointer]->desktop.inter.any.interactive = parse_bool(doc, n); } else if ([act func] == action_send_to_desktop) { if ((n = parse_find_node("desktop", node->xmlChildrenNode))) [act data_pointer]->sendto.desk = parse_int(doc, n); if ([act data].sendto.desk > 0) [act data_pointer]->sendto.desk--; if ((n = parse_find_node("follow", node->xmlChildrenNode))) [act data_pointer]->sendto.follow = parse_bool(doc, n); } else if ([act func] == action_desktop_dir) { if ((n = parse_find_node("wrap", node->xmlChildrenNode))) [act data_pointer]->desktopdir.wrap = parse_bool(doc, n); if ((n = parse_find_node("dialog", node->xmlChildrenNode))) [act data_pointer]->desktopdir.inter.any.interactive = parse_bool(doc, n); } else if ([act func] == action_send_to_desktop_dir) { if ((n = parse_find_node("wrap", node->xmlChildrenNode))) [act data_pointer]->sendtodir.wrap = parse_bool(doc, n); if ((n = parse_find_node("follow", node->xmlChildrenNode))) [act data_pointer]->sendtodir.follow = parse_bool(doc, n); if ((n = parse_find_node("dialog", node->xmlChildrenNode))) [act data_pointer]->sendtodir.inter.any.interactive = parse_bool(doc, n); } else if ([act func] == action_activate) { if ((n = parse_find_node("here", node->xmlChildrenNode))) [act data_pointer]->activate.here = parse_bool(doc, n); } else if ([act func] == action_cycle_windows) { if ((n = parse_find_node("linear", node->xmlChildrenNode))) [act data_pointer]->cycle.linear = parse_bool(doc, n); if ((n = parse_find_node("dialog", node->xmlChildrenNode))) [act data_pointer]->cycle.dialog = parse_bool(doc, n); } else if ([act func] == action_directional_focus) { if ((n = parse_find_node("dialog", node->xmlChildrenNode))) [act data_pointer]->cycle.dialog = parse_bool(doc, n); } else if ([act func] == action_raise || [act func] == action_lower || [act func] == action_raiselower || [act func] == action_shadelower || [act func] == action_unshaderaise) { if ((n = parse_find_node("group", node->xmlChildrenNode))) [act data_pointer]->stacking.group = parse_bool(doc, n); } INTERACTIVE_LIMIT(act, uact); } } return act; } void action_run_mouse(NSArray *acts, AZClient *c, ObFrameContext n, unsigned int s, unsigned int b, int x, int y) { action_run_list(acts, c, n, s, b, x, y, NO, NO); } void action_run_interactive(NSArray *acts, AZClient *c, unsigned int s, BOOL n, BOOL d) { action_run_list(acts, c, OB_FRAME_CONTEXT_NONE, s, 0, -1, -1, n, d); } void action_run_key(NSArray *acts, AZClient *c, unsigned int state, int x, int y) { action_run_list(acts, c, OB_FRAME_CONTEXT_NONE, state, 0, x, y, NO, NO); } void action_run(NSArray *acts, AZClient *c, unsigned int state) { action_run_list(acts, c, OB_FRAME_CONTEXT_NONE, state, 0, -1, -1, NO, NO); } void action_run_list(NSArray *acts, AZClient *c, ObFrameContext context, unsigned int state, unsigned int button, int x, int y, BOOL cancel, BOOL done) { AZAction *a; BOOL inter = NO; int i, count; if ((acts == nil) || ([acts count] == 0)) return; if (x < 0 && y < 0) { [[AZScreen defaultScreen] pointerPosAtX: &x y: &y]; } if (grab_on_keyboard()) { inter = YES; } else { count = [acts count]; for (i = 0; i < count; i++) { a = [acts objectAtIndex: i]; if ([a data].any.interactive) { inter = YES; break; } } } if (!inter) { /* sometimes when we execute another app as an action, it won't work right unless we XUngrabKeyboard first, even though we grabbed the key/button Asychronously. e.g. "gnome-panel-control --main-menu" */ XUngrabKeyboard(ob_display, [[AZEventHandler defaultHandler] eventLastTime]); } count = [acts count]; for (i = 0; i < count; i++) { a = [acts objectAtIndex: i]; if (!([a data].any.client_action == OB_CLIENT_ACTION_ALWAYS && !c)) { [a data_pointer]->any.c = [a data].any.client_action ? c : NULL; [a data_pointer]->any.context = context; [a data_pointer]->any.x = x; [a data_pointer]->any.y = y; [a data_pointer]->any.button = button; if ([a data].any.interactive) { [a data_pointer]->inter.cancel = cancel; [a data_pointer]->inter.final = done; if (!(cancel || done)) { AZKeyboardHandler *kHandler = [AZKeyboardHandler defaultHandler]; if (![kHandler interactiveGrab: state client: [a data].any.c action: a]) continue; } } /* XXX UGLY HACK race with motion event starting a move and the button release gettnig processed first. answer: don't queue moveresize starts. UGLY HACK XXX */ if ([a data].any.interactive || [a func] == action_moveresize) { /* interactive actions are not queued */ [a func]([a data_pointer]); } else { [[AZMainLoop mainLoop] queueAction: a]; } } } } void action_run_string(NSString *name, AZClient *c) { AZAction *a = [AZAction actionWithName: name userAction: OB_USER_ACTION_NONE]; if (a == nil) { NSLog(@"Internal Error: Cannot get action from string: %@", name); return; } action_run([NSArray arrayWithObjects: a, nil], c, 0); } void action_execute(union ActionData *data) { if (data->execute.path) { NSString *p = nil; if ([data->execute.path isAbsolutePath]) p = data->execute.path; else { /* Look through paths */ NSProcessInfo *pi = [NSProcessInfo processInfo]; NSArray *ps = [[[pi environment] objectForKey: @"PATH"] componentsSeparatedByString: @":"]; NSFileManager *fm = [NSFileManager defaultManager]; int i, count = [ps count]; BOOL isDir; NSString *a; for (i = 0; i < count; i++) { a = [[ps objectAtIndex: i] stringByAppendingPathComponent: data->execute.path]; if ([fm fileExistsAtPath: a isDirectory: &isDir] && (isDir == NO)) { p = a; break; } } } if (p) { [NSTask launchedTaskWithLaunchPath: p arguments: nil]; } else { NSLog(@"Cannot find command %@", data->execute.path); } } } void action_activate(union ActionData *data) { [data->activate.any.c activateHere: data->activate.here]; } void action_focus(union ActionData *data) { /* if using focus_delay, stop the timer now so that focus doesn't go moving on us */ [[AZEventHandler defaultHandler] haltFocusDelay]; [data->client.any.c focus]; } void action_unfocus (union ActionData *data) { AZFocusManager *fmanager = [AZFocusManager defaultManager]; // [data->client.any.c unfocus]; if (data->client.any.c == [fmanager focus_client]) [fmanager fallbackTarget: OB_FOCUS_FALLBACK_UNFOCUSING]; } void action_iconify(union ActionData *data) { client_action_start(data); [data->client.any.c iconify: YES currentDesktop: YES]; client_action_end(data); } void action_focus_order_to_bottom(union ActionData *data) { [[AZFocusManager defaultManager] focusOrderToBottom: data->client.any.c]; } void action_raiselower(union ActionData *data) { AZClient *c = data->client.any.c; BOOL raise = NO; int i, count = [[AZStacking stacking] count]; for (i = 0; i < count; i++) { id temp = [[AZStacking stacking] windowAtIndex: i]; if (WINDOW_IS_CLIENT(temp)) { AZClient *cit = (AZClient *)temp; if (cit == c) break; if ([cit normal] == [c normal] && [cit layer] == [c layer] && [[cit frame] visible] && ![c searchTransient: cit]) { if (RECT_INTERSECTS_RECT([[cit frame] area], [[c frame] area])) { raise = YES; break; } } } } if (raise) action_raise(data); else action_lower(data); } void action_raise(union ActionData *data) { client_action_start(data); [[AZStacking stacking] raiseWindow: data->client.any.c group: data->stacking.group]; client_action_end(data); } void action_unshaderaise(union ActionData *data) { if ([data->client.any.c shaded]) action_unshade(data); else action_raise(data); } void action_shadelower(union ActionData *data) { if ([data->client.any.c shaded]) action_lower(data); else action_shade(data); } void action_lower(union ActionData *data) { client_action_start(data); [[AZStacking stacking] lowerWindow: data->client.any.c group: data->stacking.group]; client_action_end(data); } void action_close(union ActionData *data) { [data->client.any.c close]; } void action_kill(union ActionData *data) { [data->client.any.c kill]; } void action_shade(union ActionData *data) { client_action_start(data); [data->client.any.c shade: YES]; client_action_end(data); } void action_unshade(union ActionData *data) { client_action_start(data); [data->client.any.c shade: NO]; client_action_end(data); } void action_toggle_shade(union ActionData *data) { client_action_start(data); [data->client.any.c shade: ![data->client.any.c shaded]]; client_action_end(data); } void action_toggle_omnipresent(union ActionData *data) { int num = ([data->client.any.c desktop] == DESKTOP_ALL) ? [[AZScreen defaultScreen] desktop] : DESKTOP_ALL; [data->client.any.c setDesktop: num hide: NO]; } void action_move_relative_horz(union ActionData *data) { AZClient *c = data->relative.any.c; client_action_start(data); [c moveToX: [c area].x + data->relative.deltax y: [c area].y]; client_action_end(data); } void action_move_relative_vert(union ActionData *data) { AZClient *c = data->relative.any.c; client_action_start(data); [c moveToX: [c area].x y: [c area].y + data->relative.deltax]; client_action_end(data); } void action_move_to_center(union ActionData *data) { AZClient *c = data->client.any.c; Rect *area; area = [[AZScreen defaultScreen] areaOfDesktop: [c desktop] monitor: 0]; client_action_start(data); [c moveToX: area->width / 2 - [c area].width / 2 y: area->height / 2 - [c area].height / 2]; client_action_end(data); } void action_resize_relative_horz(union ActionData *data) { AZClient *c = data->relative.any.c; client_action_start(data); [c resizeToWidth: [c area].width + data->relative.deltax * [c size_inc].width height: [c area].height]; client_action_end(data); } void action_resize_relative_vert(union ActionData *data) { AZClient *c = data->relative.any.c; if (![c shaded]) { client_action_start(data); [c resizeToWidth: [c area].width height: [c area].height + data->relative.deltax * [c size_inc].height]; client_action_end(data); } } void action_move_relative(union ActionData *data) { AZClient *c = data->relative.any.c; client_action_start(data); [c moveToX: [c area].x + data->relative.deltax y: [c area].y + data->relative.deltay]; client_action_end(data); } void action_resize_relative(union ActionData *data) { AZClient *c = data->relative.any.c; client_action_start(data); [c moveAndResizeToX: [c area].x - data->relative.deltaxl * [c size_inc].width y: [c area].y - data->relative.deltayu * [c size_inc].height width: [c area].width + data->relative.deltax * [c size_inc].width + data->relative.deltaxl * [c size_inc].width height: [c area].height + data->relative.deltay * [c size_inc].height + data->relative.deltayu * [c size_inc].height]; client_action_end(data); } void action_maximize_full(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: YES direction: 0 saveArea: YES]; client_action_end(data); } void action_unmaximize_full(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: NO direction: 0 saveArea: YES]; client_action_end(data); } void action_toggle_maximize_full(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: !([data->client.any.c max_horz] || [data->client.any.c max_vert]) direction: 0 saveArea: YES]; client_action_end(data); } void action_maximize_horz(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: YES direction: 1 saveArea: YES]; client_action_end(data); } void action_unmaximize_horz(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: NO direction: 1 saveArea: YES]; client_action_end(data); } void action_toggle_maximize_horz(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: ![data->client.any.c max_horz] direction: 1 saveArea: YES]; client_action_end(data); } void action_maximize_vert(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: YES direction: 2 saveArea: YES]; client_action_end(data); } void action_unmaximize_vert(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: NO direction: 2 saveArea: YES]; client_action_end(data); } void action_toggle_maximize_vert(union ActionData *data) { client_action_start(data); [data->client.any.c maximize: ![data->client.any.c max_vert] direction: 2 saveArea: YES]; client_action_end(data); } void action_toggle_fullscreen(union ActionData *data) { client_action_start(data); [data->client.any.c fullscreen: !([data->client.any.c fullscreen]) saveArea: YES]; client_action_end(data); } void action_send_to_desktop(union ActionData *data) { AZClient *c = data->sendto.any.c; if (![c normal]) return; AZScreen *defaultScreen = [AZScreen defaultScreen]; if (data->sendto.desk < [defaultScreen numberOfDesktops] || data->sendto.desk == DESKTOP_ALL) { [c setDesktop: data->sendto.desk hide: data->sendto.follow]; if (data->sendto.follow) { [defaultScreen setDesktop: data->sendto.desk]; } } } void action_desktop(union ActionData *data) { static unsigned int first = (unsigned) -1; AZScreen *defaultScreen = [AZScreen defaultScreen]; if (data->inter.any.interactive && first == (unsigned) -1) { first = [defaultScreen desktop]; } if (!data->inter.any.interactive || (!data->inter.cancel && !data->inter.final)) { if (data->desktop.desk < [defaultScreen numberOfDesktops] || data->desktop.desk == DESKTOP_ALL) { [defaultScreen setDesktop: data->desktop.desk]; if (data->inter.any.interactive) { [defaultScreen desktopPopup: data->desktop.desk show: YES]; } } } else if (data->inter.cancel) { [defaultScreen setDesktop: first]; } if (!data->inter.any.interactive || data->inter.final) { [defaultScreen desktopPopup: 0 show: NO]; first = (unsigned) -1; } } void action_desktop_dir(union ActionData *data) { unsigned int d; AZScreen *screen = [AZScreen defaultScreen]; d = [screen cycleDesktop: data->desktopdir.dir wrap: data->desktopdir.wrap linear: data->desktopdir.linear dialog: data->desktopdir.inter.any.interactive done: data->desktopdir.inter.final cancel: data->desktopdir.inter.cancel]; if (!data->sendtodir.inter.any.interactive || !data->sendtodir.inter.final || data->sendtodir.inter.cancel) { [screen setDesktop: d]; } } void action_send_to_desktop_dir(union ActionData *data) { AZClient *c = data->sendtodir.inter.any.c; unsigned int d; if (![c normal]) return; AZScreen *screen = [AZScreen defaultScreen]; d = [screen cycleDesktop: data->sendtodir.dir wrap: data->sendtodir.wrap linear: data->sendtodir.linear dialog: data->sendtodir.inter.any.interactive done: data->sendtodir.inter.final cancel: data->sendtodir.inter.cancel]; if (!data->sendtodir.inter.any.interactive || !data->sendtodir.inter.final || data->sendtodir.inter.cancel) { [c setDesktop: d hide: data->sendtodir.follow]; if (data->sendtodir.follow) { [screen setDesktop: d]; } } } void action_desktop_last(union ActionData *data) { AZScreen *screen = [AZScreen defaultScreen]; [screen setDesktop: [screen lastDesktop]]; } void action_toggle_decorations(union ActionData *data) { AZClient *c = data->client.any.c; client_action_start(data); [c setUndecorated: ![c undecorated]]; client_action_end(data); } static unsigned long pick_corner(int x, int y, int cx, int cy, int cw, int ch) { if (config_resize_four_corners) { if (x - cx > cw / 2) { if (y - cy > ch / 2) return prop_atoms.net_wm_moveresize_size_bottomright; else return prop_atoms.net_wm_moveresize_size_topright; } else { if (y - cy > ch / 2) return prop_atoms.net_wm_moveresize_size_bottomleft; else return prop_atoms.net_wm_moveresize_size_topleft; } } else { if (x - cx > cw * 2 / 3) { if (y - cy > ch * 2 / 3) return prop_atoms.net_wm_moveresize_size_bottomright; else if (y - cy < ch / 3) return prop_atoms.net_wm_moveresize_size_topright; else return prop_atoms.net_wm_moveresize_size_right; } else if (x - cx < cw / 3) { if (y - cy > ch * 2 / 3) return prop_atoms.net_wm_moveresize_size_bottomleft; else if (y - cy < ch / 3) return prop_atoms.net_wm_moveresize_size_topleft; else return prop_atoms.net_wm_moveresize_size_left; } else if (y - cy > ch * 2 / 3) return prop_atoms.net_wm_moveresize_size_bottom; else if (y - cy < ch / 3) return prop_atoms.net_wm_moveresize_size_top; else return prop_atoms.net_wm_moveresize_move; } } void action_moveresize(union ActionData *data) { AZClient *c = data->moveresize.any.c; unsigned long corner; if (![c normal]) return; if (data->moveresize.keyboard) { corner = (data->moveresize.move ? prop_atoms.net_wm_moveresize_move_keyboard : prop_atoms.net_wm_moveresize_size_keyboard); } else { corner = (data->moveresize.move ? prop_atoms.net_wm_moveresize_move : pick_corner(data->any.x, data->any.y, [[c frame] area].x, [[c frame] area].y, /* use the client size because the frame can be differently sized (shaded windows) and we want this based on the clients size */ [c area].width + [[c frame] size].left + [[c frame] size].right, [c area].height + [[c frame] size].top + [[c frame] size].bottom)); } [[AZMoveResizeHandler defaultHandler] startWithClient: c x: data->any.x y: data->any.y button: data->any.button corner: corner]; } void action_reconfigure(union ActionData *data) { ob_reconfigure(); } void action_restart(union ActionData *data) { ob_restart_other([data->execute.path cString]); } void action_exit(union ActionData *data) { ob_exit(0); } void action_showmenu(union ActionData *data) { if (data->showmenu.name) { [[AZMenuManager defaultManager] showMenu: data->showmenu.name x: data->any.x y: data->any.y client: data->showmenu.any.c]; } } void action_cycle_windows(union ActionData *data) { /* if using focus_delay, stop the timer now so that focus doesn't go moving on us */ [[AZEventHandler defaultHandler] haltFocusDelay]; [[AZFocusManager defaultManager] cycleForward: data->cycle.forward linear: data->cycle.linear interactive: data->any.interactive dialog: data->cycle.dialog done: data->cycle.inter.final cancel: data->cycle.inter.cancel]; } void action_directional_focus(union ActionData *data) { /* if using focus_delay, stop the timer now so that focus doesn't go moving on us */ [[AZEventHandler defaultHandler] haltFocusDelay]; [[AZFocusManager defaultManager] directionalCycle: data->interdiraction.direction interactive: data->any.interactive dialog: data->interdiraction.dialog done: data->interdiraction.inter.final cancel: data->interdiraction.inter.cancel]; } void action_movetoedge(union ActionData *data) { int x, y; AZClient *c = data->diraction.any.c; x = [[c frame] area].x; y = [[c frame] area].y; switch(data->diraction.direction) { case OB_DIRECTION_NORTH: y = [c directionalEdgeSearch: OB_DIRECTION_NORTH]; break; case OB_DIRECTION_WEST: x = [c directionalEdgeSearch: OB_DIRECTION_WEST]; break; case OB_DIRECTION_SOUTH: y = [c directionalEdgeSearch: OB_DIRECTION_SOUTH] - [[c frame] area].height; break; case OB_DIRECTION_EAST: x = [c directionalEdgeSearch: OB_DIRECTION_EAST] - [[c frame] area].width; break; default: NSLog(@"Internal Error: should not reach here"); } [[c frame] frameGravityAtX: &x y: &y]; client_action_start(data); [c moveToX: x y: y]; client_action_end(data); } void action_growtoedge(union ActionData *data) { int x, y, width, height, dest; AZClient *c = data->diraction.any.c; Rect *a; //FIXME growtoedge resizes shaded windows to 0 height if ([c shaded]) return; a = [[AZScreen defaultScreen] areaOfDesktop: [c desktop]]; x = [[c frame] area].x; y = [[c frame] area].y; width = [[c frame] area].width; height = [[c frame] area].height; switch(data->diraction.direction) { case OB_DIRECTION_NORTH: dest = [c directionalEdgeSearch: OB_DIRECTION_NORTH]; if (a->y == y) height = [[c frame] area].height / 2; else { height = [[c frame] area].y + [[c frame] area].height - dest; y = dest; } break; case OB_DIRECTION_WEST: dest = [c directionalEdgeSearch: OB_DIRECTION_WEST]; if (a->x == x) width = [[c frame] area].width / 2; else { width = [[c frame] area].x + [[c frame] area].width - dest; x = dest; } break; case OB_DIRECTION_SOUTH: dest = [c directionalEdgeSearch: OB_DIRECTION_SOUTH]; if (a->y + a->height == y + [[c frame] area].height) { height = [[c frame] area].height / 2; y = a->y + a->height - height; } else height = dest - [[c frame] area].y; y += (height - [[c frame] area].height) % [c size_inc].height; height -= (height - [[c frame] area].height) % [c size_inc].height; break; case OB_DIRECTION_EAST: dest = [c directionalEdgeSearch: OB_DIRECTION_EAST]; if (a->x + a->width == x + [[c frame] area].width) { width = [[c frame] area].width / 2; x = a->x + a->width - width; } else width = dest - [[c frame] area].x; x += (width - [[c frame] area].width) % [c size_inc].width; width -= (width - [[c frame] area].width) % [c size_inc].width; break; default: NSLog(@"Internal Error: should not reach here"); } [[c frame] frameGravityAtX: &x y: &y]; width -= [[c frame] size].left + [[c frame] size].right; height -= [[c frame] size].top + [[c frame] size].bottom; client_action_start(data); [c moveAndResizeToX: x y: y width: width height: height]; client_action_end(data); } void action_send_to_layer(union ActionData *data) { [(data->layer.any.c) setLayer: data->layer.layer]; } void action_toggle_layer(union ActionData *data) { AZClient *c = data->layer.any.c; client_action_start(data); if (data->layer.layer < 0) { [c setLayer: [c below] ? 0 : -1]; } else if (data->layer.layer > 0) { [c setLayer: [c above] ? 0 : 1]; } client_action_end(data); } void action_toggle_dockautohide(union ActionData *data) { config_dock_hide = !config_dock_hide; [[AZDock defaultDock] configure]; } void action_toggle_show_desktop(union ActionData *data) { AZScreen *screen = [AZScreen defaultScreen]; [screen showDesktop: ![screen showingDesktop]]; } void action_show_desktop(union ActionData *data) { [[AZScreen defaultScreen] showDesktop: YES]; } void action_unshow_desktop(union ActionData *data) { [[AZScreen defaultScreen] showDesktop: NO]; }