mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-20 12:58:08 +01:00
This patch avoid a crash when moving an icon without command. To reproduce the problem: 1. Launch an application, for example xeyes, with appicon. 2. Move the appicon to the clip. 3. Close the application. 4. Edit the appicon in the clip, and empty the commands fields. 5. Move the appicon from the clip to the dock. -> Crash. The crash happends because icon->icon->owner is NULL and then wwin will be NULL. Then the call of wwin->client_win will crash. This patch checks if icon->icon->owner is not null (application is running) and then assign it to wwin. Then get the command from the running application.
4005 lines
101 KiB
C
4005 lines
101 KiB
C
/* dock.c- built-in Dock module for WindowMaker
|
|
*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
|
* Copyright (c) 1998-2003 Dan Pascu
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "wconfig.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <unistd.h>
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
|
|
#ifndef PATH_MAX
|
|
#define PATH_MAX DEFAULT_PATH_MAX
|
|
#endif
|
|
|
|
#include "WindowMaker.h"
|
|
#include "wcore.h"
|
|
#include "window.h"
|
|
#include "icon.h"
|
|
#include "appicon.h"
|
|
#include "actions.h"
|
|
#include "stacking.h"
|
|
#include "dock.h"
|
|
#include "dockedapp.h"
|
|
#include "dialog.h"
|
|
#include "main.h"
|
|
#include "properties.h"
|
|
#include "menu.h"
|
|
#include "client.h"
|
|
#include "defaults.h"
|
|
#include "workspace.h"
|
|
#include "framewin.h"
|
|
#include "superfluous.h"
|
|
#include "xinerama.h"
|
|
|
|
/**** Local variables ****/
|
|
#define CLIP_REWIND 1
|
|
#define CLIP_IDLE 0
|
|
#define CLIP_FORWARD 2
|
|
|
|
/**** Global variables ****/
|
|
extern Cursor wCursor[WCUR_LAST];
|
|
extern WPreferences wPreferences;
|
|
extern XContext wWinContext;
|
|
extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
|
|
|
|
#define MOD_MASK wPreferences.modifier_mask
|
|
#define ICON_SIZE wPreferences.icon_size
|
|
|
|
/***** Local variables ****/
|
|
|
|
static WMPropList *dCommand = NULL;
|
|
static WMPropList *dPasteCommand = NULL;
|
|
#ifdef XDND /* XXX was OFFIX */
|
|
static WMPropList *dDropCommand = NULL;
|
|
#endif
|
|
static WMPropList *dAutoLaunch, *dLock;
|
|
static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
|
|
static WMPropList *dHost, *dDock, *dClip;
|
|
static WMPropList *dAutoAttractIcons;
|
|
|
|
static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
|
|
|
|
static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
|
|
|
|
static void dockIconPaint(WAppIcon *btn);
|
|
|
|
static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
|
|
|
|
static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
|
|
|
|
static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
|
|
|
|
static int getClipButton(int px, int py);
|
|
|
|
static void toggleLowered(WDock *dock);
|
|
|
|
static void toggleCollapsed(WDock *dock);
|
|
|
|
static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
|
|
|
|
static void clipLeave(WDock *dock);
|
|
|
|
static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
|
|
|
|
static Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
|
|
|
|
static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
|
|
static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
|
|
static void clipAutoCollapse(void *cdata);
|
|
static void clipAutoExpand(void *cdata);
|
|
static void launchDockedApplication(WAppIcon *btn, Bool withSelection);
|
|
|
|
static void clipAutoLower(void *cdata);
|
|
static void clipAutoRaise(void *cdata);
|
|
static void reattachIcon(WDock *dock, WAppIcon *icon, int x, int y);
|
|
static WAppIcon *mainIconCreate(WScreen *scr, int type);
|
|
|
|
static void make_keys(void)
|
|
{
|
|
if (dCommand != NULL)
|
|
return;
|
|
|
|
dCommand = WMRetainPropList(WMCreatePLString("Command"));
|
|
dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
|
|
#ifdef XDND
|
|
dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
|
|
#endif
|
|
dLock = WMRetainPropList(WMCreatePLString("Lock"));
|
|
dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
|
|
dName = WMRetainPropList(WMCreatePLString("Name"));
|
|
dForced = WMRetainPropList(WMCreatePLString("Forced"));
|
|
dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
|
|
dYes = WMRetainPropList(WMCreatePLString("Yes"));
|
|
dNo = WMRetainPropList(WMCreatePLString("No"));
|
|
dHost = WMRetainPropList(WMCreatePLString("Host"));
|
|
|
|
dPosition = WMCreatePLString("Position");
|
|
dApplications = WMCreatePLString("Applications");
|
|
dLowered = WMCreatePLString("Lowered");
|
|
dCollapsed = WMCreatePLString("Collapsed");
|
|
dAutoCollapse = WMCreatePLString("AutoCollapse");
|
|
dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
|
|
dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
|
|
|
|
dOmnipresent = WMCreatePLString("Omnipresent");
|
|
|
|
dDock = WMCreatePLString("Dock");
|
|
dClip = WMCreatePLString("Clip");
|
|
}
|
|
|
|
static void renameCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WDock *dock = entry->clientdata;
|
|
char buffer[128];
|
|
int wspace;
|
|
char *name;
|
|
|
|
assert(entry->clientdata != NULL);
|
|
|
|
wspace = dock->screen_ptr->current_workspace;
|
|
|
|
name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
|
|
|
|
snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace + 1);
|
|
if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer, &name))
|
|
wWorkspaceRename(dock->screen_ptr, wspace, name);
|
|
|
|
wfree(name);
|
|
}
|
|
|
|
static void toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
assert(entry->clientdata != NULL);
|
|
|
|
toggleLowered(entry->clientdata);
|
|
|
|
entry->flags.indicator_on = !(((WDock *) entry->clientdata)->lowered);
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static int matchWindow(const void *item, const void *cdata)
|
|
{
|
|
return (((WFakeGroupLeader *) item)->leader == (Window) cdata);
|
|
}
|
|
|
|
static void killCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WScreen *scr = menu->menu->screen_ptr;
|
|
WAppIcon *icon;
|
|
WFakeGroupLeader *fPtr;
|
|
char *buffer, *shortname, **argv;
|
|
char *basename(const char *shortname);
|
|
int argc;
|
|
|
|
if (!WCHECK_STATE(WSTATE_NORMAL))
|
|
return;
|
|
|
|
assert(entry->clientdata != NULL);
|
|
|
|
icon = (WAppIcon *) entry->clientdata;
|
|
|
|
icon->editing = 1;
|
|
|
|
WCHANGE_STATE(WSTATE_MODAL);
|
|
|
|
/* strip away dir names */
|
|
shortname = basename(icon->command);
|
|
/* separate out command options */
|
|
wtokensplit(shortname, &argv, &argc);
|
|
|
|
buffer = wstrconcat(argv[0],
|
|
_(" will be forcibly closed.\n"
|
|
"Any unsaved changes will be lost.\n" "Please confirm."));
|
|
|
|
if (icon->icon && icon->icon->owner) {
|
|
fPtr = icon->icon->owner->fake_group;
|
|
} else {
|
|
/* is this really necessary? can we kill a non-running dock icon? */
|
|
Window win = icon->main_window;
|
|
int index;
|
|
|
|
index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void *)win);
|
|
if (index != WANotFound)
|
|
fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
|
|
else
|
|
fPtr = NULL;
|
|
}
|
|
|
|
if (wPreferences.dont_confirm_kill
|
|
|| wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
|
|
buffer, _("Yes"), _("No"), NULL) == WAPRDefault) {
|
|
if (fPtr != NULL) {
|
|
WWindow *wwin, *twin;
|
|
|
|
wwin = scr->focused_window;
|
|
while (wwin) {
|
|
twin = wwin->prev;
|
|
if (wwin->fake_group == fPtr)
|
|
wClientKill(wwin);
|
|
|
|
wwin = twin;
|
|
}
|
|
} else if (icon->icon && icon->icon->owner) {
|
|
wClientKill(icon->icon->owner);
|
|
}
|
|
}
|
|
|
|
wfree(buffer);
|
|
wtokenfree(argv, argc);
|
|
|
|
icon->editing = 0;
|
|
|
|
WCHANGE_STATE(WSTATE_NORMAL);
|
|
}
|
|
|
|
/* TODO: replace this function with a member of the dock struct */
|
|
static int numberOfSelectedIcons(WDock *dock)
|
|
{
|
|
WAppIcon *aicon;
|
|
int i, n;
|
|
|
|
n = 0;
|
|
for (i = 1; i < dock->max_icons; i++) {
|
|
aicon = dock->icon_array[i];
|
|
if (aicon && aicon->icon->selected)
|
|
n++;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
static WMArray *getSelected(WDock *dock)
|
|
{
|
|
WMArray *ret = WMCreateArray(8);
|
|
WAppIcon *btn;
|
|
int i;
|
|
|
|
for (i = 1; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (btn && btn->icon->selected)
|
|
WMAddToArray(ret, btn);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
|
|
{
|
|
Window win = clipIcon->icon->core->window;
|
|
WScreen *scr = clipIcon->icon->core->screen_ptr;
|
|
XPoint p[4];
|
|
int pt = CLIP_BUTTON_SIZE * ICON_SIZE / 64;
|
|
int tp = ICON_SIZE - pt;
|
|
int as = pt - 15; /* 15 = 5+5+5 */
|
|
GC gc = scr->draw_gc; /* maybe use WMColorGC() instead here? */
|
|
WMColor *color;
|
|
|
|
color = scr->clip_title_color[CLIP_NORMAL];
|
|
|
|
XSetForeground(dpy, gc, WMColorPixel(color));
|
|
|
|
if (rpushed) {
|
|
p[0].x = tp + 1;
|
|
p[0].y = 1;
|
|
p[1].x = ICON_SIZE - 2;
|
|
p[1].y = 1;
|
|
p[2].x = ICON_SIZE - 2;
|
|
p[2].y = pt - 1;
|
|
} else if (lpushed) {
|
|
p[0].x = 1;
|
|
p[0].y = tp;
|
|
p[1].x = pt;
|
|
p[1].y = ICON_SIZE - 2;
|
|
p[2].x = 1;
|
|
p[2].y = ICON_SIZE - 2;
|
|
}
|
|
if (lpushed || rpushed) {
|
|
XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
|
|
XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
|
|
XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
|
|
}
|
|
|
|
/* top right arrow */
|
|
p[0].x = p[3].x = ICON_SIZE - 5 - as;
|
|
p[0].y = p[3].y = 5;
|
|
p[1].x = ICON_SIZE - 6;
|
|
p[1].y = 5;
|
|
p[2].x = ICON_SIZE - 6;
|
|
p[2].y = 4 + as;
|
|
if (rpushed) {
|
|
XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
|
|
XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
|
|
} else {
|
|
XFillPolygon(dpy, win, gc, p, 3, Convex, CoordModeOrigin);
|
|
XDrawLines(dpy, win, gc, p, 4, CoordModeOrigin);
|
|
}
|
|
|
|
/* bottom left arrow */
|
|
p[0].x = p[3].x = 5;
|
|
p[0].y = p[3].y = ICON_SIZE - 5 - as;
|
|
p[1].x = 5;
|
|
p[1].y = ICON_SIZE - 6;
|
|
p[2].x = 4 + as;
|
|
p[2].y = ICON_SIZE - 6;
|
|
if (lpushed) {
|
|
XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
|
|
XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
|
|
} else {
|
|
XFillPolygon(dpy, win, gc, p, 3, Convex, CoordModeOrigin);
|
|
XDrawLines(dpy, win, gc, p, 4, CoordModeOrigin);
|
|
}
|
|
}
|
|
|
|
RImage *wClipMakeTile(WScreen *scr, RImage *normalTile)
|
|
{
|
|
RImage *tile = RCloneImage(normalTile);
|
|
RColor black;
|
|
RColor dark;
|
|
RColor light;
|
|
int pt, tp;
|
|
int as;
|
|
|
|
pt = CLIP_BUTTON_SIZE * wPreferences.icon_size / 64;
|
|
tp = wPreferences.icon_size - 1 - pt;
|
|
as = pt - 15;
|
|
|
|
black.alpha = 255;
|
|
black.red = black.green = black.blue = 0;
|
|
|
|
dark.alpha = 0;
|
|
dark.red = dark.green = dark.blue = 60;
|
|
|
|
light.alpha = 0;
|
|
light.red = light.green = light.blue = 80;
|
|
|
|
/* top right */
|
|
ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size - 2, pt - 1, &dark);
|
|
RDrawLine(tile, tp - 1, 0, wPreferences.icon_size - 1, pt + 1, &black);
|
|
ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size - 3, pt, &light);
|
|
|
|
/* arrow bevel */
|
|
ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4, ICON_SIZE - 5, 4, &dark);
|
|
ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5, ICON_SIZE - 5, 6 + as, &dark);
|
|
ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as, &light);
|
|
|
|
/* bottom left */
|
|
ROperateLine(tile, RAddOperation, 2, tp + 2, pt - 2, wPreferences.icon_size - 3, &dark);
|
|
RDrawLine(tile, 0, tp - 1, pt + 1, wPreferences.icon_size - 1, &black);
|
|
ROperateLine(tile, RSubtractOperation, 0, tp - 2, pt + 1, wPreferences.icon_size - 2, &light);
|
|
|
|
/* arrow bevel */
|
|
ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4, ICON_SIZE - 5, &dark);
|
|
ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as, 6 + as, ICON_SIZE - 5, &dark);
|
|
ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5, &light);
|
|
|
|
return tile;
|
|
}
|
|
|
|
static void omnipresentCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *clickedIcon = entry->clientdata;
|
|
WAppIcon *aicon;
|
|
WDock *dock;
|
|
WMArray *selectedIcons;
|
|
WMArrayIterator iter;
|
|
int failed;
|
|
|
|
assert(entry->clientdata != NULL);
|
|
|
|
dock = clickedIcon->dock;
|
|
|
|
selectedIcons = getSelected(dock);
|
|
|
|
if (!WMGetArrayItemCount(selectedIcons))
|
|
WMAddToArray(selectedIcons, clickedIcon);
|
|
|
|
failed = 0;
|
|
WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
|
|
if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
|
|
failed++;
|
|
else if (aicon->icon->selected)
|
|
wIconSelect(aicon->icon);
|
|
}
|
|
WMFreeArray(selectedIcons);
|
|
|
|
if (failed > 1) {
|
|
wMessageDialog(dock->screen_ptr, _("Warning"),
|
|
_("Some icons cannot be made omnipresent. "
|
|
"Please make sure that no other icon is "
|
|
"docked in the same positions on the other "
|
|
"workspaces and the Clip is not full in "
|
|
"some workspace."), _("OK"), NULL, NULL);
|
|
} else if (failed == 1) {
|
|
wMessageDialog(dock->screen_ptr, _("Warning"),
|
|
_("Icon cannot be made omnipresent. "
|
|
"Please make sure that no other icon is "
|
|
"docked in the same position on the other "
|
|
"workspaces and the Clip is not full in "
|
|
"some workspace."), _("OK"), NULL, NULL);
|
|
}
|
|
}
|
|
|
|
static void removeIconsCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
|
|
WDock *dock;
|
|
WAppIcon *aicon;
|
|
WMArray *selectedIcons;
|
|
int keepit;
|
|
WMArrayIterator it;
|
|
|
|
assert(clickedIcon != NULL);
|
|
|
|
dock = clickedIcon->dock;
|
|
|
|
selectedIcons = getSelected(dock);
|
|
|
|
if (WMGetArrayItemCount(selectedIcons)) {
|
|
if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
|
|
_("All selected icons will be removed!"),
|
|
_("OK"), _("Cancel"), NULL) != WAPRDefault) {
|
|
WMFreeArray(selectedIcons);
|
|
return;
|
|
}
|
|
} else {
|
|
if (clickedIcon->xindex == 0 && clickedIcon->yindex == 0) {
|
|
WMFreeArray(selectedIcons);
|
|
return;
|
|
}
|
|
WMAddToArray(selectedIcons, clickedIcon);
|
|
}
|
|
|
|
WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
|
|
keepit = aicon->running && wApplicationOf(aicon->main_window);
|
|
wDockDetach(dock, aicon);
|
|
if (keepit) {
|
|
/* XXX: can: aicon->icon == NULL ? */
|
|
PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos,
|
|
wGetHeadForWindow(aicon->icon->owner));
|
|
XMoveWindow(dpy, aicon->icon->core->window, aicon->x_pos, aicon->y_pos);
|
|
if (!dock->mapped || dock->collapsed)
|
|
XMapWindow(dpy, aicon->icon->core->window);
|
|
}
|
|
}
|
|
WMFreeArray(selectedIcons);
|
|
|
|
if (wPreferences.auto_arrange_icons)
|
|
wArrangeIcons(dock->screen_ptr, True);
|
|
}
|
|
|
|
static void keepIconsCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
|
|
WDock *dock;
|
|
WAppIcon *aicon;
|
|
WMArray *selectedIcons;
|
|
WMArrayIterator it;
|
|
|
|
assert(clickedIcon != NULL);
|
|
dock = clickedIcon->dock;
|
|
|
|
selectedIcons = getSelected(dock);
|
|
|
|
if (!WMGetArrayItemCount(selectedIcons)
|
|
&& clickedIcon != dock->screen_ptr->clip_icon) {
|
|
char *command = NULL;
|
|
|
|
if (!clickedIcon->command && !clickedIcon->editing) {
|
|
clickedIcon->editing = 1;
|
|
if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
|
|
_("Type the command used to launch the application"), &command)) {
|
|
if (command && (command[0] == 0 || (command[0] == '-' && command[1] == 0))) {
|
|
wfree(command);
|
|
command = NULL;
|
|
}
|
|
clickedIcon->command = command;
|
|
clickedIcon->editing = 0;
|
|
} else {
|
|
clickedIcon->editing = 0;
|
|
if (command)
|
|
wfree(command);
|
|
WMFreeArray(selectedIcons);
|
|
return;
|
|
}
|
|
}
|
|
|
|
WMAddToArray(selectedIcons, clickedIcon);
|
|
}
|
|
|
|
WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
|
|
if (aicon->icon->selected)
|
|
wIconSelect(aicon->icon);
|
|
|
|
if (aicon && aicon->attracted && aicon->command) {
|
|
aicon->attracted = 0;
|
|
if (aicon->icon->shadowed) {
|
|
aicon->icon->shadowed = 0;
|
|
|
|
/* Update the icon images */
|
|
wIconUpdate(aicon->icon, NULL);
|
|
|
|
/* Paint it */
|
|
wAppIconPaint(aicon);
|
|
}
|
|
}
|
|
save_appicon(aicon, True);
|
|
}
|
|
WMFreeArray(selectedIcons);
|
|
}
|
|
|
|
static void toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WDock *dock = (WDock *) entry->clientdata;
|
|
|
|
assert(entry->clientdata != NULL);
|
|
|
|
dock->attract_icons = !dock->attract_icons;
|
|
|
|
entry->flags.indicator_on = dock->attract_icons;
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static void selectCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *icon = (WAppIcon *) entry->clientdata;
|
|
|
|
assert(icon != NULL);
|
|
|
|
wIconSelect(icon->icon);
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static void colectIconsCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
|
|
WDock *clip;
|
|
WAppIcon *aicon;
|
|
int x, y, x_pos, y_pos;
|
|
Bool update_icon = False;
|
|
|
|
assert(entry->clientdata != NULL);
|
|
clip = clickedIcon->dock;
|
|
|
|
aicon = clip->screen_ptr->app_icon_list;
|
|
|
|
while (aicon) {
|
|
if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
|
|
x_pos = clip->x_pos + x * ICON_SIZE;
|
|
y_pos = clip->y_pos + y * ICON_SIZE;
|
|
if (aicon->x_pos != x_pos || aicon->y_pos != y_pos)
|
|
move_window(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, x_pos, y_pos);
|
|
|
|
aicon->attracted = 1;
|
|
if (!aicon->icon->shadowed) {
|
|
aicon->icon->shadowed = 1;
|
|
update_icon = True;
|
|
}
|
|
wDockAttachIcon(clip, aicon, x, y, update_icon);
|
|
if (clip->collapsed || !clip->mapped)
|
|
XUnmapWindow(dpy, aicon->icon->core->window);
|
|
}
|
|
aicon = aicon->next;
|
|
}
|
|
}
|
|
|
|
static void selectIconsCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata;
|
|
WDock *dock;
|
|
WMArray *selectedIcons;
|
|
WMArrayIterator iter;
|
|
WAppIcon *btn;
|
|
int i;
|
|
|
|
assert(clickedIcon != NULL);
|
|
dock = clickedIcon->dock;
|
|
|
|
selectedIcons = getSelected(dock);
|
|
|
|
if (!WMGetArrayItemCount(selectedIcons)) {
|
|
for (i = 1; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (btn && !btn->icon->selected)
|
|
wIconSelect(btn->icon);
|
|
}
|
|
} else {
|
|
WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
|
|
wIconSelect(btn->icon);
|
|
}
|
|
}
|
|
WMFreeArray(selectedIcons);
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static void toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
assert(entry->clientdata != NULL);
|
|
|
|
toggleCollapsed(entry->clientdata);
|
|
|
|
entry->flags.indicator_on = ((WDock *) entry->clientdata)->collapsed;
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static void toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WDock *dock;
|
|
assert(entry->clientdata != NULL);
|
|
|
|
dock = (WDock *) entry->clientdata;
|
|
|
|
dock->auto_collapse = !dock->auto_collapse;
|
|
|
|
entry->flags.indicator_on = ((WDock *) entry->clientdata)->auto_collapse;
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static void toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WDock *dock;
|
|
assert(entry->clientdata != NULL);
|
|
|
|
dock = (WDock *) entry->clientdata;
|
|
|
|
dock->auto_raise_lower = !dock->auto_raise_lower;
|
|
|
|
entry->flags.indicator_on = ((WDock *) entry->clientdata)->auto_raise_lower;
|
|
|
|
wMenuPaint(menu);
|
|
}
|
|
|
|
static void launchCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *btn = (WAppIcon *) entry->clientdata;
|
|
|
|
launchDockedApplication(btn, False);
|
|
}
|
|
|
|
static void settingsCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *btn = (WAppIcon *) entry->clientdata;
|
|
|
|
if (btn->editing)
|
|
return;
|
|
ShowDockAppSettingsPanel(btn);
|
|
}
|
|
|
|
static void hideCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WApplication *wapp;
|
|
WAppIcon *btn = (WAppIcon *) entry->clientdata;
|
|
|
|
wapp = wApplicationOf(btn->icon->owner->main_window);
|
|
|
|
if (wapp->flags.hidden) {
|
|
wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
|
|
wUnhideApplication(wapp, False, False);
|
|
} else {
|
|
wHideApplication(wapp);
|
|
}
|
|
}
|
|
|
|
static void unhideHereCallback(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WApplication *wapp;
|
|
WAppIcon *btn = (WAppIcon *) entry->clientdata;
|
|
|
|
wapp = wApplicationOf(btn->icon->owner->main_window);
|
|
|
|
wUnhideApplication(wapp, False, True);
|
|
}
|
|
|
|
static WAppIcon *mainIconCreate(WScreen *scr, int type)
|
|
{
|
|
WAppIcon *btn;
|
|
int x_pos;
|
|
|
|
if (type == WM_CLIP) {
|
|
if (scr->clip_icon)
|
|
return scr->clip_icon;
|
|
btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
|
|
btn->icon->core->descriptor.handle_expose = clipIconExpose;
|
|
btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
|
|
btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
|
|
x_pos = 0;
|
|
} else {
|
|
btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
|
|
x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
|
|
}
|
|
|
|
btn->xindex = 0;
|
|
btn->yindex = 0;
|
|
|
|
btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
|
|
btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
|
|
btn->icon->core->descriptor.parent = btn;
|
|
XMapWindow(dpy, btn->icon->core->window);
|
|
btn->x_pos = x_pos;
|
|
btn->y_pos = 0;
|
|
btn->docked = 1;
|
|
if (type == WM_CLIP)
|
|
scr->clip_icon = btn;
|
|
|
|
return btn;
|
|
}
|
|
|
|
static void switchWSCommand(WMenu *menu, WMenuEntry *entry)
|
|
{
|
|
WAppIcon *btn, *icon = (WAppIcon *) entry->clientdata;
|
|
WScreen *scr = icon->icon->core->screen_ptr;
|
|
WDock *src, *dest;
|
|
WMArray *selectedIcons;
|
|
int x, y;
|
|
|
|
if (entry->order == scr->current_workspace)
|
|
return;
|
|
src = icon->dock;
|
|
dest = scr->workspaces[entry->order]->clip;
|
|
|
|
selectedIcons = getSelected(src);
|
|
|
|
if (WMGetArrayItemCount(selectedIcons)) {
|
|
WMArrayIterator iter;
|
|
|
|
WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
|
|
if (wDockFindFreeSlot(dest, &x, &y)) {
|
|
moveIconBetweenDocks(src, dest, btn, x, y);
|
|
XUnmapWindow(dpy, btn->icon->core->window);
|
|
}
|
|
}
|
|
} else if (icon != scr->clip_icon) {
|
|
if (wDockFindFreeSlot(dest, &x, &y)) {
|
|
moveIconBetweenDocks(src, dest, icon, x, y);
|
|
XUnmapWindow(dpy, icon->icon->core->window);
|
|
}
|
|
}
|
|
WMFreeArray(selectedIcons);
|
|
}
|
|
|
|
static void launchDockedApplication(WAppIcon *btn, Bool withSelection)
|
|
{
|
|
WScreen *scr = btn->icon->core->screen_ptr;
|
|
|
|
if (!btn->launching &&
|
|
((!withSelection && btn->command != NULL) || (withSelection && btn->paste_command != NULL))) {
|
|
if (!btn->forced_dock) {
|
|
btn->relaunching = btn->running;
|
|
btn->running = 1;
|
|
}
|
|
if (btn->wm_instance || btn->wm_class) {
|
|
WWindowAttributes attr;
|
|
memset(&attr, 0, sizeof(WWindowAttributes));
|
|
wDefaultFillAttributes(btn->wm_instance, btn->wm_class, &attr, NULL, True);
|
|
|
|
if (!attr.no_appicon && !btn->buggy_app)
|
|
btn->launching = 1;
|
|
else
|
|
btn->running = 0;
|
|
}
|
|
btn->drop_launch = 0;
|
|
btn->paste_launch = withSelection;
|
|
scr->last_dock = btn->dock;
|
|
btn->pid = execCommand(btn, (withSelection ? btn->paste_command : btn->command), NULL);
|
|
if (btn->pid > 0) {
|
|
if (btn->buggy_app) {
|
|
/* give feedback that the app was launched */
|
|
btn->launching = 1;
|
|
dockIconPaint(btn);
|
|
btn->launching = 0;
|
|
WMAddTimerHandler(200, (WMCallback *) dockIconPaint, btn);
|
|
} else {
|
|
dockIconPaint(btn);
|
|
}
|
|
} else {
|
|
wwarning(_("could not launch application %s"), btn->command);
|
|
btn->launching = 0;
|
|
if (!btn->relaunching)
|
|
btn->running = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
|
|
{
|
|
WScreen *scr = menu->frame->screen_ptr;
|
|
char title[MAX_WORKSPACENAME_WIDTH + 1];
|
|
int i;
|
|
|
|
if (!menu || !icon)
|
|
return;
|
|
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
if (i < menu->entry_no) {
|
|
if (strcmp(menu->entries[i]->text, scr->workspaces[i]->name) != 0) {
|
|
wfree(menu->entries[i]->text);
|
|
strcpy(title, scr->workspaces[i]->name);
|
|
menu->entries[i]->text = wstrdup(title);
|
|
menu->flags.realized = 0;
|
|
}
|
|
menu->entries[i]->clientdata = (void *)icon;
|
|
} else {
|
|
strcpy(title, scr->workspaces[i]->name);
|
|
|
|
wMenuAddCallback(menu, title, switchWSCommand, (void *)icon);
|
|
|
|
menu->flags.realized = 0;
|
|
}
|
|
|
|
if (i == scr->current_workspace)
|
|
wMenuSetEnabled(menu, i, False);
|
|
else
|
|
wMenuSetEnabled(menu, i, True);
|
|
}
|
|
|
|
if (!menu->flags.realized)
|
|
wMenuRealize(menu);
|
|
}
|
|
|
|
static WMenu *makeWorkspaceMenu(WScreen *scr)
|
|
{
|
|
WMenu *menu;
|
|
|
|
menu = wMenuCreate(scr, NULL, False);
|
|
if (!menu)
|
|
wwarning(_("could not create workspace submenu for Clip menu"));
|
|
|
|
wMenuAddCallback(menu, "", switchWSCommand, (void *)scr->clip_icon);
|
|
|
|
menu->flags.realized = 0;
|
|
wMenuRealize(menu);
|
|
|
|
return menu;
|
|
}
|
|
|
|
static void updateClipOptionsMenu(WMenu *menu, WDock *dock)
|
|
{
|
|
WMenuEntry *entry;
|
|
int index = 0;
|
|
|
|
if (!menu || !dock)
|
|
return;
|
|
|
|
/* keep on top */
|
|
entry = menu->entries[index];
|
|
entry->flags.indicator_on = !dock->lowered;
|
|
entry->clientdata = dock;
|
|
|
|
/* collapsed */
|
|
entry = menu->entries[++index];
|
|
entry->flags.indicator_on = dock->collapsed;
|
|
entry->clientdata = dock;
|
|
|
|
/* auto-collapse */
|
|
entry = menu->entries[++index];
|
|
entry->flags.indicator_on = dock->auto_collapse;
|
|
entry->clientdata = dock;
|
|
|
|
/* auto-raise/lower */
|
|
entry = menu->entries[++index];
|
|
entry->flags.indicator_on = dock->auto_raise_lower;
|
|
entry->clientdata = dock;
|
|
wMenuSetEnabled(menu, index, dock->lowered);
|
|
|
|
/* attract icons */
|
|
entry = menu->entries[++index];
|
|
entry->flags.indicator_on = dock->attract_icons;
|
|
entry->clientdata = dock;
|
|
|
|
menu->flags.realized = 0;
|
|
wMenuRealize(menu);
|
|
}
|
|
|
|
static WMenu *makeClipOptionsMenu(WScreen *scr)
|
|
{
|
|
WMenu *menu;
|
|
WMenuEntry *entry;
|
|
|
|
menu = wMenuCreate(scr, NULL, False);
|
|
if (!menu) {
|
|
wwarning(_("could not create options submenu for Clip menu"));
|
|
return NULL;
|
|
}
|
|
|
|
entry = wMenuAddCallback(menu, _("Keep on Top"), toggleLoweredCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
|
|
entry = wMenuAddCallback(menu, _("Collapsed"), toggleCollapsedCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
|
|
entry = wMenuAddCallback(menu, _("Autocollapse"), toggleAutoCollapseCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
|
|
entry = wMenuAddCallback(menu, _("Autoraise"), toggleAutoRaiseLowerCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
|
|
entry = wMenuAddCallback(menu, _("Autoattract Icons"), toggleAutoAttractCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
|
|
menu->flags.realized = 0;
|
|
wMenuRealize(menu);
|
|
|
|
return menu;
|
|
}
|
|
|
|
static WMenu *dockMenuCreate(WScreen *scr, int type)
|
|
{
|
|
WMenu *menu;
|
|
WMenuEntry *entry;
|
|
|
|
if (type == WM_CLIP && scr->clip_menu)
|
|
return scr->clip_menu;
|
|
|
|
menu = wMenuCreate(scr, NULL, False);
|
|
if (type != WM_CLIP) {
|
|
entry = wMenuAddCallback(menu, _("Keep on Top"), toggleLoweredCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
} else {
|
|
entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
|
|
scr->clip_options = makeClipOptionsMenu(scr);
|
|
if (scr->clip_options)
|
|
wMenuEntrySetCascade(menu, entry, scr->clip_options);
|
|
|
|
entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback, NULL);
|
|
wfree(entry->text);
|
|
entry->text = _("Rename Workspace");
|
|
|
|
entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = 1;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
|
|
entry = wMenuAddCallback(menu, _("Select All Icons"), selectIconsCallback, NULL);
|
|
wfree(entry->text);
|
|
entry->text = _("Select All Icons");
|
|
|
|
entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
|
|
wfree(entry->text);
|
|
entry->text = _("Keep Icon");
|
|
|
|
entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
|
|
wfree(entry->text);
|
|
entry->text = _("Move Icon To");
|
|
scr->clip_submenu = makeWorkspaceMenu(scr);
|
|
if (scr->clip_submenu)
|
|
wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
|
|
|
|
entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback, NULL);
|
|
wfree(entry->text);
|
|
entry->text = _("Remove Icon");
|
|
|
|
wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
|
|
}
|
|
|
|
wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
|
|
|
|
wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
|
|
|
|
entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
|
|
wfree(entry->text);
|
|
entry->text = _("Hide");
|
|
|
|
wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
|
|
|
|
wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
|
|
|
|
if (type == WM_CLIP)
|
|
scr->clip_menu = menu;
|
|
|
|
return menu;
|
|
}
|
|
|
|
WDock *wDockCreate(WScreen *scr, int type)
|
|
{
|
|
WDock *dock;
|
|
WAppIcon *btn;
|
|
|
|
make_keys();
|
|
|
|
dock = wmalloc(sizeof(WDock));
|
|
|
|
dock->max_icons = DOCK_MAX_ICONS;
|
|
|
|
dock->icon_array = wmalloc(sizeof(WAppIcon *) * dock->max_icons);
|
|
|
|
btn = mainIconCreate(scr, type);
|
|
|
|
btn->dock = dock;
|
|
|
|
dock->x_pos = btn->x_pos;
|
|
dock->y_pos = btn->y_pos;
|
|
dock->screen_ptr = scr;
|
|
dock->type = type;
|
|
dock->icon_count = 1;
|
|
dock->on_right_side = 1;
|
|
dock->collapsed = 0;
|
|
dock->auto_collapse = 0;
|
|
dock->auto_collapse_magic = NULL;
|
|
dock->auto_raise_lower = 0;
|
|
dock->auto_lower_magic = NULL;
|
|
dock->auto_raise_magic = NULL;
|
|
dock->attract_icons = 0;
|
|
dock->lowered = 1;
|
|
dock->icon_array[0] = btn;
|
|
wRaiseFrame(btn->icon->core);
|
|
XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
|
|
|
|
/* create dock menu */
|
|
dock->menu = dockMenuCreate(scr, type);
|
|
|
|
return dock;
|
|
}
|
|
|
|
void wDockDestroy(WDock *dock)
|
|
{
|
|
int i;
|
|
WAppIcon *aicon;
|
|
|
|
for (i = (dock->type == WM_CLIP) ? 1 : 0; i < dock->max_icons; i++) {
|
|
aicon = dock->icon_array[i];
|
|
if (aicon) {
|
|
int keepit = aicon->running && wApplicationOf(aicon->main_window);
|
|
wDockDetach(dock, aicon);
|
|
if (keepit) {
|
|
/* XXX: can: aicon->icon == NULL ? */
|
|
PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos,
|
|
wGetHeadForWindow(aicon->icon->owner));
|
|
XMoveWindow(dpy, aicon->icon->core->window, aicon->x_pos, aicon->y_pos);
|
|
if (!dock->mapped || dock->collapsed)
|
|
XMapWindow(dpy, aicon->icon->core->window);
|
|
}
|
|
}
|
|
}
|
|
if (wPreferences.auto_arrange_icons)
|
|
wArrangeIcons(dock->screen_ptr, True);
|
|
wfree(dock->icon_array);
|
|
if (dock->menu && dock->type != WM_CLIP)
|
|
wMenuDestroy(dock->menu, True);
|
|
if (dock->screen_ptr->last_dock == dock)
|
|
dock->screen_ptr->last_dock = NULL;
|
|
wfree(dock);
|
|
}
|
|
|
|
void wClipIconPaint(WAppIcon *aicon)
|
|
{
|
|
WScreen *scr = aicon->icon->core->screen_ptr;
|
|
WWorkspace *workspace = scr->workspaces[scr->current_workspace];
|
|
WMColor *color;
|
|
Window win = aicon->icon->core->window;
|
|
int length, nlength;
|
|
char *ws_name, ws_number[10];
|
|
int ty, tx;
|
|
|
|
wIconPaint(aicon->icon);
|
|
|
|
length = strlen(workspace->name);
|
|
ws_name = wmalloc(length + 1);
|
|
snprintf(ws_name, length + 1, "%s", workspace->name);
|
|
snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
|
|
nlength = strlen(ws_number);
|
|
|
|
if (!workspace->clip->collapsed)
|
|
color = scr->clip_title_color[CLIP_NORMAL];
|
|
else
|
|
color = scr->clip_title_color[CLIP_COLLAPSED];
|
|
|
|
ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
|
|
|
|
tx = CLIP_BUTTON_SIZE * ICON_SIZE / 64;
|
|
|
|
if(wPreferences.show_clip_title)
|
|
WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx, ty, ws_name, length);
|
|
|
|
tx = (ICON_SIZE / 2 - WMWidthOfString(scr->clip_title_font, ws_number, nlength)) / 2;
|
|
|
|
WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx, 2, ws_number, nlength);
|
|
|
|
wfree(ws_name);
|
|
|
|
if (aicon->launching)
|
|
XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
|
|
0, 0, wPreferences.icon_size, wPreferences.icon_size);
|
|
|
|
paintClipButtons(aicon, aicon->dock->lclip_button_pushed, aicon->dock->rclip_button_pushed);
|
|
}
|
|
|
|
static void clipIconExpose(WObjDescriptor *desc, XEvent *event)
|
|
{
|
|
wClipIconPaint(desc->parent);
|
|
}
|
|
|
|
static void dockIconPaint(WAppIcon *btn)
|
|
{
|
|
if (btn == btn->icon->core->screen_ptr->clip_icon) {
|
|
wClipIconPaint(btn);
|
|
} else {
|
|
wAppIconPaint(btn);
|
|
save_appicon(btn, True);
|
|
}
|
|
}
|
|
|
|
static WMPropList *make_icon_state(WAppIcon *btn)
|
|
{
|
|
WMPropList *node = NULL;
|
|
WMPropList *command, *autolaunch, *lock, *name, *forced;
|
|
WMPropList *position, *buggy, *omnipresent;
|
|
char *tmp;
|
|
char buffer[64];
|
|
|
|
if (btn) {
|
|
if (!btn->command)
|
|
command = WMCreatePLString("-");
|
|
else
|
|
command = WMCreatePLString(btn->command);
|
|
|
|
autolaunch = btn->auto_launch ? dYes : dNo;
|
|
|
|
lock = btn->lock ? dYes : dNo;
|
|
|
|
tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
|
|
|
|
name = WMCreatePLString(tmp);
|
|
|
|
wfree(tmp);
|
|
|
|
forced = btn->forced_dock ? dYes : dNo;
|
|
|
|
buggy = btn->buggy_app ? dYes : dNo;
|
|
|
|
if (btn == btn->icon->core->screen_ptr->clip_icon)
|
|
snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
|
|
else
|
|
snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
|
|
position = WMCreatePLString(buffer);
|
|
|
|
node = WMCreatePLDictionary(dCommand, command,
|
|
dName, name,
|
|
dAutoLaunch, autolaunch,
|
|
dLock, lock,
|
|
dForced, forced, dBuggyApplication, buggy, dPosition, position, NULL);
|
|
WMReleasePropList(command);
|
|
WMReleasePropList(name);
|
|
WMReleasePropList(position);
|
|
|
|
omnipresent = btn->omnipresent ? dYes : dNo;
|
|
if (btn->dock != btn->icon->core->screen_ptr->dock && (btn->xindex != 0 || btn->yindex != 0))
|
|
WMPutInPLDictionary(node, dOmnipresent, omnipresent);
|
|
|
|
#ifdef XDND /* was OFFIX */
|
|
if (btn->dnd_command) {
|
|
command = WMCreatePLString(btn->dnd_command);
|
|
WMPutInPLDictionary(node, dDropCommand, command);
|
|
WMReleasePropList(command);
|
|
}
|
|
#endif /* XDND */
|
|
|
|
if (btn->paste_command) {
|
|
command = WMCreatePLString(btn->paste_command);
|
|
WMPutInPLDictionary(node, dPasteCommand, command);
|
|
WMReleasePropList(command);
|
|
}
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
static WMPropList *dockSaveState(WDock *dock)
|
|
{
|
|
int i;
|
|
WMPropList *icon_info;
|
|
WMPropList *list = NULL, *dock_state = NULL;
|
|
WMPropList *value, *key;
|
|
char buffer[256];
|
|
|
|
list = WMCreatePLArray(NULL);
|
|
|
|
for (i = (dock->type == WM_DOCK ? 0 : 1); i < dock->max_icons; i++) {
|
|
WAppIcon *btn = dock->icon_array[i];
|
|
|
|
if (!btn || btn->attracted)
|
|
continue;
|
|
|
|
if ((icon_info = make_icon_state(dock->icon_array[i]))) {
|
|
WMAddToPLArray(list, icon_info);
|
|
WMReleasePropList(icon_info);
|
|
}
|
|
}
|
|
|
|
dock_state = WMCreatePLDictionary(dApplications, list, NULL);
|
|
|
|
if (dock->type == WM_DOCK) {
|
|
snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
|
|
key = WMCreatePLString(buffer);
|
|
WMPutInPLDictionary(dock_state, key, list);
|
|
WMReleasePropList(key);
|
|
|
|
snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0), dock->y_pos);
|
|
value = WMCreatePLString(buffer);
|
|
WMPutInPLDictionary(dock_state, dPosition, value);
|
|
WMReleasePropList(value);
|
|
}
|
|
WMReleasePropList(list);
|
|
|
|
value = (dock->lowered ? dYes : dNo);
|
|
WMPutInPLDictionary(dock_state, dLowered, value);
|
|
|
|
if (dock->type == WM_CLIP) {
|
|
value = (dock->collapsed ? dYes : dNo);
|
|
WMPutInPLDictionary(dock_state, dCollapsed, value);
|
|
|
|
value = (dock->auto_collapse ? dYes : dNo);
|
|
WMPutInPLDictionary(dock_state, dAutoCollapse, value);
|
|
|
|
value = (dock->auto_raise_lower ? dYes : dNo);
|
|
WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
|
|
|
|
value = (dock->attract_icons ? dYes : dNo);
|
|
WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
|
|
}
|
|
|
|
return dock_state;
|
|
}
|
|
|
|
void wDockSaveState(WScreen *scr, WMPropList *old_state)
|
|
{
|
|
WMPropList *dock_state;
|
|
WMPropList *keys;
|
|
|
|
dock_state = dockSaveState(scr->dock);
|
|
|
|
/*
|
|
* Copy saved states of docks with different sizes.
|
|
*/
|
|
if (old_state) {
|
|
int i;
|
|
WMPropList *tmp;
|
|
|
|
keys = WMGetPLDictionaryKeys(old_state);
|
|
for (i = 0; i < WMGetPropListItemCount(keys); i++) {
|
|
tmp = WMGetFromPLArray(keys, i);
|
|
|
|
if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
|
|
&& !WMGetFromPLDictionary(dock_state, tmp)) {
|
|
|
|
WMPutInPLDictionary(dock_state, tmp, WMGetFromPLDictionary(old_state, tmp));
|
|
}
|
|
}
|
|
WMReleasePropList(keys);
|
|
}
|
|
|
|
WMPutInPLDictionary(scr->session_state, dDock, dock_state);
|
|
|
|
WMReleasePropList(dock_state);
|
|
}
|
|
|
|
void wClipSaveState(WScreen *scr)
|
|
{
|
|
WMPropList *clip_state;
|
|
|
|
clip_state = make_icon_state(scr->clip_icon);
|
|
|
|
WMPutInPLDictionary(scr->session_state, dClip, clip_state);
|
|
|
|
WMReleasePropList(clip_state);
|
|
}
|
|
|
|
WMPropList *wClipSaveWorkspaceState(WScreen *scr, int workspace)
|
|
{
|
|
return dockSaveState(scr->workspaces[workspace]->clip);
|
|
}
|
|
|
|
static Bool getBooleanDockValue(WMPropList *value, WMPropList *key)
|
|
{
|
|
if (value) {
|
|
if (WMIsPLString(value)) {
|
|
if (strcasecmp(WMGetFromPLString(value), "YES") == 0)
|
|
return True;
|
|
} else {
|
|
wwarning(_("bad value in docked icon state info %s"), WMGetFromPLString(key));
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static WAppIcon *restore_icon_state(WScreen *scr, WMPropList *info, int type, int index)
|
|
{
|
|
WAppIcon *aicon;
|
|
WMPropList *cmd, *value;
|
|
char *wclass, *winstance, *command;
|
|
|
|
cmd = WMGetFromPLDictionary(info, dCommand);
|
|
if (!cmd || !WMIsPLString(cmd))
|
|
return NULL;
|
|
|
|
/* parse window name */
|
|
value = WMGetFromPLDictionary(info, dName);
|
|
if (!value)
|
|
return NULL;
|
|
|
|
ParseWindowName(value, &winstance, &wclass, "dock");
|
|
|
|
if (!winstance && !wclass)
|
|
return NULL;
|
|
|
|
/* get commands */
|
|
if (cmd)
|
|
command = wstrdup(WMGetFromPLString(cmd));
|
|
else
|
|
command = NULL;
|
|
|
|
if (!command || strcmp(command, "-") == 0) {
|
|
if (command)
|
|
wfree(command);
|
|
if (wclass)
|
|
wfree(wclass);
|
|
if (winstance)
|
|
wfree(winstance);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
aicon = wAppIconCreateForDock(scr, command, winstance, wclass, TILE_NORMAL);
|
|
if (wclass)
|
|
wfree(wclass);
|
|
if (winstance)
|
|
wfree(winstance);
|
|
if (command)
|
|
wfree(command);
|
|
|
|
aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
|
|
if (type == WM_CLIP) {
|
|
aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
|
|
aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
|
|
}
|
|
aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
|
|
aicon->icon->core->descriptor.parent = aicon;
|
|
|
|
#ifdef XDND /* was OFFIX */
|
|
cmd = WMGetFromPLDictionary(info, dDropCommand);
|
|
if (cmd)
|
|
aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
|
|
#endif
|
|
|
|
cmd = WMGetFromPLDictionary(info, dPasteCommand);
|
|
if (cmd)
|
|
aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
|
|
|
|
/* check auto launch */
|
|
value = WMGetFromPLDictionary(info, dAutoLaunch);
|
|
|
|
aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
|
|
|
|
/* check lock */
|
|
value = WMGetFromPLDictionary(info, dLock);
|
|
|
|
aicon->lock = getBooleanDockValue(value, dLock);
|
|
|
|
/* check if it wasn't normally docked */
|
|
value = WMGetFromPLDictionary(info, dForced);
|
|
|
|
aicon->forced_dock = getBooleanDockValue(value, dForced);
|
|
|
|
/* check if we can rely on the stuff in the app */
|
|
value = WMGetFromPLDictionary(info, dBuggyApplication);
|
|
|
|
aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
|
|
|
|
/* get position in the dock */
|
|
value = WMGetFromPLDictionary(info, dPosition);
|
|
if (value && WMIsPLString(value)) {
|
|
if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex, &aicon->yindex) != 2)
|
|
wwarning(_("bad value in docked icon state info %s"), WMGetFromPLString(dPosition));
|
|
|
|
/* check position sanity */
|
|
/* incomplete section! */
|
|
if (type == WM_DOCK) {
|
|
aicon->xindex = 0;
|
|
if (aicon->yindex < 0)
|
|
wwarning(_("bad value in docked icon position %i,%i"),
|
|
aicon->xindex, aicon->yindex);
|
|
}
|
|
} else {
|
|
aicon->yindex = index;
|
|
aicon->xindex = 0;
|
|
}
|
|
|
|
/* check if icon is omnipresent */
|
|
value = WMGetFromPLDictionary(info, dOmnipresent);
|
|
|
|
aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
|
|
|
|
aicon->running = 0;
|
|
aicon->docked = 1;
|
|
|
|
return aicon;
|
|
}
|
|
|
|
#define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
|
|
|
|
WAppIcon *wClipRestoreState(WScreen *scr, WMPropList *clip_state)
|
|
{
|
|
WAppIcon *icon;
|
|
WMPropList *value;
|
|
|
|
icon = mainIconCreate(scr, WM_CLIP);
|
|
|
|
if (!clip_state)
|
|
return icon;
|
|
|
|
WMRetainPropList(clip_state);
|
|
|
|
/* restore position */
|
|
|
|
value = WMGetFromPLDictionary(clip_state, dPosition);
|
|
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("Position");
|
|
} else {
|
|
WMRect rect;
|
|
int flags;
|
|
|
|
if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos, &icon->y_pos) != 2)
|
|
COMPLAIN("Position");
|
|
|
|
/* check position sanity */
|
|
rect.pos.x = icon->x_pos;
|
|
rect.pos.y = icon->y_pos;
|
|
rect.size.width = rect.size.height = ICON_SIZE;
|
|
|
|
wGetRectPlacementInfo(scr, rect, &flags);
|
|
if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
|
|
wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos, ICON_SIZE, ICON_SIZE);
|
|
}
|
|
}
|
|
#ifdef XDND /* was OFFIX */
|
|
value = WMGetFromPLDictionary(clip_state, dDropCommand);
|
|
if (value && WMIsPLString(value))
|
|
icon->dnd_command = wstrdup(WMGetFromPLString(value));
|
|
#endif
|
|
|
|
value = WMGetFromPLDictionary(clip_state, dPasteCommand);
|
|
if (value && WMIsPLString(value))
|
|
icon->paste_command = wstrdup(WMGetFromPLString(value));
|
|
|
|
WMReleasePropList(clip_state);
|
|
|
|
return icon;
|
|
}
|
|
|
|
WDock *wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
|
|
{
|
|
WDock *dock;
|
|
WMPropList *apps;
|
|
WMPropList *value;
|
|
WAppIcon *aicon, *old_top;
|
|
int count, i;
|
|
|
|
dock = wDockCreate(scr, type);
|
|
|
|
if (!dock_state)
|
|
return dock;
|
|
|
|
WMRetainPropList(dock_state);
|
|
|
|
/* restore position */
|
|
value = WMGetFromPLDictionary(dock_state, dPosition);
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("Position");
|
|
} else {
|
|
WMRect rect;
|
|
int flags;
|
|
|
|
if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos, &dock->y_pos) != 2)
|
|
COMPLAIN("Position");
|
|
|
|
/* check position sanity */
|
|
rect.pos.x = dock->x_pos;
|
|
rect.pos.y = dock->y_pos;
|
|
rect.size.width = rect.size.height = ICON_SIZE;
|
|
|
|
wGetRectPlacementInfo(scr, rect, &flags);
|
|
if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
|
|
int x = dock->x_pos;
|
|
wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
|
|
}
|
|
|
|
/* Is this needed any more? */
|
|
if (type == WM_CLIP) {
|
|
if (dock->x_pos < 0) {
|
|
dock->x_pos = 0;
|
|
} else if (dock->x_pos > scr->scr_width - ICON_SIZE) {
|
|
dock->x_pos = scr->scr_width - ICON_SIZE;
|
|
}
|
|
} else {
|
|
if (dock->x_pos >= 0) {
|
|
dock->x_pos = DOCK_EXTRA_SPACE;
|
|
dock->on_right_side = 0;
|
|
} else {
|
|
dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
|
|
dock->on_right_side = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* restore lowered/raised state */
|
|
dock->lowered = 0;
|
|
|
|
value = WMGetFromPLDictionary(dock_state, dLowered);
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("Lowered");
|
|
} else {
|
|
if (strcasecmp(WMGetFromPLString(value), "YES") == 0)
|
|
dock->lowered = 1;
|
|
}
|
|
}
|
|
|
|
/* restore collapsed state */
|
|
dock->collapsed = 0;
|
|
|
|
value = WMGetFromPLDictionary(dock_state, dCollapsed);
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("Collapsed");
|
|
} else {
|
|
if (strcasecmp(WMGetFromPLString(value), "YES") == 0)
|
|
dock->collapsed = 1;
|
|
}
|
|
}
|
|
|
|
/* restore auto-collapsed state */
|
|
value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("AutoCollapse");
|
|
} else {
|
|
if (strcasecmp(WMGetFromPLString(value), "YES") == 0) {
|
|
dock->auto_collapse = 1;
|
|
dock->collapsed = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* restore auto-raise/lower state */
|
|
value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("AutoRaiseLower");
|
|
} else {
|
|
if (strcasecmp(WMGetFromPLString(value), "YES") == 0)
|
|
dock->auto_raise_lower = 1;
|
|
}
|
|
}
|
|
|
|
/* restore attract icons state */
|
|
dock->attract_icons = 0;
|
|
|
|
value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
|
|
if (value) {
|
|
if (!WMIsPLString(value)) {
|
|
COMPLAIN("AutoAttractIcons");
|
|
} else {
|
|
if (strcasecmp(WMGetFromPLString(value), "YES") == 0)
|
|
dock->attract_icons = 1;
|
|
}
|
|
}
|
|
|
|
/* application list */
|
|
|
|
{
|
|
WMPropList *tmp;
|
|
char buffer[64];
|
|
|
|
/*
|
|
* When saving, it saves the dock state in
|
|
* Applications and Applicationsnnn
|
|
*
|
|
* When loading, it will first try Applicationsnnn.
|
|
* If it does not exist, use Applications as default.
|
|
*/
|
|
|
|
snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
|
|
|
|
tmp = WMCreatePLString(buffer);
|
|
apps = WMGetFromPLDictionary(dock_state, tmp);
|
|
WMReleasePropList(tmp);
|
|
|
|
if (!apps)
|
|
apps = WMGetFromPLDictionary(dock_state, dApplications);
|
|
}
|
|
|
|
if (!apps)
|
|
goto finish;
|
|
|
|
count = WMGetPropListItemCount(apps);
|
|
if (count == 0)
|
|
goto finish;
|
|
|
|
old_top = dock->icon_array[0];
|
|
|
|
/* dock->icon_count is set to 1 when dock is created.
|
|
* Since Clip is already restored, we want to keep it so for clip,
|
|
* but for dock we may change the default top tile, so we set it to 0.
|
|
*/
|
|
if (type == WM_DOCK)
|
|
dock->icon_count = 0;
|
|
|
|
for (i = 0; i < count; i++) {
|
|
if (dock->icon_count >= dock->max_icons) {
|
|
wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
|
|
break;
|
|
}
|
|
|
|
value = WMGetFromPLArray(apps, i);
|
|
aicon = restore_icon_state(scr, value, type, dock->icon_count);
|
|
|
|
dock->icon_array[dock->icon_count] = aicon;
|
|
|
|
if (aicon) {
|
|
aicon->dock = dock;
|
|
aicon->x_pos = dock->x_pos + (aicon->xindex * ICON_SIZE);
|
|
aicon->y_pos = dock->y_pos + (aicon->yindex * ICON_SIZE);
|
|
|
|
if (dock->lowered)
|
|
ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
|
|
else
|
|
ChangeStackingLevel(aicon->icon->core, WMDockLevel);
|
|
|
|
wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos, 0, 0);
|
|
if (!dock->collapsed)
|
|
XMapWindow(dpy, aicon->icon->core->window);
|
|
|
|
wRaiseFrame(aicon->icon->core);
|
|
|
|
dock->icon_count++;
|
|
} else if (dock->icon_count == 0 && type == WM_DOCK) {
|
|
dock->icon_count++;
|
|
}
|
|
}
|
|
|
|
/* if the first icon is not defined, use the default */
|
|
if (dock->icon_array[0] == NULL) {
|
|
/* update default icon */
|
|
old_top->x_pos = dock->x_pos;
|
|
old_top->y_pos = dock->y_pos;
|
|
if (dock->lowered)
|
|
ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
|
|
else
|
|
ChangeStackingLevel(old_top->icon->core, WMDockLevel);
|
|
|
|
dock->icon_array[0] = old_top;
|
|
XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
|
|
/* we don't need to increment dock->icon_count here because it was
|
|
* incremented in the loop above.
|
|
*/
|
|
} else if (old_top != dock->icon_array[0]) {
|
|
if (old_top == scr->clip_icon)
|
|
scr->clip_icon = dock->icon_array[0];
|
|
|
|
wAppIconDestroy(old_top);
|
|
}
|
|
|
|
finish:
|
|
WMReleasePropList(dock_state);
|
|
|
|
return dock;
|
|
}
|
|
|
|
void wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
|
|
{
|
|
if (btn && btn->command && !btn->running && !btn->launching) {
|
|
btn->drop_launch = 0;
|
|
btn->paste_launch = 0;
|
|
|
|
btn->pid = execCommand(btn, btn->command, state);
|
|
|
|
if (btn->pid > 0) {
|
|
if (!btn->forced_dock && !btn->buggy_app) {
|
|
btn->launching = 1;
|
|
dockIconPaint(btn);
|
|
}
|
|
}
|
|
} else {
|
|
wfree(state);
|
|
}
|
|
}
|
|
|
|
void wDockDoAutoLaunch(WDock *dock, int workspace)
|
|
{
|
|
WAppIcon *btn;
|
|
WSavedState *state;
|
|
int i;
|
|
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (!btn || !btn->auto_launch)
|
|
continue;
|
|
|
|
state = wmalloc(sizeof(WSavedState));
|
|
state->workspace = workspace;
|
|
/* TODO: this is klugy and is very difficult to understand
|
|
* what's going on. Try to clean up */
|
|
wDockLaunchWithState(dock, btn, state);
|
|
}
|
|
}
|
|
|
|
#ifdef XDND /* was OFFIX */
|
|
static WDock *findDock(WScreen *scr, XEvent *event, int *icon_pos)
|
|
{
|
|
WDock *dock;
|
|
int i;
|
|
|
|
*icon_pos = -1;
|
|
if ((dock = scr->dock) != NULL) {
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
if (dock->icon_array[i]
|
|
&& dock->icon_array[i]->icon->core->window == event->xclient.window) {
|
|
*icon_pos = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (*icon_pos < 0 && (dock = scr->workspaces[scr->current_workspace]->clip) != NULL) {
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
if (dock->icon_array[i]
|
|
&& dock->icon_array[i]->icon->core->window == event->xclient.window) {
|
|
*icon_pos = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (*icon_pos >= 0)
|
|
return dock;
|
|
return NULL;
|
|
}
|
|
|
|
int wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
|
|
{
|
|
WDock *dock;
|
|
WAppIcon *btn;
|
|
int icon_pos;
|
|
|
|
dock = findDock(scr, event, &icon_pos);
|
|
if (!dock)
|
|
return False;
|
|
|
|
/*
|
|
* Return True if the drop was on an application icon window.
|
|
* In this case, let the ClientMessage handler redirect the
|
|
* message to the app.
|
|
*/
|
|
if (dock->icon_array[icon_pos]->icon->icon_win != None)
|
|
return True;
|
|
|
|
if (dock->icon_array[icon_pos]->dnd_command != NULL) {
|
|
scr->flags.dnd_data_convertion_status = 0;
|
|
|
|
btn = dock->icon_array[icon_pos];
|
|
|
|
if (!btn->forced_dock) {
|
|
btn->relaunching = btn->running;
|
|
btn->running = 1;
|
|
}
|
|
if (btn->wm_instance || btn->wm_class) {
|
|
WWindowAttributes attr;
|
|
memset(&attr, 0, sizeof(WWindowAttributes));
|
|
wDefaultFillAttributes(btn->wm_instance, btn->wm_class, &attr, NULL, True);
|
|
|
|
if (!attr.no_appicon)
|
|
btn->launching = 1;
|
|
else
|
|
btn->running = 0;
|
|
}
|
|
|
|
btn->paste_launch = 0;
|
|
btn->drop_launch = 1;
|
|
scr->last_dock = dock;
|
|
btn->pid = execCommand(btn, btn->dnd_command, NULL);
|
|
if (btn->pid > 0) {
|
|
dockIconPaint(btn);
|
|
} else {
|
|
btn->launching = 0;
|
|
if (!btn->relaunching)
|
|
btn->running = 0;
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
#endif /* XDND */
|
|
|
|
Bool wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y, Bool update_icon)
|
|
{
|
|
WWindow *wwin;
|
|
Bool lupdate_icon = False;
|
|
char *command = NULL;
|
|
int index;
|
|
|
|
icon->editing = 0;
|
|
|
|
if (update_icon)
|
|
lupdate_icon = True;
|
|
|
|
if (icon->command == NULL) {
|
|
/* If icon->owner exists, it means the application is running */
|
|
if (icon->icon->owner) {
|
|
wwin = icon->icon->owner;
|
|
command = GetCommandForWindow(wwin->client_win);
|
|
}
|
|
|
|
if (command) {
|
|
icon->command = command;
|
|
} else {
|
|
/* icon->forced_dock = 1; */
|
|
if (dock->type != WM_CLIP || !icon->attracted) {
|
|
icon->editing = 1;
|
|
if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
|
|
_("Type the command used to launch the application"), &command)) {
|
|
if (command && (command[0] == 0 || (command[0] == '-' && command[1] == 0))) {
|
|
wfree(command);
|
|
command = NULL;
|
|
}
|
|
icon->command = command;
|
|
icon->editing = 0;
|
|
} else {
|
|
icon->editing = 0;
|
|
if (command)
|
|
wfree(command);
|
|
/* If the target is the dock, reject the icon. If
|
|
* the target is the clip, make it an attracted icon
|
|
*/
|
|
if (dock->type == WM_CLIP) {
|
|
icon->attracted = 1;
|
|
if (!icon->icon->shadowed) {
|
|
icon->icon->shadowed = 1;
|
|
lupdate_icon = True;
|
|
}
|
|
} else {
|
|
return False;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (index = 1; index < dock->max_icons; index++)
|
|
if (dock->icon_array[index] == NULL)
|
|
break;
|
|
/* if (index == dock->max_icons)
|
|
return; */
|
|
|
|
assert(index < dock->max_icons);
|
|
|
|
dock->icon_array[index] = icon;
|
|
icon->yindex = y;
|
|
icon->xindex = x;
|
|
|
|
icon->omnipresent = 0;
|
|
|
|
icon->x_pos = dock->x_pos + x * ICON_SIZE;
|
|
icon->y_pos = dock->y_pos + y * ICON_SIZE;
|
|
|
|
dock->icon_count++;
|
|
|
|
icon->running = 1;
|
|
icon->launching = 0;
|
|
icon->docked = 1;
|
|
icon->dock = dock;
|
|
icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
|
|
if (dock->type == WM_CLIP) {
|
|
icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
|
|
icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
|
|
}
|
|
icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
|
|
icon->icon->core->descriptor.parent = icon;
|
|
|
|
MoveInStackListUnder(dock->icon_array[index - 1]->icon->core, icon->icon->core);
|
|
wAppIconMove(icon, icon->x_pos, icon->y_pos);
|
|
|
|
/* Update the icon images */
|
|
if (lupdate_icon)
|
|
wIconUpdate(icon->icon, NULL);
|
|
|
|
/* Paint it */
|
|
wAppIconPaint(icon);
|
|
|
|
/* Save it */
|
|
save_appicon(icon, True);
|
|
|
|
if (wPreferences.auto_arrange_icons)
|
|
wArrangeIcons(dock->screen_ptr, True);
|
|
|
|
#ifdef XDND /* was OFFIX */
|
|
if (icon->command && !icon->dnd_command) {
|
|
int len = strlen(icon->command) + 8;
|
|
icon->dnd_command = wmalloc(len);
|
|
snprintf(icon->dnd_command, len, "%s %%d", icon->command);
|
|
}
|
|
#endif
|
|
|
|
if (icon->command && !icon->paste_command) {
|
|
int len = strlen(icon->command) + 8;
|
|
icon->paste_command = wmalloc(len);
|
|
snprintf(icon->paste_command, len, "%s %%s", icon->command);
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
static void reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
|
|
{
|
|
int index;
|
|
|
|
for (index = 1; index < dock->max_icons; index++) {
|
|
if (dock->icon_array[index] == icon)
|
|
break;
|
|
}
|
|
assert(index < dock->max_icons);
|
|
|
|
icon->yindex = y;
|
|
icon->xindex = x;
|
|
|
|
icon->x_pos = dock->x_pos + x * ICON_SIZE;
|
|
icon->y_pos = dock->y_pos + y * ICON_SIZE;
|
|
}
|
|
|
|
static Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
|
|
{
|
|
WWindow *wwin;
|
|
char *command = NULL;
|
|
int index;
|
|
Bool update_icon = False;
|
|
|
|
if (src == dest)
|
|
return True; /* No move needed, we're already there */
|
|
|
|
if (dest == NULL)
|
|
return False;
|
|
|
|
/*
|
|
* For the moment we can't do this if we move icons in Clip from one
|
|
* workspace to other, because if we move two or more icons without
|
|
* command, the dialog box will not be able to tell us to which of the
|
|
* moved icons it applies. -Dan
|
|
*/
|
|
if ((dest->type == WM_DOCK /*|| dest->keep_attracted */ ) && icon->command == NULL) {
|
|
/* If icon->owner exists, it means the application is running */
|
|
if (icon->icon->owner) {
|
|
wwin = icon->icon->owner;
|
|
command = GetCommandForWindow(wwin->client_win);
|
|
}
|
|
|
|
if (command) {
|
|
icon->command = command;
|
|
} else {
|
|
icon->editing = 1;
|
|
/* icon->forced_dock = 1; */
|
|
if (wInputDialog(src->screen_ptr, _("Dock Icon"),
|
|
_("Type the command used to launch the application"), &command)) {
|
|
if (command && (command[0] == 0 || (command[0] == '-' && command[1] == 0))) {
|
|
wfree(command);
|
|
command = NULL;
|
|
}
|
|
icon->command = command;
|
|
} else {
|
|
icon->editing = 0;
|
|
if (command)
|
|
wfree(command);
|
|
return False;
|
|
}
|
|
icon->editing = 0;
|
|
}
|
|
}
|
|
|
|
if (dest->type == WM_DOCK)
|
|
wClipMakeIconOmnipresent(icon, False);
|
|
|
|
for (index = 1; index < src->max_icons; index++) {
|
|
if (src->icon_array[index] == icon)
|
|
break;
|
|
}
|
|
assert(index < src->max_icons);
|
|
|
|
src->icon_array[index] = NULL;
|
|
src->icon_count--;
|
|
|
|
for (index = 1; index < dest->max_icons; index++) {
|
|
if (dest->icon_array[index] == NULL)
|
|
break;
|
|
}
|
|
|
|
assert(index < dest->max_icons);
|
|
|
|
dest->icon_array[index] = icon;
|
|
icon->dock = dest;
|
|
|
|
/* deselect the icon */
|
|
if (icon->icon->selected)
|
|
wIconSelect(icon->icon);
|
|
|
|
if (dest->type == WM_DOCK) {
|
|
icon->icon->core->descriptor.handle_enternotify = NULL;
|
|
icon->icon->core->descriptor.handle_leavenotify = NULL;
|
|
} else {
|
|
icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
|
|
icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
|
|
}
|
|
|
|
/* set it to be kept when moving to dock.
|
|
* Unless the icon does not have a command set
|
|
*/
|
|
if (icon->command && dest->type == WM_DOCK) {
|
|
icon->attracted = 0;
|
|
if (icon->icon->shadowed) {
|
|
icon->icon->shadowed = 0;
|
|
update_icon = True;
|
|
}
|
|
save_appicon(icon, True);
|
|
}
|
|
|
|
if (src->auto_collapse || src->auto_raise_lower)
|
|
clipLeave(src);
|
|
|
|
icon->yindex = y;
|
|
icon->xindex = x;
|
|
|
|
icon->x_pos = dest->x_pos + x * ICON_SIZE;
|
|
icon->y_pos = dest->y_pos + y * ICON_SIZE;
|
|
|
|
dest->icon_count++;
|
|
|
|
MoveInStackListUnder(dest->icon_array[index - 1]->icon->core, icon->icon->core);
|
|
|
|
/* Update the icon images */
|
|
if (update_icon)
|
|
wIconUpdate(icon->icon, NULL);
|
|
|
|
/* Paint it */
|
|
wAppIconPaint(icon);
|
|
|
|
return True;
|
|
}
|
|
|
|
void wDockDetach(WDock *dock, WAppIcon *icon)
|
|
{
|
|
int index;
|
|
Bool update_icon = False;
|
|
|
|
/* make the settings panel be closed */
|
|
if (icon->panel)
|
|
DestroyDockAppSettingsPanel(icon->panel);
|
|
|
|
/* This must be called before icon->dock is set to NULL.
|
|
* Don't move it. -Dan
|
|
*/
|
|
wClipMakeIconOmnipresent(icon, False);
|
|
|
|
icon->docked = 0;
|
|
icon->dock = NULL;
|
|
icon->attracted = 0;
|
|
icon->auto_launch = 0;
|
|
if (icon->icon->shadowed) {
|
|
icon->icon->shadowed = 0;
|
|
update_icon = True;
|
|
}
|
|
|
|
/* deselect the icon */
|
|
if (icon->icon->selected)
|
|
wIconSelect(icon->icon);
|
|
|
|
if (icon->command) {
|
|
wfree(icon->command);
|
|
icon->command = NULL;
|
|
}
|
|
#ifdef XDND /* was OFFIX */
|
|
if (icon->dnd_command) {
|
|
wfree(icon->dnd_command);
|
|
icon->dnd_command = NULL;
|
|
}
|
|
#endif
|
|
if (icon->paste_command) {
|
|
wfree(icon->paste_command);
|
|
icon->paste_command = NULL;
|
|
}
|
|
|
|
for (index = 1; index < dock->max_icons; index++)
|
|
if (dock->icon_array[index] == icon)
|
|
break;
|
|
|
|
assert(index < dock->max_icons);
|
|
dock->icon_array[index] = NULL;
|
|
icon->yindex = -1;
|
|
icon->xindex = -1;
|
|
|
|
dock->icon_count--;
|
|
|
|
/* if the dock is not attached to an application or
|
|
* the application did not set the appropriate hints yet,
|
|
* destroy the icon */
|
|
if (!icon->running || !wApplicationOf(icon->main_window)) {
|
|
wAppIconDestroy(icon);
|
|
} else {
|
|
icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
|
|
icon->icon->core->descriptor.handle_enternotify = NULL;
|
|
icon->icon->core->descriptor.handle_leavenotify = NULL;
|
|
icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
|
|
icon->icon->core->descriptor.parent = icon;
|
|
|
|
ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
|
|
|
|
/* Update the icon images */
|
|
if (update_icon)
|
|
wIconUpdate(icon->icon, NULL);
|
|
|
|
/* Paint it */
|
|
wAppIconPaint(icon);
|
|
|
|
if (wPreferences.auto_arrange_icons)
|
|
wArrangeIcons(dock->screen_ptr, True);
|
|
}
|
|
if (dock->auto_collapse || dock->auto_raise_lower)
|
|
clipLeave(dock);
|
|
}
|
|
|
|
/*
|
|
* returns the closest Dock slot index for the passed
|
|
* coordinates.
|
|
*
|
|
* Returns False if icon can't be docked.
|
|
*
|
|
* Note: this function should NEVER alter ret_x or ret_y, unless it will
|
|
* return True. -Dan
|
|
*/
|
|
Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x, int *ret_y, int redocking)
|
|
{
|
|
WScreen *scr = dock->screen_ptr;
|
|
int dx, dy;
|
|
int ex_x, ex_y;
|
|
int i, offset = ICON_SIZE / 2;
|
|
WAppIcon *aicon = NULL;
|
|
WAppIcon *nicon = NULL;
|
|
int max_y_icons;
|
|
|
|
/* TODO: XINERAMA, for these */
|
|
max_y_icons = scr->scr_height / ICON_SIZE - 1;
|
|
|
|
if (wPreferences.flags.noupdates)
|
|
return False;
|
|
|
|
dx = dock->x_pos;
|
|
dy = dock->y_pos;
|
|
|
|
/* if the dock is full */
|
|
if (!redocking && (dock->icon_count >= dock->max_icons))
|
|
return False;
|
|
|
|
/* exact position */
|
|
if (req_y < dy)
|
|
ex_y = (req_y - offset - dy) / ICON_SIZE;
|
|
else
|
|
ex_y = (req_y + offset - dy) / ICON_SIZE;
|
|
|
|
if (req_x < dx)
|
|
ex_x = (req_x - offset - dx) / ICON_SIZE;
|
|
else
|
|
ex_x = (req_x + offset - dx) / ICON_SIZE;
|
|
|
|
/* check if the icon is outside the screen boundaries */
|
|
{
|
|
WMRect rect;
|
|
int flags;
|
|
|
|
rect.pos.x = dx + ex_x * ICON_SIZE;
|
|
rect.pos.y = dy + ex_y * ICON_SIZE;
|
|
rect.size.width = rect.size.height = ICON_SIZE;
|
|
|
|
wGetRectPlacementInfo(scr, rect, &flags);
|
|
if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
|
|
return False;
|
|
}
|
|
|
|
if (dock->type == WM_DOCK) {
|
|
if (icon->dock != dock && ex_x != 0)
|
|
return False;
|
|
|
|
aicon = NULL;
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
nicon = dock->icon_array[i];
|
|
if (nicon && nicon->yindex == ex_y) {
|
|
aicon = nicon;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (redocking) {
|
|
int sig, done, closest;
|
|
|
|
/* Possible cases when redocking:
|
|
*
|
|
* icon dragged out of range of any slot -> false
|
|
* icon dragged to range of free slot
|
|
* icon dragged to range of same slot
|
|
* icon dragged to range of different icon
|
|
*/
|
|
if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
|
|
return False;
|
|
|
|
if (ex_y >= 0 && ex_y <= max_y_icons && (aicon == icon || !aicon)) {
|
|
*ret_x = 0;
|
|
*ret_y = ex_y;
|
|
return True;
|
|
}
|
|
|
|
/* start looking at the upper slot or lower? */
|
|
if (ex_y * ICON_SIZE < (req_y + offset - dy))
|
|
sig = 1;
|
|
else
|
|
sig = -1;
|
|
|
|
closest = -1;
|
|
done = 0;
|
|
/* look for closest free slot */
|
|
for (i = 0; i < (DOCK_DETTACH_THRESHOLD + 1) * 2 && !done; i++) {
|
|
int j;
|
|
|
|
done = 1;
|
|
closest = sig * (i / 2) + ex_y;
|
|
/* check if this slot is used */
|
|
if (closest >= 0) {
|
|
for (j = 0; j < dock->max_icons; j++) {
|
|
if (dock->icon_array[j]
|
|
&& dock->icon_array[j]->yindex == closest) {
|
|
/* slot is used by someone else */
|
|
if (dock->icon_array[j] != icon)
|
|
done = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
sig = -sig;
|
|
}
|
|
if (done && closest >= 0 && closest <= max_y_icons &&
|
|
((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD + 1)
|
|
|| (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD + 1))) {
|
|
*ret_x = 0;
|
|
*ret_y = closest;
|
|
return True;
|
|
}
|
|
} else { /* !redocking */
|
|
|
|
/* if slot is free and the icon is close enough, return it */
|
|
if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
|
|
*ret_x = 0;
|
|
*ret_y = ex_y;
|
|
return True;
|
|
}
|
|
}
|
|
} else { /* CLIP */
|
|
int neighbours = 0;
|
|
int start, stop, k;
|
|
|
|
start = icon->omnipresent ? 0 : scr->current_workspace;
|
|
stop = icon->omnipresent ? scr->workspace_count : start + 1;
|
|
|
|
aicon = NULL;
|
|
for (k = start; k < stop; k++) {
|
|
WDock *tmp = scr->workspaces[k]->clip;
|
|
if (!tmp)
|
|
continue;
|
|
for (i = 0; i < tmp->max_icons; i++) {
|
|
nicon = tmp->icon_array[i];
|
|
if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
|
|
aicon = nicon;
|
|
break;
|
|
}
|
|
}
|
|
if (aicon)
|
|
break;
|
|
}
|
|
for (k = start; k < stop; k++) {
|
|
WDock *tmp = scr->workspaces[k]->clip;
|
|
if (!tmp)
|
|
continue;
|
|
for (i = 0; i < tmp->max_icons; i++) {
|
|
nicon = tmp->icon_array[i];
|
|
if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
|
|
(abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
|
|
abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
|
|
neighbours = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (neighbours)
|
|
break;
|
|
}
|
|
|
|
if (neighbours && (aicon == NULL || (redocking && aicon == icon))) {
|
|
*ret_x = ex_x;
|
|
*ret_y = ex_y;
|
|
return True;
|
|
}
|
|
}
|
|
return False;
|
|
}
|
|
|
|
static int onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey)
|
|
{
|
|
WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE);
|
|
int flags;
|
|
|
|
wGetRectPlacementInfo(scr, rect, &flags);
|
|
|
|
return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
|
|
}
|
|
|
|
/*
|
|
* returns true if it can find a free slot in the dock,
|
|
* in which case it changes x_pos and y_pos accordingly.
|
|
* Else returns false.
|
|
*/
|
|
Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
|
|
{
|
|
WScreen *scr = dock->screen_ptr;
|
|
WAppIcon *btn;
|
|
WAppIconChain *chain;
|
|
unsigned char *slot_map;
|
|
int mwidth;
|
|
int r;
|
|
int x, y;
|
|
int i, done = False;
|
|
int corner;
|
|
int sx = 0, sy = 0, ex = scr->scr_width, ey = scr->scr_height;
|
|
int extra_count = 0;
|
|
|
|
if (dock->type == WM_CLIP && dock != scr->workspaces[scr->current_workspace]->clip)
|
|
extra_count = scr->global_icon_count;
|
|
|
|
/* if the dock is full */
|
|
if (dock->icon_count + extra_count >= dock->max_icons)
|
|
return False;
|
|
|
|
if (!wPreferences.flags.nodock && scr->dock) {
|
|
if (scr->dock->on_right_side)
|
|
ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
|
|
else
|
|
sx += ICON_SIZE + DOCK_EXTRA_SPACE;
|
|
}
|
|
|
|
if (ex < dock->x_pos)
|
|
ex = dock->x_pos;
|
|
if (sx > dock->x_pos + ICON_SIZE)
|
|
sx = dock->x_pos + ICON_SIZE;
|
|
#define C_NONE 0
|
|
#define C_NW 1
|
|
#define C_NE 2
|
|
#define C_SW 3
|
|
#define C_SE 4
|
|
|
|
/* check if clip is in a corner */
|
|
if (dock->type == WM_CLIP) {
|
|
if (dock->x_pos < 1 && dock->y_pos < 1)
|
|
corner = C_NE;
|
|
else if (dock->x_pos < 1 && dock->y_pos >= (ey - ICON_SIZE))
|
|
corner = C_SE;
|
|
else if (dock->x_pos >= (ex - ICON_SIZE) && dock->y_pos >= (ey - ICON_SIZE))
|
|
corner = C_SW;
|
|
else if (dock->x_pos >= (ex - ICON_SIZE) && dock->y_pos < 1)
|
|
corner = C_NW;
|
|
else
|
|
corner = C_NONE;
|
|
} else {
|
|
corner = C_NONE;
|
|
}
|
|
|
|
/* If the clip is in the corner, use only slots that are in the border
|
|
* of the screen */
|
|
if (corner != C_NONE) {
|
|
char *hmap, *vmap;
|
|
int hcount, vcount;
|
|
|
|
hcount = WMIN(dock->max_icons, scr->scr_width / ICON_SIZE);
|
|
vcount = WMIN(dock->max_icons, scr->scr_height / ICON_SIZE);
|
|
hmap = wmalloc(hcount + 1);
|
|
vmap = wmalloc(vcount + 1);
|
|
|
|
/* mark used positions */
|
|
switch (corner) {
|
|
case C_NE:
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (!btn)
|
|
continue;
|
|
|
|
if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
|
|
vmap[btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
|
|
hmap[btn->xindex] = 1;
|
|
}
|
|
for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
|
|
btn = chain->aicon;
|
|
if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
|
|
vmap[btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
|
|
hmap[btn->xindex] = 1;
|
|
}
|
|
break;
|
|
case C_NW:
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (!btn)
|
|
continue;
|
|
|
|
if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
|
|
vmap[btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
|
|
hmap[-btn->xindex] = 1;
|
|
}
|
|
for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
|
|
btn = chain->aicon;
|
|
if (btn->xindex == 0 && btn->yindex > 0 && btn->yindex < vcount)
|
|
vmap[btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
|
|
hmap[-btn->xindex] = 1;
|
|
}
|
|
break;
|
|
case C_SE:
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (!btn)
|
|
continue;
|
|
|
|
if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
|
|
vmap[-btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
|
|
hmap[btn->xindex] = 1;
|
|
}
|
|
for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
|
|
btn = chain->aicon;
|
|
if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
|
|
vmap[-btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex > 0 && btn->xindex < hcount)
|
|
hmap[btn->xindex] = 1;
|
|
}
|
|
break;
|
|
case C_SW:
|
|
default:
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (!btn)
|
|
continue;
|
|
|
|
if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
|
|
vmap[-btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
|
|
hmap[-btn->xindex] = 1;
|
|
}
|
|
for (chain = scr->global_icons; chain != NULL; chain = chain->next) {
|
|
btn = chain->aicon;
|
|
if (btn->xindex == 0 && btn->yindex < 0 && btn->yindex > -vcount)
|
|
vmap[-btn->yindex] = 1;
|
|
else if (btn->yindex == 0 && btn->xindex < 0 && btn->xindex > -hcount)
|
|
hmap[-btn->xindex] = 1;
|
|
}
|
|
}
|
|
x = 0;
|
|
y = 0;
|
|
done = 0;
|
|
/* search a vacant slot */
|
|
for (i = 1; i < WMAX(vcount, hcount); i++) {
|
|
if (i < vcount && vmap[i] == 0) {
|
|
/* found a slot */
|
|
x = 0;
|
|
y = i;
|
|
done = 1;
|
|
break;
|
|
} else if (i < hcount && hmap[i] == 0) {
|
|
/* found a slot */
|
|
x = i;
|
|
y = 0;
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
wfree(vmap);
|
|
wfree(hmap);
|
|
/* If found a slot, translate and return */
|
|
if (done) {
|
|
if (corner == C_NW || corner == C_NE)
|
|
*y_pos = y;
|
|
else
|
|
*y_pos = -y;
|
|
|
|
if (corner == C_NE || corner == C_SE)
|
|
*x_pos = x;
|
|
else
|
|
*x_pos = -x;
|
|
|
|
return True;
|
|
}
|
|
/* else, try to find a slot somewhere else */
|
|
}
|
|
|
|
/* a map of mwidth x mwidth would be enough if we allowed icons to be
|
|
* placed outside of screen */
|
|
mwidth = (int)ceil(sqrt(dock->max_icons));
|
|
|
|
/* In the worst case (the clip is in the corner of the screen),
|
|
* the amount of icons that fit in the clip is smaller.
|
|
* Double the map to get a safe value.
|
|
*/
|
|
mwidth += mwidth;
|
|
|
|
r = (mwidth - 1) / 2;
|
|
|
|
slot_map = wmalloc(mwidth * mwidth);
|
|
|
|
#define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
|
|
|
|
/* mark used slots in the map. If the slot falls outside the map
|
|
* (for example, when all icons are placed in line), ignore them. */
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (btn)
|
|
slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
|
|
}
|
|
|
|
for (chain = scr->global_icons; chain != NULL; chain = chain->next)
|
|
slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
|
|
|
|
/* Find closest slot from the center that is free by scanning the
|
|
* map from the center to outward in circular passes.
|
|
* This will not result in a neat layout, but will be optimal
|
|
* in the sense that there will not be holes left.
|
|
*/
|
|
done = 0;
|
|
for (i = 1; i <= r && !done; i++) {
|
|
int tx, ty;
|
|
|
|
/* top and bottom parts of the ring */
|
|
for (x = -i; x <= i && !done; x++) {
|
|
tx = dock->x_pos + x * ICON_SIZE;
|
|
y = -i;
|
|
ty = dock->y_pos + y * ICON_SIZE;
|
|
if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
|
|
*x_pos = x;
|
|
*y_pos = y;
|
|
done = 1;
|
|
break;
|
|
}
|
|
y = i;
|
|
ty = dock->y_pos + y * ICON_SIZE;
|
|
if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
|
|
*x_pos = x;
|
|
*y_pos = y;
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
/* left and right parts of the ring */
|
|
for (y = -i + 1; y <= i - 1; y++) {
|
|
ty = dock->y_pos + y * ICON_SIZE;
|
|
x = -i;
|
|
tx = dock->x_pos + x * ICON_SIZE;
|
|
if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
|
|
*x_pos = x;
|
|
*y_pos = y;
|
|
done = 1;
|
|
break;
|
|
}
|
|
x = i;
|
|
tx = dock->x_pos + x * ICON_SIZE;
|
|
if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
|
|
*x_pos = x;
|
|
*y_pos = y;
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
wfree(slot_map);
|
|
#undef XY2OFS
|
|
return done;
|
|
}
|
|
|
|
static void moveDock(WDock *dock, int new_x, int new_y)
|
|
{
|
|
WAppIcon *btn;
|
|
int i;
|
|
|
|
dock->x_pos = new_x;
|
|
dock->y_pos = new_y;
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (btn) {
|
|
btn->x_pos = new_x + btn->xindex * ICON_SIZE;
|
|
btn->y_pos = new_y + btn->yindex * ICON_SIZE;
|
|
XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void swapDock(WDock *dock)
|
|
{
|
|
WScreen *scr = dock->screen_ptr;
|
|
WAppIcon *btn;
|
|
int x, i;
|
|
|
|
if (dock->on_right_side)
|
|
x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
|
|
else
|
|
x = dock->x_pos = DOCK_EXTRA_SPACE;
|
|
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
btn = dock->icon_array[i];
|
|
if (btn) {
|
|
btn->x_pos = x;
|
|
XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
|
|
}
|
|
}
|
|
|
|
wScreenUpdateUsableArea(scr);
|
|
}
|
|
|
|
static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state)
|
|
{
|
|
WScreen *scr = btn->icon->core->screen_ptr;
|
|
pid_t pid;
|
|
char **argv;
|
|
int argc;
|
|
char *cmdline;
|
|
|
|
cmdline = ExpandOptions(scr, command);
|
|
|
|
if (scr->flags.dnd_data_convertion_status || !cmdline) {
|
|
if (cmdline)
|
|
wfree(cmdline);
|
|
if (state)
|
|
wfree(state);
|
|
return 0;
|
|
}
|
|
|
|
wtokensplit(cmdline, &argv, &argc);
|
|
|
|
if (!argc) {
|
|
if (cmdline)
|
|
wfree(cmdline);
|
|
if (state)
|
|
wfree(state);
|
|
return 0;
|
|
}
|
|
|
|
if ((pid = fork()) == 0) {
|
|
char **args;
|
|
int i;
|
|
|
|
SetupEnvironment(scr);
|
|
|
|
#ifdef HAVE_SETSID
|
|
setsid();
|
|
#endif
|
|
|
|
args = malloc(sizeof(char *) * (argc + 1));
|
|
if (!args)
|
|
exit(111);
|
|
|
|
for (i = 0; i < argc; i++)
|
|
args[i] = argv[i];
|
|
|
|
args[argc] = NULL;
|
|
execvp(argv[0], args);
|
|
exit(111);
|
|
}
|
|
wtokenfree(argv, argc);
|
|
|
|
if (pid > 0) {
|
|
if (!state) {
|
|
state = wmalloc(sizeof(WSavedState));
|
|
state->hidden = -1;
|
|
state->miniaturized = -1;
|
|
state->shaded = -1;
|
|
if (btn->dock == scr->dock || btn->omnipresent)
|
|
state->workspace = -1;
|
|
else
|
|
state->workspace = scr->current_workspace;
|
|
}
|
|
wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid, state);
|
|
wAddDeathHandler(pid, (WDeathHandler *) trackDeadProcess, btn->dock);
|
|
} else if (state) {
|
|
wfree(state);
|
|
}
|
|
wfree(cmdline);
|
|
return pid;
|
|
}
|
|
|
|
void wDockHideIcons(WDock *dock)
|
|
{
|
|
int i;
|
|
|
|
if (dock == NULL)
|
|
return;
|
|
|
|
for (i = 1; i < dock->max_icons; i++) {
|
|
if (dock->icon_array[i])
|
|
XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
|
|
}
|
|
dock->mapped = 0;
|
|
|
|
dockIconPaint(dock->icon_array[0]);
|
|
}
|
|
|
|
void wDockShowIcons(WDock *dock)
|
|
{
|
|
int i, newlevel;
|
|
WAppIcon *btn;
|
|
|
|
if (dock == NULL)
|
|
return;
|
|
|
|
btn = dock->icon_array[0];
|
|
moveDock(dock, btn->x_pos, btn->y_pos);
|
|
|
|
newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
|
|
ChangeStackingLevel(btn->icon->core, newlevel);
|
|
|
|
for (i = 1; i < dock->max_icons; i++) {
|
|
if (dock->icon_array[i]) {
|
|
MoveInStackListAbove(dock->icon_array[i]->icon->core, btn->icon->core);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!dock->collapsed) {
|
|
for (i = 1; i < dock->max_icons; i++) {
|
|
if (dock->icon_array[i])
|
|
XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
|
|
}
|
|
}
|
|
dock->mapped = 1;
|
|
|
|
dockIconPaint(btn);
|
|
}
|
|
|
|
void wDockLower(WDock *dock)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
if (dock->icon_array[i])
|
|
wLowerFrame(dock->icon_array[i]->icon->core);
|
|
}
|
|
}
|
|
|
|
void wDockRaise(WDock *dock)
|
|
{
|
|
int i;
|
|
|
|
for (i = dock->max_icons - 1; i >= 0; i--) {
|
|
if (dock->icon_array[i])
|
|
wRaiseFrame(dock->icon_array[i]->icon->core);
|
|
}
|
|
}
|
|
|
|
void wDockRaiseLower(WDock *dock)
|
|
{
|
|
if (!dock->icon_array[0]->icon->core->stacking->above
|
|
|| (dock->icon_array[0]->icon->core->stacking->window_level
|
|
!= dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
|
|
wDockLower(dock);
|
|
else
|
|
wDockRaise(dock);
|
|
}
|
|
|
|
void wDockFinishLaunch(WDock *dock, WAppIcon *icon)
|
|
{
|
|
icon->launching = 0;
|
|
icon->relaunching = 0;
|
|
dockIconPaint(icon);
|
|
}
|
|
|
|
WAppIcon *wDockFindIconForWindow(WDock *dock, Window window)
|
|
{
|
|
WAppIcon *icon;
|
|
int i;
|
|
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
icon = dock->icon_array[i];
|
|
if (icon && icon->main_window == window)
|
|
return icon;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void wDockTrackWindowLaunch(WDock *dock, Window window)
|
|
{
|
|
WAppIcon *icon;
|
|
char *wm_class, *wm_instance;
|
|
int i;
|
|
Bool firstPass = True;
|
|
Bool found = False;
|
|
char *command = NULL;
|
|
|
|
if (!PropGetWMClass(window, &wm_class, &wm_instance) || (!wm_class && !wm_instance))
|
|
return;
|
|
|
|
command = GetCommandForWindow(window);
|
|
retry:
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
icon = dock->icon_array[i];
|
|
if (!icon)
|
|
continue;
|
|
|
|
/* app is already attached to icon */
|
|
if (icon->main_window == window) {
|
|
found = True;
|
|
break;
|
|
}
|
|
|
|
if ((icon->wm_instance || icon->wm_class)
|
|
&& (icon->launching || !icon->running)) {
|
|
|
|
if (icon->wm_instance && wm_instance && strcmp(icon->wm_instance, wm_instance) != 0)
|
|
continue;
|
|
|
|
if (icon->wm_class && wm_class && strcmp(icon->wm_class, wm_class) != 0)
|
|
continue;
|
|
|
|
if (firstPass && command && strcmp(icon->command, command) != 0)
|
|
continue;
|
|
|
|
if (!icon->relaunching) {
|
|
WApplication *wapp;
|
|
|
|
/* Possibly an application that was docked with dockit,
|
|
* but the user did not update WMState to indicate that
|
|
* it was docked by force */
|
|
wapp = wApplicationOf(window);
|
|
if (!wapp) {
|
|
icon->forced_dock = 1;
|
|
icon->running = 0;
|
|
}
|
|
if (!icon->forced_dock)
|
|
icon->main_window = window;
|
|
}
|
|
found = True;
|
|
if (!wPreferences.no_animations && !icon->launching &&
|
|
!dock->screen_ptr->flags.startup && !dock->collapsed) {
|
|
WAppIcon *aicon;
|
|
int x0, y0;
|
|
|
|
icon->launching = 1;
|
|
dockIconPaint(icon);
|
|
|
|
aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
|
|
wm_instance, wm_class, TILE_NORMAL);
|
|
/* XXX: can: aicon->icon == NULL ? */
|
|
PlaceIcon(dock->screen_ptr, &x0, &y0, wGetHeadForWindow(aicon->icon->owner));
|
|
wAppIconMove(aicon, x0, y0);
|
|
/* Should this always be lowered? -Dan */
|
|
if (dock->lowered)
|
|
wLowerFrame(aicon->icon->core);
|
|
XMapWindow(dpy, aicon->icon->core->window);
|
|
aicon->launching = 1;
|
|
wAppIconPaint(aicon);
|
|
SlideWindow(aicon->icon->core->window, x0, y0, icon->x_pos, icon->y_pos);
|
|
XUnmapWindow(dpy, aicon->icon->core->window);
|
|
wAppIconDestroy(aicon);
|
|
}
|
|
wDockFinishLaunch(dock, icon);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (firstPass && !found) {
|
|
firstPass = False;
|
|
goto retry;
|
|
}
|
|
|
|
if (command)
|
|
wfree(command);
|
|
|
|
if (wm_class)
|
|
free(wm_class);
|
|
if (wm_instance)
|
|
free(wm_instance);
|
|
}
|
|
|
|
void wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
|
|
{
|
|
if (!wPreferences.flags.noclip) {
|
|
scr->clip_icon->dock = scr->workspaces[workspace]->clip;
|
|
if (scr->current_workspace != workspace) {
|
|
WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
|
|
WAppIconChain *chain = scr->global_icons;
|
|
|
|
while (chain) {
|
|
moveIconBetweenDocks(chain->aicon->dock,
|
|
scr->workspaces[workspace]->clip,
|
|
chain->aicon, chain->aicon->xindex, chain->aicon->yindex);
|
|
if (scr->workspaces[workspace]->clip->collapsed)
|
|
XUnmapWindow(dpy, chain->aicon->icon->core->window);
|
|
chain = chain->next;
|
|
}
|
|
|
|
wDockHideIcons(old_clip);
|
|
if (old_clip->auto_raise_lower) {
|
|
if (old_clip->auto_raise_magic) {
|
|
WMDeleteTimerHandler(old_clip->auto_raise_magic);
|
|
old_clip->auto_raise_magic = NULL;
|
|
}
|
|
wDockLower(old_clip);
|
|
}
|
|
if (old_clip->auto_collapse) {
|
|
if (old_clip->auto_expand_magic) {
|
|
WMDeleteTimerHandler(old_clip->auto_expand_magic);
|
|
old_clip->auto_expand_magic = NULL;
|
|
}
|
|
old_clip->collapsed = 1;
|
|
}
|
|
wDockShowIcons(scr->workspaces[workspace]->clip);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
|
|
{
|
|
WAppIcon *icon;
|
|
int i;
|
|
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
icon = dock->icon_array[i];
|
|
if (!icon)
|
|
continue;
|
|
|
|
if (icon->launching && icon->pid == pid) {
|
|
if (!icon->relaunching) {
|
|
icon->running = 0;
|
|
icon->main_window = None;
|
|
}
|
|
wDockFinishLaunch(dock, icon);
|
|
icon->pid = 0;
|
|
if (status == 111) {
|
|
char msg[PATH_MAX];
|
|
char *cmd;
|
|
|
|
#ifdef XDND
|
|
if (icon->drop_launch)
|
|
cmd = icon->dnd_command;
|
|
else
|
|
#endif
|
|
if (icon->paste_launch)
|
|
cmd = icon->paste_command;
|
|
else
|
|
cmd = icon->command;
|
|
|
|
snprintf(msg, sizeof(msg), _("Could not execute command \"%s\""), cmd);
|
|
|
|
wMessageDialog(dock->screen_ptr, _("Error"), msg, _("OK"), NULL, NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void toggleLowered(WDock *dock)
|
|
{
|
|
WAppIcon *tmp;
|
|
int newlevel, i;
|
|
|
|
/* lower/raise Dock */
|
|
if (!dock->lowered) {
|
|
newlevel = WMNormalLevel;
|
|
dock->lowered = 1;
|
|
} else {
|
|
newlevel = WMDockLevel;
|
|
dock->lowered = 0;
|
|
}
|
|
|
|
for (i = 0; i < dock->max_icons; i++) {
|
|
tmp = dock->icon_array[i];
|
|
if (!tmp)
|
|
continue;
|
|
|
|
ChangeStackingLevel(tmp->icon->core, newlevel);
|
|
if (dock->lowered)
|
|
wLowerFrame(tmp->icon->core);
|
|
}
|
|
|
|
if (dock->type == WM_DOCK)
|
|
wScreenUpdateUsableArea(dock->screen_ptr);
|
|
}
|
|
|
|
static void toggleCollapsed(WDock *dock)
|
|
{
|
|
if (dock->collapsed) {
|
|
dock->collapsed = 0;
|
|
wDockShowIcons(dock);
|
|
} else {
|
|
dock->collapsed = 1;
|
|
wDockHideIcons(dock);
|
|
}
|
|
}
|
|
|
|
static void openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
|
|
{
|
|
WScreen *scr = dock->screen_ptr;
|
|
WObjDescriptor *desc;
|
|
WMenuEntry *entry;
|
|
WApplication *wapp = NULL;
|
|
int index = 0;
|
|
int x_pos;
|
|
int n_selected;
|
|
int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
|
|
|
|
if (dock->type == WM_DOCK) {
|
|
/* keep on top */
|
|
entry = dock->menu->entries[index];
|
|
entry->flags.indicator_on = !dock->lowered;
|
|
entry->clientdata = dock;
|
|
dock->menu->flags.realized = 0;
|
|
} else {
|
|
/* clip options */
|
|
if (scr->clip_options)
|
|
updateClipOptionsMenu(scr->clip_options, dock);
|
|
|
|
n_selected = numberOfSelectedIcons(dock);
|
|
|
|
/* Rename Workspace */
|
|
entry = dock->menu->entries[++index];
|
|
if (aicon == scr->clip_icon) {
|
|
entry->callback = renameCallback;
|
|
entry->clientdata = dock;
|
|
entry->flags.indicator = 0;
|
|
entry->text = _("Rename Workspace");
|
|
} else {
|
|
entry->callback = omnipresentCallback;
|
|
entry->clientdata = aicon;
|
|
if (n_selected > 0) {
|
|
entry->flags.indicator = 0;
|
|
entry->text = _("Toggle Omnipresent");
|
|
} else {
|
|
entry->flags.indicator = 1;
|
|
entry->flags.indicator_on = aicon->omnipresent;
|
|
entry->flags.indicator_type = MI_CHECK;
|
|
entry->text = _("Omnipresent");
|
|
}
|
|
}
|
|
|
|
/* select/unselect icon */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
entry->flags.indicator_on = aicon->icon->selected;
|
|
wMenuSetEnabled(dock->menu, index, aicon != scr->clip_icon);
|
|
|
|
/* select/unselect all icons */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
if (n_selected > 0)
|
|
entry->text = _("Unselect All Icons");
|
|
else
|
|
entry->text = _("Select All Icons");
|
|
|
|
wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
|
|
|
|
/* keep icon(s) */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
if (n_selected > 1)
|
|
entry->text = _("Keep Icons");
|
|
else
|
|
entry->text = _("Keep Icon");
|
|
|
|
wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
|
|
|
|
/* this is the workspace submenu part */
|
|
entry = dock->menu->entries[++index];
|
|
if (n_selected > 1)
|
|
entry->text = _("Move Icons To");
|
|
else
|
|
entry->text = _("Move Icon To");
|
|
|
|
if (scr->clip_submenu)
|
|
updateWorkspaceMenu(scr->clip_submenu, aicon);
|
|
|
|
wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
|
|
|
|
/* remove icon(s) */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
if (n_selected > 1)
|
|
entry->text = _("Remove Icons");
|
|
else
|
|
entry->text = _("Remove Icon");
|
|
|
|
wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
|
|
|
|
/* attract icon(s) */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
|
|
dock->menu->flags.realized = 0;
|
|
wMenuRealize(dock->menu);
|
|
}
|
|
|
|
if (aicon->icon->owner)
|
|
wapp = wApplicationOf(aicon->icon->owner->main_window);
|
|
else
|
|
wapp = NULL;
|
|
|
|
/* launch */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
wMenuSetEnabled(dock->menu, index, aicon->command != NULL);
|
|
|
|
/* unhide here */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
if (wapp && wapp->flags.hidden)
|
|
entry->text = _("Unhide Here");
|
|
else
|
|
entry->text = _("Bring Here");
|
|
|
|
wMenuSetEnabled(dock->menu, index, appIsRunning);
|
|
|
|
/* hide */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
if (wapp && wapp->flags.hidden)
|
|
entry->text = _("Unhide");
|
|
else
|
|
entry->text = _("Hide");
|
|
|
|
wMenuSetEnabled(dock->menu, index, appIsRunning);
|
|
|
|
/* settings */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
wMenuSetEnabled(dock->menu, index, !aicon->editing && !wPreferences.flags.noupdates);
|
|
|
|
/* kill */
|
|
entry = dock->menu->entries[++index];
|
|
entry->clientdata = aicon;
|
|
wMenuSetEnabled(dock->menu, index, appIsRunning);
|
|
|
|
if (!dock->menu->flags.realized)
|
|
wMenuRealize(dock->menu);
|
|
|
|
if (dock->type == WM_CLIP) {
|
|
/*x_pos = event->xbutton.x_root+2; */
|
|
x_pos = event->xbutton.x_root - dock->menu->frame->core->width / 2 - 1;
|
|
if (x_pos < 0) {
|
|
x_pos = 0;
|
|
} else if (x_pos + dock->menu->frame->core->width > scr->scr_width - 2) {
|
|
x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
|
|
}
|
|
} else {
|
|
x_pos = dock->on_right_side ? scr->scr_width - dock->menu->frame->core->width - 3 : 0;
|
|
}
|
|
|
|
wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root + 2, False);
|
|
|
|
/* allow drag select */
|
|
event->xany.send_event = True;
|
|
desc = &dock->menu->menu->descriptor;
|
|
(*desc->handle_mousedown) (desc, event);
|
|
}
|
|
|
|
/******************************************************************/
|
|
static void iconDblClick(WObjDescriptor *desc, XEvent *event)
|
|
{
|
|
WAppIcon *btn = desc->parent;
|
|
WDock *dock = btn->dock;
|
|
WApplication *wapp = NULL;
|
|
int unhideHere = 0;
|
|
|
|
if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
|
|
wapp = wApplicationOf(btn->icon->owner->main_window);
|
|
|
|
assert(wapp != NULL);
|
|
|
|
unhideHere = (event->xbutton.state & ShiftMask);
|
|
|
|
/* go to the last workspace that the user worked on the app */
|
|
if (wapp->last_workspace != dock->screen_ptr->current_workspace && !unhideHere)
|
|
wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
|
|
|
|
wUnhideApplication(wapp, event->xbutton.button == Button2, unhideHere);
|
|
|
|
if (event->xbutton.state & MOD_MASK)
|
|
wHideOtherApplications(btn->icon->owner);
|
|
} else {
|
|
if (event->xbutton.button == Button1) {
|
|
if (event->xbutton.state & MOD_MASK) {
|
|
/* raise/lower dock */
|
|
toggleLowered(dock);
|
|
} else if (btn == dock->screen_ptr->clip_icon) {
|
|
if (getClipButton(event->xbutton.x, event->xbutton.y) == CLIP_IDLE)
|
|
toggleCollapsed(dock);
|
|
else
|
|
handleClipChangeWorkspace(dock->screen_ptr, event);
|
|
} else if (btn->command) {
|
|
if (!btn->launching && (!btn->running || (event->xbutton.state & ControlMask)))
|
|
launchDockedApplication(btn, False);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
|
|
{
|
|
WScreen *scr = dock->screen_ptr;
|
|
int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
|
|
int x, y;
|
|
XEvent ev;
|
|
int grabbed = 0, swapped = 0, done;
|
|
Pixmap ghost = None;
|
|
int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
|
|
|
|
if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
|
|
| ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
|
|
GrabModeAsync, None, None, CurrentTime) != GrabSuccess)
|
|
wwarning("pointer grab failed for dock move");
|
|
|
|
y = 0;
|
|
for (x = 0; x < dock->max_icons; x++) {
|
|
if (dock->icon_array[x] != NULL && dock->icon_array[x]->yindex > y)
|
|
y = dock->icon_array[x]->yindex;
|
|
}
|
|
y++;
|
|
XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE * y);
|
|
|
|
done = 0;
|
|
while (!done) {
|
|
WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
|
|
| ButtonMotionMask | ExposureMask | EnterWindowMask, &ev);
|
|
switch (ev.type) {
|
|
case Expose:
|
|
WMHandleEvent(&ev);
|
|
break;
|
|
|
|
case EnterNotify:
|
|
/* It means the cursor moved so fast that it entered
|
|
* something else (if moving slowly, it would have
|
|
* stayed in the dock that is being moved. Ignore such
|
|
* "spurious" EnterNotifiy's */
|
|
break;
|
|
|
|
case MotionNotify:
|
|
if (!grabbed) {
|
|
if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
|
|
|| abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
|
|
XChangeActivePointerGrab(dpy, ButtonMotionMask
|
|
| ButtonReleaseMask | ButtonPressMask,
|
|
wCursor[WCUR_MOVE], CurrentTime);
|
|
grabbed = 1;
|
|
}
|
|
break;
|
|
}
|
|
if (dock->type == WM_CLIP) {
|
|
x = ev.xmotion.x_root - ofs_x;
|
|
y = ev.xmotion.y_root - ofs_y;
|
|
wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
|
|
|
|
moveDock(dock, x, y);
|
|
} else {
|
|
/* move vertically if pointer is inside the dock */
|
|
if ((dock->on_right_side && ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
|
|
|| (!dock->on_right_side && ev.xmotion.x_root <= dock->x_pos + ICON_SIZE * 2)) {
|
|
|
|
x = ev.xmotion.x_root - ofs_x;
|
|
y = ev.xmotion.y_root - ofs_y;
|
|
wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
|
|
moveDock(dock, dock->x_pos, y);
|
|
}
|
|
/* move horizontally to change sides */
|
|
x = ev.xmotion.x_root - ofs_x;
|
|
if (!dock->on_right_side) {
|
|
|
|
/* is on left */
|
|
if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE * 2) {
|
|
XMoveWindow(dpy, scr->dock_shadow, scr->scr_width - ICON_SIZE
|
|
- DOCK_EXTRA_SPACE - 1, dock->y_pos);
|
|
if (superfluous && ghost == None) {
|
|
ghost = MakeGhostDock(dock, dock->x_pos,
|
|
scr->scr_width - ICON_SIZE
|
|
- DOCK_EXTRA_SPACE - 1, dock->y_pos);
|
|
XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
|
|
XClearWindow(dpy, scr->dock_shadow);
|
|
}
|
|
XMapRaised(dpy, scr->dock_shadow);
|
|
swapped = 1;
|
|
} else {
|
|
if (superfluous && ghost != None) {
|
|
XFreePixmap(dpy, ghost);
|
|
ghost = None;
|
|
}
|
|
XUnmapWindow(dpy, scr->dock_shadow);
|
|
swapped = 0;
|
|
}
|
|
} else {
|
|
/* is on right */
|
|
if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
|
|
XMoveWindow(dpy, scr->dock_shadow, DOCK_EXTRA_SPACE, dock->y_pos);
|
|
if (superfluous && ghost == None) {
|
|
ghost = MakeGhostDock(dock, dock->x_pos,
|
|
DOCK_EXTRA_SPACE, dock->y_pos);
|
|
XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
|
|
XClearWindow(dpy, scr->dock_shadow);
|
|
}
|
|
XMapRaised(dpy, scr->dock_shadow);
|
|
swapped = -1;
|
|
} else {
|
|
XUnmapWindow(dpy, scr->dock_shadow);
|
|
swapped = 0;
|
|
if (superfluous && ghost != None) {
|
|
XFreePixmap(dpy, ghost);
|
|
ghost = None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ButtonPress:
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
if (ev.xbutton.button != event->xbutton.button)
|
|
break;
|
|
XUngrabPointer(dpy, CurrentTime);
|
|
XUnmapWindow(dpy, scr->dock_shadow);
|
|
XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
|
|
if (dock->type == WM_DOCK) {
|
|
if (swapped != 0) {
|
|
if (swapped > 0)
|
|
dock->on_right_side = 1;
|
|
else
|
|
dock->on_right_side = 0;
|
|
swapDock(dock);
|
|
wArrangeIcons(scr, False);
|
|
}
|
|
}
|
|
done = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (superfluous) {
|
|
if (ghost != None)
|
|
XFreePixmap(dpy, ghost);
|
|
XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
|
|
}
|
|
}
|
|
|
|
static Bool handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
|
|
{
|
|
WScreen *scr = dock->screen_ptr;
|
|
Window wins[2];
|
|
WIcon *icon = aicon->icon;
|
|
WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
|
|
int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
|
|
XEvent ev;
|
|
int x = aicon->x_pos, y = aicon->y_pos;
|
|
int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
|
|
int shad_x = x, shad_y = y;
|
|
int ix = aicon->xindex, iy = aicon->yindex;
|
|
int tmp;
|
|
Pixmap ghost = None;
|
|
Bool docked;
|
|
int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
|
|
int omnipresent = aicon->omnipresent; /* this must be cached!!! */
|
|
Bool hasMoved = False;
|
|
|
|
if (wPreferences.flags.noupdates)
|
|
return hasMoved;
|
|
|
|
if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
|
|
| ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
|
|
GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
|
|
}
|
|
|
|
if (!(event->xbutton.state & MOD_MASK))
|
|
wRaiseFrame(icon->core);
|
|
|
|
if (!wPreferences.flags.noclip)
|
|
clip = scr->workspaces[scr->current_workspace]->clip;
|
|
|
|
if (dock == scr->dock && !wPreferences.flags.noclip)
|
|
dock2 = clip;
|
|
else if (dock != scr->dock && !wPreferences.flags.nodock)
|
|
dock2 = scr->dock;
|
|
|
|
wins[0] = icon->core->window;
|
|
wins[1] = scr->dock_shadow;
|
|
XRestackWindows(dpy, wins, 2);
|
|
XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE);
|
|
if (superfluous) {
|
|
if (icon->pixmap != None)
|
|
ghost = MakeGhostIcon(scr, icon->pixmap);
|
|
else
|
|
ghost = MakeGhostIcon(scr, icon->core->window);
|
|
|
|
XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
|
|
XClearWindow(dpy, scr->dock_shadow);
|
|
}
|
|
XMapWindow(dpy, scr->dock_shadow);
|
|
|
|
ondock = 1;
|
|
|
|
while (1) {
|
|
XMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask
|
|
| ButtonMotionMask | ExposureMask, &ev);
|
|
switch (ev.type) {
|
|
case Expose:
|
|
WMHandleEvent(&ev);
|
|
break;
|
|
|
|
case MotionNotify:
|
|
hasMoved = True;
|
|
if (!grabbed) {
|
|
if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD
|
|
|| abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) {
|
|
XChangeActivePointerGrab(dpy, ButtonMotionMask
|
|
| ButtonReleaseMask | ButtonPressMask,
|
|
wCursor[WCUR_MOVE], CurrentTime);
|
|
grabbed = 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (omnipresent) {
|
|
int i;
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
if (i == scr->current_workspace)
|
|
continue;
|
|
wDockShowIcons(scr->workspaces[i]->clip);
|
|
}
|
|
}
|
|
|
|
x = ev.xmotion.x_root - ofs_x;
|
|
y = ev.xmotion.y_root - ofs_y;
|
|
tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
|
|
if (tmp && dock2) {
|
|
change_dock = 0;
|
|
if (last_dock != dock && collapsed) {
|
|
last_dock->collapsed = 1;
|
|
wDockHideIcons(last_dock);
|
|
collapsed = 0;
|
|
}
|
|
if (!collapsed && (collapsed = dock->collapsed)) {
|
|
dock->collapsed = 0;
|
|
wDockShowIcons(dock);
|
|
}
|
|
if (dock->auto_raise_lower)
|
|
wDockRaise(dock);
|
|
last_dock = dock;
|
|
} else if (dock2) {
|
|
tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
|
|
if (tmp) {
|
|
change_dock = 1;
|
|
if (last_dock != dock2 && collapsed) {
|
|
last_dock->collapsed = 1;
|
|
wDockHideIcons(last_dock);
|
|
collapsed = 0;
|
|
}
|
|
if (!collapsed && (collapsed = dock2->collapsed)) {
|
|
dock2->collapsed = 0;
|
|
wDockShowIcons(dock2);
|
|
}
|
|
if (dock2->auto_raise_lower)
|
|
wDockRaise(dock2);
|
|
last_dock = dock2;
|
|
}
|
|
}
|
|
if (aicon->launching || aicon->lock || (aicon->running && !(ev.xmotion.state & MOD_MASK))
|
|
|| (!aicon->running && tmp)) {
|
|
shad_x = last_dock->x_pos + ix * wPreferences.icon_size;
|
|
shad_y = last_dock->y_pos + iy * wPreferences.icon_size;
|
|
|
|
XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
|
|
|
|
if (!ondock)
|
|
XMapWindow(dpy, scr->dock_shadow);
|
|
|
|
ondock = 1;
|
|
} else {
|
|
if (ondock)
|
|
XUnmapWindow(dpy, scr->dock_shadow);
|
|
|
|
ondock = 0;
|
|
}
|
|
XMoveWindow(dpy, icon->core->window, x, y);
|
|
break;
|
|
|
|
case ButtonPress:
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
if (ev.xbutton.button != event->xbutton.button)
|
|
break;
|
|
XUngrabPointer(dpy, CurrentTime);
|
|
if (ondock) {
|
|
SlideWindow(icon->core->window, x, y, shad_x, shad_y);
|
|
XUnmapWindow(dpy, scr->dock_shadow);
|
|
if (!change_dock) {
|
|
reattachIcon(dock, aicon, ix, iy);
|
|
if (clip && dock != clip && clip->auto_raise_lower)
|
|
wDockLower(clip);
|
|
} else {
|
|
docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
|
|
if (!docked) {
|
|
/* Slide it back if dock rejected it */
|
|
SlideWindow(icon->core->window, x, y, aicon->x_pos, aicon->y_pos);
|
|
reattachIcon(dock, aicon, aicon->xindex, aicon->yindex);
|
|
}
|
|
if (last_dock->type == WM_CLIP && last_dock->auto_collapse)
|
|
collapsed = 0;
|
|
}
|
|
} else {
|
|
aicon->x_pos = x;
|
|
aicon->y_pos = y;
|
|
if (superfluous) {
|
|
if (!aicon->running && !wPreferences.no_animations) {
|
|
/* We need to deselect it, even if is deselected in
|
|
* wDockDetach(), because else DoKaboom() will fail.
|
|
*/
|
|
if (aicon->icon->selected)
|
|
wIconSelect(aicon->icon);
|
|
|
|
DoKaboom(scr, aicon->icon->core->window, x, y);
|
|
}
|
|
}
|
|
if (clip && clip->auto_raise_lower)
|
|
wDockLower(clip);
|
|
wDockDetach(dock, aicon);
|
|
}
|
|
if (collapsed) {
|
|
last_dock->collapsed = 1;
|
|
wDockHideIcons(last_dock);
|
|
collapsed = 0;
|
|
}
|
|
if (superfluous) {
|
|
if (ghost != None)
|
|
XFreePixmap(dpy, ghost);
|
|
XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
|
|
}
|
|
if (omnipresent) {
|
|
int i;
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
if (i == scr->current_workspace)
|
|
continue;
|
|
wDockHideIcons(scr->workspaces[i]->clip);
|
|
}
|
|
}
|
|
return hasMoved;;
|
|
}
|
|
}
|
|
return False; /* never reached */
|
|
}
|
|
|
|
static int getClipButton(int px, int py)
|
|
{
|
|
int pt = (CLIP_BUTTON_SIZE + 2) * ICON_SIZE / 64;
|
|
|
|
if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
|
|
return CLIP_IDLE;
|
|
|
|
if (py <= pt - ((int)ICON_SIZE - 1 - px))
|
|
return CLIP_FORWARD;
|
|
else if (px <= pt - ((int)ICON_SIZE - 1 - py))
|
|
return CLIP_REWIND;
|
|
|
|
return CLIP_IDLE;
|
|
}
|
|
|
|
static void handleClipChangeWorkspace(WScreen *scr, XEvent *event)
|
|
{
|
|
XEvent ev;
|
|
int done, direction, new_ws;
|
|
int new_dir;
|
|
WDock *clip = scr->clip_icon->dock;
|
|
|
|
direction = getClipButton(event->xbutton.x, event->xbutton.y);
|
|
|
|
clip->lclip_button_pushed = direction == CLIP_REWIND;
|
|
clip->rclip_button_pushed = direction == CLIP_FORWARD;
|
|
|
|
wClipIconPaint(scr->clip_icon);
|
|
done = 0;
|
|
while (!done) {
|
|
WMMaskEvent(dpy, ExposureMask | ButtonMotionMask | ButtonReleaseMask | ButtonPressMask, &ev);
|
|
switch (ev.type) {
|
|
case Expose:
|
|
WMHandleEvent(&ev);
|
|
break;
|
|
|
|
case MotionNotify:
|
|
new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
|
|
if (new_dir != direction) {
|
|
direction = new_dir;
|
|
clip->lclip_button_pushed = direction == CLIP_REWIND;
|
|
clip->rclip_button_pushed = direction == CLIP_FORWARD;
|
|
wClipIconPaint(scr->clip_icon);
|
|
}
|
|
break;
|
|
|
|
case ButtonPress:
|
|
break;
|
|
|
|
case ButtonRelease:
|
|
if (ev.xbutton.button == event->xbutton.button)
|
|
done = 1;
|
|
}
|
|
}
|
|
|
|
clip->lclip_button_pushed = 0;
|
|
clip->rclip_button_pushed = 0;
|
|
|
|
new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
|
|
|
|
if (direction == CLIP_FORWARD) {
|
|
if (scr->current_workspace < scr->workspace_count - 1)
|
|
wWorkspaceChange(scr, scr->current_workspace + 1);
|
|
else if (new_ws && scr->current_workspace < MAX_WORKSPACES - 1)
|
|
wWorkspaceChange(scr, scr->current_workspace + 1);
|
|
else if (wPreferences.ws_cycle)
|
|
wWorkspaceChange(scr, 0);
|
|
} else if (direction == CLIP_REWIND) {
|
|
if (scr->current_workspace > 0)
|
|
wWorkspaceChange(scr, scr->current_workspace - 1);
|
|
else if (scr->current_workspace == 0 && wPreferences.ws_cycle)
|
|
wWorkspaceChange(scr, scr->workspace_count - 1);
|
|
}
|
|
|
|
wClipIconPaint(scr->clip_icon);
|
|
}
|
|
|
|
static void iconMouseDown(WObjDescriptor *desc, XEvent *event)
|
|
{
|
|
WAppIcon *aicon = desc->parent;
|
|
WDock *dock = aicon->dock;
|
|
WScreen *scr = aicon->icon->core->screen_ptr;
|
|
|
|
if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
|
|
return;
|
|
|
|
scr->last_dock = dock;
|
|
|
|
if (dock->menu->flags.mapped)
|
|
wMenuUnmap(dock->menu);
|
|
|
|
if (IsDoubleClick(scr, event)) {
|
|
/* double-click was not in the main clip icon */
|
|
if (dock->type != WM_CLIP || aicon->xindex != 0 || aicon->yindex != 0
|
|
|| getClipButton(event->xbutton.x, event->xbutton.y) == CLIP_IDLE) {
|
|
iconDblClick(desc, event);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (event->xbutton.button == Button1) {
|
|
if (event->xbutton.state & MOD_MASK)
|
|
wDockLower(dock);
|
|
else
|
|
wDockRaise(dock);
|
|
|
|
if ((event->xbutton.state & ShiftMask) && aicon != scr->clip_icon && dock->type != WM_DOCK) {
|
|
wIconSelect(aicon->icon);
|
|
return;
|
|
}
|
|
|
|
if (aicon->yindex == 0 && aicon->xindex == 0) {
|
|
if (getClipButton(event->xbutton.x, event->xbutton.y) != CLIP_IDLE
|
|
&& dock->type == WM_CLIP)
|
|
handleClipChangeWorkspace(scr, event);
|
|
else
|
|
handleDockMove(dock, aicon, event);
|
|
} else {
|
|
Bool hasMoved = handleIconMove(dock, aicon, event);
|
|
if (wPreferences.single_click && !hasMoved)
|
|
iconDblClick(desc, event);
|
|
}
|
|
} else if (event->xbutton.button == Button2 && dock->type == WM_CLIP && aicon == scr->clip_icon) {
|
|
if (!scr->clip_ws_menu) {
|
|
scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
|
|
}
|
|
if (scr->clip_ws_menu) {
|
|
WMenu *wsMenu = scr->clip_ws_menu;
|
|
int xpos;
|
|
|
|
wWorkspaceMenuUpdate(scr, wsMenu);
|
|
|
|
xpos = event->xbutton.x_root - wsMenu->frame->core->width / 2 - 1;
|
|
if (xpos < 0) {
|
|
xpos = 0;
|
|
} else if (xpos + wsMenu->frame->core->width > scr->scr_width - 2) {
|
|
xpos = scr->scr_width - wsMenu->frame->core->width - 4;
|
|
}
|
|
wMenuMapAt(wsMenu, xpos, event->xbutton.y_root + 2, False);
|
|
|
|
desc = &wsMenu->menu->descriptor;
|
|
event->xany.send_event = True;
|
|
(*desc->handle_mousedown) (desc, event);
|
|
}
|
|
} else if (event->xbutton.button == Button2 && dock->type == WM_CLIP &&
|
|
(event->xbutton.state & ShiftMask) && aicon != scr->clip_icon) {
|
|
wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
|
|
} else if (event->xbutton.button == Button3) {
|
|
if (event->xbutton.send_event &&
|
|
XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
|
|
| ButtonReleaseMask | ButtonPressMask, GrabModeAsync,
|
|
GrabModeAsync, None, None, CurrentTime) != GrabSuccess) {
|
|
wwarning("pointer grab failed for dockicon menu");
|
|
return;
|
|
}
|
|
|
|
openDockMenu(dock, aicon, event);
|
|
} else if (event->xbutton.button == Button2) {
|
|
WAppIcon *btn = desc->parent;
|
|
|
|
if (!btn->launching && (!btn->running || (event->xbutton.state & ControlMask)))
|
|
launchDockedApplication(btn, True);
|
|
}
|
|
}
|
|
|
|
static void clipEnterNotify(WObjDescriptor *desc, XEvent *event)
|
|
{
|
|
WAppIcon *btn = (WAppIcon *) desc->parent;
|
|
WDock *dock;
|
|
WScreen *scr;
|
|
|
|
assert(event->type == EnterNotify);
|
|
|
|
if (desc->parent_type != WCLASS_DOCK_ICON)
|
|
return;
|
|
|
|
scr = btn->icon->core->screen_ptr;
|
|
if (!btn->omnipresent)
|
|
dock = btn->dock;
|
|
else
|
|
dock = scr->workspaces[scr->current_workspace]->clip;
|
|
|
|
if (!dock || dock->type != WM_CLIP)
|
|
return;
|
|
|
|
/* The auto raise/lower code */
|
|
if (dock->auto_lower_magic) {
|
|
WMDeleteTimerHandler(dock->auto_lower_magic);
|
|
dock->auto_lower_magic = NULL;
|
|
}
|
|
if (dock->auto_raise_lower && !dock->auto_raise_magic)
|
|
dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY, clipAutoRaise, (void *)dock);
|
|
|
|
/* The auto expand/collapse code */
|
|
if (dock->auto_collapse_magic) {
|
|
WMDeleteTimerHandler(dock->auto_collapse_magic);
|
|
dock->auto_collapse_magic = NULL;
|
|
}
|
|
if (dock->auto_collapse && !dock->auto_expand_magic)
|
|
dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY, clipAutoExpand, (void *)dock);
|
|
}
|
|
|
|
static void clipLeave(WDock *dock)
|
|
{
|
|
XEvent event;
|
|
WObjDescriptor *desc = NULL;
|
|
|
|
if (!dock || dock->type != WM_CLIP)
|
|
return;
|
|
|
|
if (XCheckTypedEvent(dpy, EnterNotify, &event) != False) {
|
|
if (XFindContext(dpy, event.xcrossing.window, wWinContext,
|
|
(XPointer *) & desc) != XCNOENT
|
|
&& desc && desc->parent_type == WCLASS_DOCK_ICON
|
|
&& ((WAppIcon *) desc->parent)->dock && ((WAppIcon *) desc->parent)->dock->type == WM_CLIP) {
|
|
/* We didn't left the Clip yet */
|
|
XPutBackEvent(dpy, &event);
|
|
return;
|
|
}
|
|
|
|
XPutBackEvent(dpy, &event);
|
|
} else {
|
|
/* We entered a withdrawn window, so we're still in Clip */
|
|
return;
|
|
}
|
|
|
|
if (dock->auto_raise_magic) {
|
|
WMDeleteTimerHandler(dock->auto_raise_magic);
|
|
dock->auto_raise_magic = NULL;
|
|
}
|
|
if (dock->auto_raise_lower && !dock->auto_lower_magic)
|
|
dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY, clipAutoLower, (void *)dock);
|
|
|
|
if (dock->auto_expand_magic) {
|
|
WMDeleteTimerHandler(dock->auto_expand_magic);
|
|
dock->auto_expand_magic = NULL;
|
|
}
|
|
if (dock->auto_collapse && !dock->auto_collapse_magic)
|
|
dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY, clipAutoCollapse, (void *)dock);
|
|
}
|
|
|
|
static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
|
|
{
|
|
WAppIcon *btn = (WAppIcon *) desc->parent;
|
|
|
|
assert(event->type == LeaveNotify);
|
|
|
|
if (desc->parent_type != WCLASS_DOCK_ICON)
|
|
return;
|
|
|
|
clipLeave(btn->dock);
|
|
}
|
|
|
|
static void clipAutoCollapse(void *cdata)
|
|
{
|
|
WDock *dock = (WDock *) cdata;
|
|
|
|
if (dock->type != WM_CLIP)
|
|
return;
|
|
|
|
if (dock->auto_collapse) {
|
|
dock->collapsed = 1;
|
|
wDockHideIcons(dock);
|
|
}
|
|
dock->auto_collapse_magic = NULL;
|
|
}
|
|
|
|
static void clipAutoExpand(void *cdata)
|
|
{
|
|
WDock *dock = (WDock *) cdata;
|
|
|
|
if (dock->type != WM_CLIP)
|
|
return;
|
|
|
|
if (dock->auto_collapse) {
|
|
dock->collapsed = 0;
|
|
wDockShowIcons(dock);
|
|
}
|
|
dock->auto_expand_magic = NULL;
|
|
}
|
|
|
|
static void clipAutoLower(void *cdata)
|
|
{
|
|
WDock *dock = (WDock *) cdata;
|
|
|
|
if (dock->type != WM_CLIP)
|
|
return;
|
|
|
|
if (dock->auto_raise_lower)
|
|
wDockLower(dock);
|
|
|
|
dock->auto_lower_magic = NULL;
|
|
}
|
|
|
|
static void clipAutoRaise(void *cdata)
|
|
{
|
|
WDock *dock = (WDock *) cdata;
|
|
|
|
if (dock->type != WM_CLIP)
|
|
return;
|
|
|
|
if (dock->auto_raise_lower)
|
|
wDockRaise(dock);
|
|
|
|
dock->auto_raise_magic = NULL;
|
|
}
|
|
|
|
static Bool iconCanBeOmnipresent(WAppIcon *aicon)
|
|
{
|
|
WScreen *scr = aicon->icon->core->screen_ptr;
|
|
WDock *clip;
|
|
WAppIcon *btn;
|
|
int i, j;
|
|
|
|
for (i = 0; i < scr->workspace_count; i++) {
|
|
clip = scr->workspaces[i]->clip;
|
|
|
|
if (clip == aicon->dock)
|
|
continue;
|
|
|
|
if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
|
|
return False; /* Clip is full in some workspace */
|
|
|
|
for (j = 0; j < clip->max_icons; j++) {
|
|
btn = clip->icon_array[j];
|
|
if (btn && btn->xindex == aicon->xindex && btn->yindex == aicon->yindex)
|
|
return False;
|
|
}
|
|
}
|
|
|
|
return True;
|
|
}
|
|
|
|
int wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
|
|
{
|
|
WScreen *scr = aicon->icon->core->screen_ptr;
|
|
WAppIconChain *new_entry, *tmp, *tmp1;
|
|
int status = WO_SUCCESS;
|
|
|
|
if ((scr->dock && aicon->dock == scr->dock) || aicon == scr->clip_icon)
|
|
return WO_NOT_APPLICABLE;
|
|
|
|
if (aicon->omnipresent == omnipresent)
|
|
return WO_SUCCESS;
|
|
|
|
if (omnipresent) {
|
|
if (iconCanBeOmnipresent(aicon)) {
|
|
aicon->omnipresent = 1;
|
|
new_entry = wmalloc(sizeof(WAppIconChain));
|
|
new_entry->aicon = aicon;
|
|
new_entry->next = scr->global_icons;
|
|
scr->global_icons = new_entry;
|
|
scr->global_icon_count++;
|
|
} else {
|
|
aicon->omnipresent = 0;
|
|
status = WO_FAILED;
|
|
}
|
|
} else {
|
|
aicon->omnipresent = 0;
|
|
if (aicon == scr->global_icons->aicon) {
|
|
tmp = scr->global_icons->next;
|
|
wfree(scr->global_icons);
|
|
scr->global_icons = tmp;
|
|
scr->global_icon_count--;
|
|
} else {
|
|
tmp = scr->global_icons;
|
|
while (tmp->next) {
|
|
if (tmp->next->aicon == aicon) {
|
|
tmp1 = tmp->next->next;
|
|
wfree(tmp->next);
|
|
tmp->next = tmp1;
|
|
scr->global_icon_count--;
|
|
break;
|
|
}
|
|
tmp = tmp->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
wAppIconPaint(aicon);
|
|
|
|
return status;
|
|
}
|