/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*- AZDock.m for the Azalea window manager Copyright (c) 2006 Yen-Ju Chen dock.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 "openbox.h" #import "AZMainLoop.h" #import "AZScreen.h" #import "AZDebug.h" #import "AZStacking.h" #import "prop.h" #import "config.h" #define DOCK_EVENT_MASK (ButtonPressMask | ButtonReleaseMask | \ EnterWindowMask | LeaveWindowMask) #define DOCKAPP_EVENT_MASK (StructureNotifyMask) static AZDock *sharedInstance; @interface AZDock (AZPrivate) - (BOOL) hideTimeout: (id) data; - (BOOL) showTimeout: (id) data; @end @implementation AZDock - (void) startup: (BOOL) reconfig { XSetWindowAttributes attrib; if (reconfig) { XSetWindowBorder(ob_display, x_frame, RrColorPixel(ob_rr_theme->b_color)); XSetWindowBorderWidth(ob_display, x_frame, ob_rr_theme->bwidth); DESTROY(a_frame); a_frame = [ob_rr_theme->a_focused_title copy]; [[AZStacking stacking] addWindow: self]; [self configure]; [self setHide: YES]; int i, count = [dock_apps count]; for (i = 0; i < count; i++) { [(AZDockApp *)[dock_apps objectAtIndex: i] grabButton: YES]; } return; } STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); hidden = YES; attrib.event_mask = DOCK_EVENT_MASK; attrib.override_redirect = True; x_frame = XCreateWindow(ob_display, RootWindow(ob_display, ob_screen), 0, 0, 1, 1, 0, [ob_rr_inst depth], InputOutput, [ob_rr_inst visual], CWOverrideRedirect | CWEventMask, &attrib); a_frame = [ob_rr_theme->a_focused_title copy]; XSetWindowBorder(ob_display, x_frame, RrColorPixel(ob_rr_theme->b_color)); XSetWindowBorderWidth(ob_display, x_frame, ob_rr_theme->bwidth); [window_map setObject: self forKey: [NSNumber numberWithInt: x_frame]]; [[AZStacking stacking] addWindow: self]; } - (void) shutdown: (BOOL) reconfig { if (reconfig) { [[AZStacking stacking] removeWindow: self]; int i, count = [dock_apps count]; for (i = 0; i < count; i++) { [(AZDockApp *)[dock_apps objectAtIndex: i] grabButton: NO]; } return; } XDestroyWindow(ob_display, x_frame); DESTROY(a_frame); [[AZStacking stacking] removeWindow: self]; [window_map removeObjectForKey: [NSNumber numberWithInt: x_frame]]; } - (void) addWindow: (Window) win hints: (XWMHints *) wmhints { AZDockApp *app; XWindowAttributes attrib; NSArray *data = nil; app = [[AZDockApp alloc] init]; [app setWindow: win]; [app setIconWindow: (wmhints->flags & IconWindowHint) ? wmhints->icon_window : win]; if (PROP_GETSS([app window], wm_class, locale, &data)) { if ([data count] > 0) { [app setName: [data objectAtIndex: 0]]; if ([data count] > 1) [app setClass: [data objectAtIndex: 1]]; } } if ([app name] == nil) [app setName: [NSString string]]; if ([app class] == nil) [app setClass: [NSString string]]; if (XGetWindowAttributes(ob_display, [app iconWindow], &attrib)) { [app setW: attrib.width]; [app setH: attrib.height]; } else { [app setW: 64]; [app setH: 64]; } [dock_apps addObject: app]; [self configure]; XReparentWindow(ob_display, [app iconWindow], x_frame, [app x], [app y]); /* This is the same case as in frame.c for client windows. When Openbox is starting, the window is already mapped so we see unmap events occur for it. There are 2 unmap events generated that we see, one with the 'event' member set the root window, and one set to the client, but both get handled and need to be ignored. */ if (ob_state() == OB_STATE_STARTING) { [app setIgnoreUnmaps: [app ignoreUnmaps]+2]; } if ([app window] != [app iconWindow]) { /* have to map it so that it can be re-managed on a restart */ XMoveWindow(ob_display, [app window], -1000, -1000); XMapWindow(ob_display, [app window]); } XMapWindow(ob_display, [app iconWindow]); XSync(ob_display, False); /* specify that if we exit, the window should not be destroyed and should be reparented back to root automatically */ XChangeSaveSet(ob_display, [app iconWindow], SetModeInsert); XSelectInput(ob_display, [app iconWindow], DOCKAPP_EVENT_MASK); [app grabButton: YES]; /* Fake dock_app struct */ [window_map setObject: app forKey: [NSNumber numberWithInt: [app iconWindow]]]; AZDebug("Managed Dock App: 0x%lx (%s)\n", [app iconWindow], [app class]); //RELEASE(app); /* crash if release here. Have to release at -remove:: */ } - (void) removeAll { int i, count = [dock_apps count]; for (i = count-1; i > -1; i--) { [self remove: [dock_apps objectAtIndex: i] reparent: YES]; } } - (void) remove: (AZDockApp *) app reparent: (BOOL) reparent { [app grabButton: NO]; XSelectInput(ob_display, [app iconWindow], NoEventMask); /* remove the window from our save set */ XChangeSaveSet(ob_display, [app iconWindow], SetModeDelete); XSync(ob_display, False); [window_map removeObjectForKey: [NSNumber numberWithInt: [app iconWindow]]]; if (reparent) XReparentWindow(ob_display, [app iconWindow], RootWindow(ob_display, ob_screen), [app x], [app y]); [dock_apps removeObject: app]; [self configure]; AZDebug("Unmanaged Dock App: 0x%lx (%s)\n", [app iconWindow], [app class]); DESTROY(app); /* it is added in addWindow: */ } - (void) configure { int spot; int gravity; int minw, minh; int strw, strh; Rect *a; [a_frame minimalSizeWithWidth: &minw height: &minh]; w = h = 0; /* get the size */ int i, count = [dock_apps count]; for (i = 0; i < count; i++) { AZDockApp *app = [dock_apps objectAtIndex: i]; switch (config_dock_orient) { case OB_ORIENTATION_HORZ: w += [app w]; h = MAX(h, [app h]); break; case OB_ORIENTATION_VERT: w = MAX(w, [app w]); h += [app h]; break; } } spot = (config_dock_orient == OB_ORIENTATION_HORZ ? minw : minh) / 2; /* position the apps */ count = [dock_apps count]; for (i = 0; i < count; i++) { AZDockApp *app = [dock_apps objectAtIndex: i]; switch (config_dock_orient) { case OB_ORIENTATION_HORZ: [app setX: spot]; [app setY: (h - [app h]) / 2]; spot += [app w]; break; case OB_ORIENTATION_VERT: [app setX: (w - [app w]) / 2]; [app setY: spot]; spot += [app h]; break; } XMoveWindow(ob_display, [app iconWindow], [app x], [app y]); } /* used for calculating offsets */ w += ob_rr_theme->bwidth * 2; h += ob_rr_theme->bwidth * 2; a = [[AZScreen defaultScreen] physicalArea]; /* calculate position */ if (config_dock_floating) { x = config_dock_x; y = config_dock_y; gravity = NorthWestGravity; } else { switch (config_dock_pos) { case OB_DIRECTION_NORTHWEST: x = 0; y = 0; gravity = NorthWestGravity; break; case OB_DIRECTION_NORTH: x = a->width / 2; y = 0; gravity = NorthGravity; break; case OB_DIRECTION_NORTHEAST: x = a->width; y = 0; gravity = NorthEastGravity; break; case OB_DIRECTION_WEST: x = 0; y = a->height / 2; gravity = WestGravity; break; case OB_DIRECTION_EAST: x = a->width; y = a->height / 2; gravity = EastGravity; break; case OB_DIRECTION_SOUTHWEST: x = 0; y = a->height; gravity = SouthWestGravity; break; case OB_DIRECTION_SOUTH: x = a->width / 2; y = a->height; gravity = SouthGravity; break; case OB_DIRECTION_SOUTHEAST: x = a->width; y = a->height; gravity = SouthEastGravity; break; } } switch(gravity) { case NorthGravity: case CenterGravity: case SouthGravity: x -= w / 2; break; case NorthEastGravity: case EastGravity: case SouthEastGravity: x -= w; break; } switch(gravity) { case WestGravity: case CenterGravity: case EastGravity: y -= h / 2; break; case SouthWestGravity: case SouthGravity: case SouthEastGravity: y -= h; break; } if (config_dock_hide && hidden) { if (!config_dock_floating) { switch (config_dock_pos) { case OB_DIRECTION_NORTHWEST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: y -= h - ob_rr_theme->bwidth; break; case OB_ORIENTATION_VERT: x -= w - ob_rr_theme->bwidth; break; } break; case OB_DIRECTION_NORTH: y -= h - ob_rr_theme->bwidth; break; case OB_DIRECTION_NORTHEAST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: y -= h - ob_rr_theme->bwidth; break; case OB_ORIENTATION_VERT: x += w - ob_rr_theme->bwidth; break; } break; case OB_DIRECTION_WEST: x -= w - ob_rr_theme->bwidth; break; case OB_DIRECTION_EAST: x += w - ob_rr_theme->bwidth; break; case OB_DIRECTION_SOUTHWEST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: y += h - ob_rr_theme->bwidth; break; case OB_ORIENTATION_VERT: x -= w - ob_rr_theme->bwidth; break; } break; case OB_DIRECTION_SOUTH: y += h - ob_rr_theme->bwidth; break; case OB_DIRECTION_SOUTHEAST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: y += h - ob_rr_theme->bwidth; break; case OB_ORIENTATION_VERT: x += w - ob_rr_theme->bwidth; break; } break; } } } if (!config_dock_floating && config_dock_hide) { strw = ob_rr_theme->bwidth; strh = ob_rr_theme->bwidth; } else { strw = w; strh = h; } /* set the strut */ if ([dock_apps count] == 0) { STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } else if (config_dock_floating || config_dock_nostrut) { STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); } else { switch (config_dock_pos) { case OB_DIRECTION_NORTHWEST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0, 0, 0, x, x + w - 1, 0, 0, 0, 0); break; case OB_ORIENTATION_VERT: STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0, 0, 0); break; } break; case OB_DIRECTION_NORTH: STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0, x, x + w - 1, 0, 0, 0, 0, 0, 0); break; case OB_DIRECTION_NORTHEAST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: STRUT_PARTIAL_SET(dock_strut, 0, strh, 0, 0, 0, 0, x, x + w -1, 0, 0, 0, 0); break; case OB_ORIENTATION_VERT: STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0, 0, 0, 0, 0, y, y + h - 1, 0, 0); break; } break; case OB_DIRECTION_WEST: STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0, 0, 0); break; case OB_DIRECTION_EAST: STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0, 0, 0, 0, 0, y, y + h - 1, 0, 0); break; case OB_DIRECTION_SOUTHWEST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh, 0, 0, 0, 0, 0, 0, x, x + w - 1); break; case OB_ORIENTATION_VERT: STRUT_PARTIAL_SET(dock_strut, strw, 0, 0, 0, y, y + h - 1, 0, 0, 0, 0, 0, 0); break; } break; case OB_DIRECTION_SOUTH: STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh, 0, 0, 0, 0, 0, 0, x, x + w - 1); break; case OB_DIRECTION_SOUTHEAST: switch (config_dock_orient) { case OB_ORIENTATION_HORZ: STRUT_PARTIAL_SET(dock_strut, 0, 0, 0, strh, 0, 0, 0, 0, 0, 0, x, x + w - 1); break; case OB_ORIENTATION_VERT: STRUT_PARTIAL_SET(dock_strut, 0, 0, strw, 0, 0, 0, 0, 0, y, y + h - 1, 0, 0); break; } break; } } w += minw; h += minh; /* not used for actually sizing shit */ w -= ob_rr_theme->bwidth * 2; h -= ob_rr_theme->bwidth * 2; if ([dock_apps count]) { NSAssert(w > 0, @"Width is less than 0"); NSAssert(h > 0, @"Height is less than 0"); XMoveResizeWindow(ob_display, x_frame, x, y, w, h); [a_frame paint: x_frame width: w height: h]; XMapWindow(ob_display, x_frame); } else XUnmapWindow(ob_display, x_frame); /* but they are useful outside of this function! */ w += ob_rr_theme->bwidth * 2; h += ob_rr_theme->bwidth * 2; [[AZScreen defaultScreen] updateAreas]; } - (void) setHide: (BOOL) hide { AZMainLoop *mainLoop = [AZMainLoop mainLoop]; if (!hide) { if (hidden && config_dock_hide) { [mainLoop addTimeout: self handler: @selector(showTimeout:) microseconds: config_dock_show_delay data: nil notify: NULL]; } else if (!hidden && config_dock_hide) { [mainLoop removeTimeout: self handler: @selector(hideTimeout:)]; } } else { if (!hidden && config_dock_hide) { [mainLoop addTimeout: self handler: @selector(hideTimeout:) microseconds: config_dock_hide_delay data: nil notify: NULL]; } else if (hidden && config_dock_hide) { [mainLoop removeTimeout: self handler: @selector(showTimeout:)]; } } } - (int) x { return x; } - (int) y { return y; } - (int) w { return w; } - (int) h { return h; } - (StrutPartial) strut { return dock_strut; } - (Window) frame { return x_frame; } - (NSArray *) dockApplications { return dock_apps; } - (void) moveDockApp: (AZDockApp *) app toIndex: (int) index { int orig = [dock_apps indexOfObject: app]; if ((orig == NSNotFound) || (orig == index)) return; RETAIN(app); [dock_apps removeObjectAtIndex: orig]; if (orig < index) index--; [dock_apps insertObject: app atIndex: index]; RELEASE(app); } - (id) init { self = [super init]; dock_apps = [[NSMutableArray alloc] init]; return self; } - (void) dealloc { DESTROY(dock_apps); [super dealloc]; } + (AZDock *) defaultDock { if (sharedInstance == nil) { sharedInstance = [[AZDock alloc] init]; } return sharedInstance; } /* AZWindow protocol */ - (Window_InternalType) windowType { return Window_Dock; } - (int) windowLayer { return config_dock_layer; } - (Window) windowTop { return x_frame; } @end @implementation AZDock (AZPrivate) - (BOOL) hideTimeout: (id) data { /* hide */ hidden = YES; [self configure]; return NO; /* don't repeat */ } - (BOOL) showTimeout: (id) data { /* show */ hidden = NO; [self configure]; return NO; /* don't repeat */ } @end