changed indentation to use spaces only
[wmaker-crm.git] / src / dock.c
blob37e6f2c817a94a3fdd70d953c94333ab13ee330c
1 /* dock.c- built-in Dock module for WindowMaker
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
21 * USA.
25 #include "wconfig.h"
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <math.h>
33 #include <limits.h>
35 #ifndef PATH_MAX
36 #define PATH_MAX DEFAULT_PATH_MAX
37 #endif
39 #include "WindowMaker.h"
40 #include "wcore.h"
41 #include "window.h"
42 #include "icon.h"
43 #include "appicon.h"
44 #include "actions.h"
45 #include "stacking.h"
46 #include "dock.h"
47 #include "dialog.h"
48 #include "funcs.h"
49 #include "properties.h"
50 #include "menu.h"
51 #include "client.h"
52 #include "defaults.h"
53 #include "workspace.h"
54 #include "framewin.h"
55 #include "superfluous.h"
56 #include "wsound.h"
57 #include "xinerama.h"
62 /**** Local variables ****/
63 #define CLIP_REWIND 1
64 #define CLIP_IDLE 0
65 #define CLIP_FORWARD 2
68 /**** Global variables ****/
70 /* in dockedapp.c */
71 extern void DestroyDockAppSettingsPanel();
73 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
76 extern XContext wWinContext;
78 extern Cursor wCursor[WCUR_LAST];
80 extern WPreferences wPreferences;
82 extern XContext wWinContext;
84 #ifdef OFFIX_DND
85 extern Atom _XA_DND_PROTOCOL;
86 #endif
89 #define MOD_MASK wPreferences.modifier_mask
91 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
93 #define ICON_SIZE wPreferences.icon_size
96 /***** Local variables ****/
98 static WMPropList *dCommand=NULL;
99 static WMPropList *dPasteCommand=NULL;
100 #ifdef OFFIX_DND
101 static WMPropList *dDropCommand=NULL;
102 #endif
103 static WMPropList *dAutoLaunch, *dLock;
104 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
105 static WMPropList *dHost, *dDock, *dClip;
106 static WMPropList *dAutoAttractIcons;
108 static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed;
110 static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent;
112 static void dockIconPaint(WAppIcon *btn);
114 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
116 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
118 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
120 static int getClipButton(int px, int py);
122 static void toggleLowered(WDock *dock);
124 static void toggleCollapsed(WDock *dock);
126 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
128 static void clipLeave(WDock *dock);
130 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
132 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
134 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
135 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
136 static void clipAutoCollapse(void *cdata);
137 static void clipAutoExpand(void *cdata);
138 static void launchDockedApplication(WAppIcon *btn, Bool withSelection);
140 static void clipAutoLower(void *cdata);
141 static void clipAutoRaise(void *cdata);
143 static void showClipBalloon(WDock *dock, int workspace);
145 #ifdef OFFIX_DND
147 #define DndNotDnd -1
148 #define DndUnknown 0
149 #define DndRawData 1
150 #define DndFile 2
151 #define DndFiles 3
152 #define DndText 4
153 #define DndDir 5
154 #define DndLink 6
155 #define DndExe 7
157 #define DndEND 8
159 #endif /* OFFIX_DND */
163 static void
164 make_keys()
166 if (dCommand!=NULL)
167 return;
169 dCommand = WMRetainPropList(WMCreatePLString("Command"));
170 dPasteCommand = WMRetainPropList(WMCreatePLString("PasteCommand"));
171 dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
172 dLock = WMRetainPropList(WMCreatePLString("Lock"));
173 dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
174 dName = WMRetainPropList(WMCreatePLString("Name"));
175 dForced = WMRetainPropList(WMCreatePLString("Forced"));
176 dBuggyApplication = WMRetainPropList(WMCreatePLString("BuggyApplication"));
177 dYes = WMRetainPropList(WMCreatePLString("Yes"));
178 dNo = WMRetainPropList(WMCreatePLString("No"));
179 dHost = WMRetainPropList(WMCreatePLString("Host"));
181 dPosition = WMCreatePLString("Position");
182 dApplications = WMCreatePLString("Applications");
183 dLowered = WMCreatePLString("Lowered");
184 dCollapsed = WMCreatePLString("Collapsed");
185 dAutoCollapse = WMCreatePLString("AutoCollapse");
186 dAutoRaiseLower = WMCreatePLString("AutoRaiseLower");
187 dAutoAttractIcons = WMCreatePLString("AutoAttractIcons");
189 dOmnipresent = WMCreatePLString("Omnipresent");
191 dDock = WMCreatePLString("Dock");
192 dClip = WMCreatePLString("Clip");
197 static void
198 renameCallback(WMenu *menu, WMenuEntry *entry)
200 WDock *dock = entry->clientdata;
201 char buffer[128];
202 int wspace;
203 char *name;
205 assert(entry->clientdata!=NULL);
207 wspace = dock->screen_ptr->current_workspace;
209 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
211 snprintf(buffer, sizeof(buffer), _("Type the name for workspace %i:"), wspace+1);
212 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
213 &name)) {
214 wWorkspaceRename(dock->screen_ptr, wspace, name);
216 if (name) {
217 wfree(name);
222 static void
223 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
225 assert(entry->clientdata!=NULL);
227 toggleLowered(entry->clientdata);
229 entry->flags.indicator_on = !(((WDock*)entry->clientdata)->lowered);
231 wMenuPaint(menu);
235 static int
236 matchWindow(void *item, void *cdata)
238 return (((WFakeGroupLeader*)item)->leader == (Window)cdata);
242 static void
243 killCallback(WMenu *menu, WMenuEntry *entry)
245 WScreen *scr = menu->menu->screen_ptr;
246 WAppIcon *icon;
247 WFakeGroupLeader *fPtr;
248 char *buffer;
250 if (!WCHECK_STATE(WSTATE_NORMAL))
251 return;
253 assert(entry->clientdata!=NULL);
255 icon = (WAppIcon*)entry->clientdata;
257 icon->editing = 1;
259 WCHANGE_STATE(WSTATE_MODAL);
261 buffer = wstrconcat(icon->wm_class,
262 _(" will be forcibly closed.\n"
263 "Any unsaved changes will be lost.\n"
264 "Please confirm."));
266 if (icon->icon && icon->icon->owner) {
267 fPtr = icon->icon->owner->fake_group;
268 } else {
269 /* is this really necessary? can we kill a non-running dock icon? */
270 Window win = icon->main_window;
271 int index;
273 index = WMFindInArray(scr->fakeGroupLeaders, matchWindow, (void*)win);
274 if (index != WANotFound)
275 fPtr = WMGetFromArray(scr->fakeGroupLeaders, index);
276 else
277 fPtr = NULL;
280 if (wPreferences.dont_confirm_kill
281 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
282 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
283 if (fPtr!=NULL) {
284 WWindow *wwin, *twin;
286 wwin = scr->focused_window;
287 while (wwin) {
288 twin = wwin->prev;
289 if (wwin->fake_group == fPtr) {
290 wClientKill(wwin);
292 wwin = twin;
294 } else if (icon->icon && icon->icon->owner) {
295 wClientKill(icon->icon->owner);
299 wfree(buffer);
301 icon->editing = 0;
303 WCHANGE_STATE(WSTATE_NORMAL);
307 /* TODO: replace this function with a member of the dock struct */
308 static int
309 numberOfSelectedIcons(WDock *dock)
311 WAppIcon *aicon;
312 int i, n;
314 n = 0;
315 for (i=1; i<dock->max_icons; i++) {
316 aicon = dock->icon_array[i];
317 if (aicon && aicon->icon->selected) {
318 n++;
322 return n;
326 static WMArray*
327 getSelected(WDock *dock)
329 WMArray *ret = WMCreateArray(8);
330 WAppIcon *btn;
331 int i;
333 for (i=1; i<dock->max_icons; i++) {
334 btn = dock->icon_array[i];
335 if (btn && btn->icon->selected) {
336 WMAddToArray(ret, btn);
340 return ret;
344 static void
345 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
347 Window win = clipIcon->icon->core->window;
348 WScreen *scr = clipIcon->icon->core->screen_ptr;
349 XPoint p[4];
350 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
351 int tp = ICON_SIZE - pt;
352 int as = pt - 15; /* 15 = 5+5+5 */
353 GC gc = scr->draw_gc; /* maybe use WMColorGC() instead here? */
354 WMColor *color;
355 #ifdef GRADIENT_CLIP_ARROW
356 Bool collapsed = clipIcon->dock->collapsed;
357 #endif
359 //if (!clipIcon->dock->collapsed)
360 // color = scr->clip_title_color[CLIP_NORMAL];
361 //else
362 // color = scr->clip_title_color[CLIP_COLLAPSED];
363 color = scr->clip_title_color[CLIP_NORMAL];
365 XSetForeground(dpy, gc, WMColorPixel(color));
367 if (rpushed) {
368 p[0].x = tp+1;
369 p[0].y = 1;
370 p[1].x = ICON_SIZE-2;
371 p[1].y = 1;
372 p[2].x = ICON_SIZE-2;
373 p[2].y = pt-1;
374 } else if (lpushed) {
375 p[0].x = 1;
376 p[0].y = tp;
377 p[1].x = pt;
378 p[1].y = ICON_SIZE-2;
379 p[2].x = 1;
380 p[2].y = ICON_SIZE-2;
382 if (lpushed || rpushed) {
383 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
384 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
385 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
387 #ifdef GRADIENT_CLIP_ARROW
388 if (!collapsed) {
389 XSetFillStyle(dpy, scr->copy_gc, FillTiled);
390 XSetTile(dpy, scr->copy_gc, scr->clip_arrow_gradient);
391 XSetClipMask(dpy, scr->copy_gc, None);
392 gc = scr->copy_gc;
394 #endif /* GRADIENT_CLIP_ARROW */
396 /* top right arrow */
397 p[0].x = p[3].x = ICON_SIZE-5-as;
398 p[0].y = p[3].y = 5;
399 p[1].x = ICON_SIZE-6;
400 p[1].y = 5;
401 p[2].x = ICON_SIZE-6;
402 p[2].y = 4+as;
403 if (rpushed) {
404 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
405 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
406 } else {
407 #ifdef GRADIENT_CLIP_ARROW
408 if (!collapsed)
409 XSetTSOrigin(dpy, gc, ICON_SIZE-6-as, 5);
410 #endif
411 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
412 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
415 /* bottom left arrow */
416 p[0].x = p[3].x = 5;
417 p[0].y = p[3].y = ICON_SIZE-5-as;
418 p[1].x = 5;
419 p[1].y = ICON_SIZE-6;
420 p[2].x = 4+as;
421 p[2].y = ICON_SIZE-6;
422 if (lpushed) {
423 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
424 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
425 } else {
426 #ifdef GRADIENT_CLIP_ARROW
427 if (!collapsed)
428 XSetTSOrigin(dpy, gc, 5, ICON_SIZE-6-as);
429 #endif
430 XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin);
431 XDrawLines(dpy, win, gc, p,4,CoordModeOrigin);
433 #ifdef GRADIENT_CLIP_ARROW
434 if (!collapsed)
435 XSetFillStyle(dpy, scr->copy_gc, FillSolid);
436 #endif
440 RImage*
441 wClipMakeTile(WScreen *scr, RImage *normalTile)
443 RImage *tile = RCloneImage(normalTile);
444 RColor black;
445 RColor dark;
446 RColor light;
447 int pt, tp;
448 int as;
450 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
451 tp = wPreferences.icon_size-1 - pt;
452 as = pt - 15;
454 black.alpha = 255;
455 black.red = black.green = black.blue = 0;
457 dark.alpha = 0;
458 dark.red = dark.green = dark.blue = 60;
460 light.alpha = 0;
461 light.red = light.green = light.blue = 80;
464 /* top right */
465 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
466 pt-1, &dark);
467 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
468 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
469 pt, &light);
471 /* arrow bevel */
472 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 7 - as, 4,
473 ICON_SIZE - 5, 4, &dark);
474 ROperateLine(tile, RSubtractOperation, ICON_SIZE - 6 - as, 5,
475 ICON_SIZE - 5, 6 + as, &dark);
476 ROperateLine(tile, RAddOperation, ICON_SIZE - 5, 4, ICON_SIZE - 5, 6 + as,
477 &light);
479 /* bottom left */
480 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
481 wPreferences.icon_size-3, &dark);
482 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
483 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
484 wPreferences.icon_size-2, &light);
486 /* arrow bevel */
487 ROperateLine(tile, RSubtractOperation, 4, ICON_SIZE - 7 - as, 4,
488 ICON_SIZE - 5, &dark);
489 ROperateLine(tile, RSubtractOperation, 5, ICON_SIZE - 6 - as,
490 6 + as, ICON_SIZE - 5, &dark);
491 ROperateLine(tile, RAddOperation, 4, ICON_SIZE - 5, 6 + as, ICON_SIZE - 5,
492 &light);
494 return tile;
498 static void
499 omnipresentCallback(WMenu *menu, WMenuEntry *entry)
501 WAppIcon *clickedIcon = entry->clientdata;
502 WAppIcon *aicon;
503 WDock *dock;
504 WMArray *selectedIcons;
505 WMArrayIterator iter;
506 int failed;
508 assert(entry->clientdata!=NULL);
510 dock = clickedIcon->dock;
512 selectedIcons = getSelected(dock);
514 if (!WMGetArrayItemCount(selectedIcons))
515 WMAddToArray(selectedIcons, clickedIcon);
517 failed = 0;
518 WM_ITERATE_ARRAY(selectedIcons, aicon, iter) {
519 if (wClipMakeIconOmnipresent(aicon, !aicon->omnipresent) == WO_FAILED)
520 failed++;
521 else if (aicon->icon->selected)
522 wIconSelect(aicon->icon);
524 WMFreeArray(selectedIcons);
526 if (failed > 1) {
527 wMessageDialog(dock->screen_ptr, _("Warning"),
528 _("Some icons cannot be made omnipresent. "
529 "Please make sure that no other icon is "
530 "docked in the same positions on the other "
531 "workspaces and the Clip is not full in "
532 "some workspace."),
533 _("OK"), NULL, NULL);
534 } else if (failed == 1) {
535 wMessageDialog(dock->screen_ptr, _("Warning"),
536 _("Icon cannot be made omnipresent. "
537 "Please make sure that no other icon is "
538 "docked in the same position on the other "
539 "workspaces and the Clip is not full in "
540 "some workspace."),
541 _("OK"), NULL, NULL);
546 static void
547 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
549 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
550 WDock *dock;
551 WAppIcon *aicon;
552 WMArray *selectedIcons;
553 int keepit;
554 WMArrayIterator it;
556 assert(clickedIcon!=NULL);
558 dock = clickedIcon->dock;
560 selectedIcons = getSelected(dock);
562 if (WMGetArrayItemCount(selectedIcons)) {
563 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
564 _("All selected icons will be removed!"),
565 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
566 WMFreeArray(selectedIcons);
567 return;
569 } else {
570 if (clickedIcon->xindex==0 && clickedIcon->yindex==0) {
571 WMFreeArray(selectedIcons);
572 return;
574 WMAddToArray(selectedIcons, clickedIcon);
577 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
578 keepit = aicon->running && wApplicationOf(aicon->main_window);
579 wDockDetach(dock, aicon);
580 if (keepit) {
581 /* XXX: can: aicon->icon == NULL ? */
582 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
583 XMoveWindow(dpy, aicon->icon->core->window,
584 aicon->x_pos, aicon->y_pos);
585 if (!dock->mapped || dock->collapsed)
586 XMapWindow(dpy, aicon->icon->core->window);
589 WMFreeArray(selectedIcons);
591 if (wPreferences.auto_arrange_icons)
592 wArrangeIcons(dock->screen_ptr, True);
596 static void
597 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
599 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
600 WDock *dock;
601 WAppIcon *aicon;
602 WMArray *selectedIcons;
603 WMArrayIterator it;
605 assert(clickedIcon!=NULL);
606 dock = clickedIcon->dock;
608 selectedIcons = getSelected(dock);
610 if (!WMGetArrayItemCount(selectedIcons)
611 && clickedIcon!=dock->screen_ptr->clip_icon) {
612 char *command = NULL;
614 if (!clickedIcon->command && !clickedIcon->editing) {
615 clickedIcon->editing = 1;
616 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
617 _("Type the command used to launch the application"),
618 &command)) {
619 if (command && (command[0]==0 ||
620 (command[0]=='-' && command[1]==0))) {
621 wfree(command);
622 command = NULL;
624 clickedIcon->command = command;
625 clickedIcon->editing = 0;
626 } else {
627 clickedIcon->editing = 0;
628 if (command)
629 wfree(command);
630 WMFreeArray(selectedIcons);
631 return;
635 WMAddToArray(selectedIcons, clickedIcon);
638 WM_ITERATE_ARRAY(selectedIcons, aicon, it) {
639 if (aicon->icon->selected)
640 wIconSelect(aicon->icon);
641 if (aicon && aicon->attracted && aicon->command) {
642 aicon->attracted = 0;
643 if (aicon->icon->shadowed) {
644 aicon->icon->shadowed = 0;
645 aicon->icon->force_paint = 1;
646 wAppIconPaint(aicon);
650 WMFreeArray(selectedIcons);
656 static void
657 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
659 WDock *dock = (WDock*)entry->clientdata;
661 assert(entry->clientdata!=NULL);
663 dock->attract_icons = !dock->attract_icons;
664 /*if (!dock->attract_icons)
665 dock->keep_attracted = 0;*/
667 entry->flags.indicator_on = dock->attract_icons;
669 wMenuPaint(menu);
673 static void
674 selectCallback(WMenu *menu, WMenuEntry *entry)
676 WAppIcon *icon = (WAppIcon*)entry->clientdata;
678 assert(icon!=NULL);
680 wIconSelect(icon->icon);
682 wMenuPaint(menu);
686 static void
687 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
689 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
690 WDock *clip;
691 WAppIcon *aicon;
692 int x, y, x_pos, y_pos;
694 assert(entry->clientdata!=NULL);
695 clip = clickedIcon->dock;
697 aicon = clip->screen_ptr->app_icon_list;
699 while (aicon) {
700 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
701 x_pos = clip->x_pos + x*ICON_SIZE;
702 y_pos = clip->y_pos + y*ICON_SIZE;
703 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
704 #ifdef ANIMATIONS
705 if (wPreferences.no_animations) {
706 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
707 } else {
708 SlideWindow(aicon->icon->core->window,
709 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
711 #else
712 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
713 #endif /* ANIMATIONS */
715 aicon->attracted = 1;
716 if (!aicon->icon->shadowed) {
717 aicon->icon->shadowed = 1;
718 aicon->icon->force_paint = 1;
719 /* We don't do an wAppIconPaint() here because it's in
720 * wDockAttachIcon(). -Dan
723 wDockAttachIcon(clip, aicon, x, y);
724 if (clip->collapsed || !clip->mapped)
725 XUnmapWindow(dpy, aicon->icon->core->window);
727 aicon = aicon->next;
732 static void
733 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
735 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
736 WDock *dock;
737 WMArray *selectedIcons;
738 WMArrayIterator iter;
739 WAppIcon *btn;
740 int i;
742 assert(clickedIcon!=NULL);
743 dock = clickedIcon->dock;
745 selectedIcons = getSelected(dock);
747 if (!WMGetArrayItemCount(selectedIcons)) {
748 for (i=1; i<dock->max_icons; i++) {
749 btn = dock->icon_array[i];
750 if (btn && !btn->icon->selected) {
751 wIconSelect(btn->icon);
754 } else {
755 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
756 wIconSelect(btn->icon);
759 WMFreeArray(selectedIcons);
761 wMenuPaint(menu);
765 static void
766 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
768 assert(entry->clientdata!=NULL);
770 toggleCollapsed(entry->clientdata);
772 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
774 wMenuPaint(menu);
778 static void
779 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
781 WDock *dock;
782 assert(entry->clientdata!=NULL);
784 dock = (WDock*) entry->clientdata;
786 dock->auto_collapse = !dock->auto_collapse;
788 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
790 wMenuPaint(menu);
794 static void
795 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
797 WDock *dock;
798 assert(entry->clientdata!=NULL);
800 dock = (WDock*) entry->clientdata;
802 dock->auto_raise_lower = !dock->auto_raise_lower;
804 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
806 wMenuPaint(menu);
810 static void
811 launchCallback(WMenu *menu, WMenuEntry *entry)
813 WAppIcon *btn = (WAppIcon*)entry->clientdata;
815 launchDockedApplication(btn, False);
819 static void
820 settingsCallback(WMenu *menu, WMenuEntry *entry)
822 WAppIcon *btn = (WAppIcon*)entry->clientdata;
824 if (btn->editing)
825 return;
826 ShowDockAppSettingsPanel(btn);
830 static void
831 hideCallback(WMenu *menu, WMenuEntry *entry)
833 WApplication *wapp;
834 WAppIcon *btn = (WAppIcon*)entry->clientdata;
836 wapp = wApplicationOf(btn->icon->owner->main_window);
838 if (wapp->flags.hidden) {
839 wWorkspaceChange(btn->icon->core->screen_ptr, wapp->last_workspace);
840 wUnhideApplication(wapp, False, False);
841 } else {
842 wHideApplication(wapp);
847 static void
848 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
850 WApplication *wapp;
851 WAppIcon *btn = (WAppIcon*)entry->clientdata;
853 wapp = wApplicationOf(btn->icon->owner->main_window);
855 wUnhideApplication(wapp, False, True);
859 WAppIcon*
860 mainIconCreate(WScreen *scr, int type)
862 WAppIcon *btn;
863 int x_pos;
865 if (type == WM_CLIP) {
866 if (scr->clip_icon)
867 return scr->clip_icon;
868 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
869 btn->icon->core->descriptor.handle_expose = clipIconExpose;
870 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
871 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
872 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
873 x_pos = 0;
874 } else {
875 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
876 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
879 btn->xindex = 0;
880 btn->yindex = 0;
882 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
883 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
884 btn->icon->core->descriptor.parent = btn;
885 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
886 XMapWindow(dpy, btn->icon->core->window);
887 btn->x_pos = x_pos;
888 btn->y_pos = 0;
889 btn->docked = 1;
890 if (type == WM_CLIP)
891 scr->clip_icon = btn;
893 return btn;
897 static void
898 switchWSCommand(WMenu *menu, WMenuEntry *entry)
900 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
901 WScreen *scr = icon->icon->core->screen_ptr;
902 WDock *src, *dest;
903 WMArray *selectedIcons;
904 int x, y;
906 if (entry->order == scr->current_workspace)
907 return;
908 src = icon->dock;
909 dest = scr->workspaces[entry->order]->clip;
911 selectedIcons = getSelected(src);
913 if (WMGetArrayItemCount(selectedIcons)) {
914 WMArrayIterator iter;
916 WM_ITERATE_ARRAY(selectedIcons, btn, iter) {
917 if (wDockFindFreeSlot(dest, &x, &y)) {
918 moveIconBetweenDocks(src, dest, btn, x, y);
919 XUnmapWindow(dpy, btn->icon->core->window);
922 } else if (icon != scr->clip_icon) {
923 if (wDockFindFreeSlot(dest, &x, &y)) {
924 moveIconBetweenDocks(src, dest, icon, x, y);
925 XUnmapWindow(dpy, icon->icon->core->window);
928 WMFreeArray(selectedIcons);
933 static void
934 launchDockedApplication(WAppIcon *btn, Bool withSelection)
936 WScreen *scr = btn->icon->core->screen_ptr;
938 if (!btn->launching &&
939 ((!withSelection && btn->command!=NULL) ||
940 (withSelection && btn->paste_command!=NULL))) {
941 if (!btn->forced_dock) {
942 btn->relaunching = btn->running;
943 btn->running = 1;
945 if (btn->wm_instance || btn->wm_class) {
946 WWindowAttributes attr;
947 memset(&attr, 0, sizeof(WWindowAttributes));
948 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
949 &attr, NULL, True);
951 if (!attr.no_appicon && !btn->buggy_app)
952 btn->launching = 1;
953 else
954 btn->running = 0;
956 btn->drop_launch = 0;
957 btn->paste_launch = withSelection;
958 scr->last_dock = btn->dock;
959 btn->pid = execCommand(btn, (withSelection ? btn->paste_command :
960 btn->command), NULL);
961 if (btn->pid>0) {
962 if (btn->buggy_app) {
963 /* give feedback that the app was launched */
964 btn->launching = 1;
965 dockIconPaint(btn);
966 btn->launching = 0;
967 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
968 } else {
969 dockIconPaint(btn);
971 } else {
972 wwarning(_("could not launch application %s\n"), btn->command);
973 btn->launching = 0;
974 if (!btn->relaunching) {
975 btn->running = 0;
983 static void
984 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
986 WScreen *scr = menu->frame->screen_ptr;
987 char title[MAX_WORKSPACENAME_WIDTH+1];
988 int i;
990 if (!menu || !icon)
991 return;
993 for (i=0; i<scr->workspace_count; i++) {
994 if (i < menu->entry_no) {
995 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
996 wfree(menu->entries[i]->text);
997 strcpy(title, scr->workspaces[i]->name);
998 menu->entries[i]->text = wstrdup(title);
999 menu->flags.realized = 0;
1001 menu->entries[i]->clientdata = (void*)icon;
1002 } else {
1003 strcpy(title, scr->workspaces[i]->name);
1005 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
1007 menu->flags.realized = 0;
1009 if (i == scr->current_workspace) {
1010 wMenuSetEnabled(menu, i, False);
1011 } else {
1012 wMenuSetEnabled(menu, i, True);
1016 if (!menu->flags.realized)
1017 wMenuRealize(menu);
1021 static WMenu*
1022 makeWorkspaceMenu(WScreen *scr)
1024 WMenu *menu;
1026 menu = wMenuCreate(scr, NULL, False);
1027 if (!menu)
1028 wwarning(_("could not create workspace submenu for Clip menu"));
1030 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
1032 menu->flags.realized = 0;
1033 wMenuRealize(menu);
1035 return menu;
1039 static void
1040 updateClipOptionsMenu(WMenu *menu, WDock *dock)
1042 WMenuEntry *entry;
1043 int index = 0;
1045 if (!menu || !dock)
1046 return;
1048 /* keep on top */
1049 entry = menu->entries[index];
1050 entry->flags.indicator_on = !dock->lowered;
1051 entry->clientdata = dock;
1053 /* collapsed */
1054 entry = menu->entries[++index];
1055 entry->flags.indicator_on = dock->collapsed;
1056 entry->clientdata = dock;
1058 /* auto-collapse */
1059 entry = menu->entries[++index];
1060 entry->flags.indicator_on = dock->auto_collapse;
1061 entry->clientdata = dock;
1063 /* auto-raise/lower */
1064 entry = menu->entries[++index];
1065 entry->flags.indicator_on = dock->auto_raise_lower;
1066 entry->clientdata = dock;
1067 wMenuSetEnabled(menu, index, dock->lowered);
1069 /* attract icons */
1070 entry = menu->entries[++index];
1071 entry->flags.indicator_on = dock->attract_icons;
1072 entry->clientdata = dock;
1074 menu->flags.realized = 0;
1075 wMenuRealize(menu);
1079 static WMenu*
1080 makeClipOptionsMenu(WScreen *scr)
1082 WMenu *menu;
1083 WMenuEntry *entry;
1085 menu = wMenuCreate(scr, NULL, False);
1086 if (!menu) {
1087 wwarning(_("could not create options submenu for Clip menu"));
1088 return NULL;
1091 entry = wMenuAddCallback(menu, _("Keep on Top"),
1092 toggleLoweredCallback, NULL);
1093 entry->flags.indicator = 1;
1094 entry->flags.indicator_on = 1;
1095 entry->flags.indicator_type = MI_CHECK;
1097 entry = wMenuAddCallback(menu, _("Collapsed"),
1098 toggleCollapsedCallback, NULL);
1099 entry->flags.indicator = 1;
1100 entry->flags.indicator_on = 1;
1101 entry->flags.indicator_type = MI_CHECK;
1103 entry = wMenuAddCallback(menu, _("Autocollapse"),
1104 toggleAutoCollapseCallback, NULL);
1105 entry->flags.indicator = 1;
1106 entry->flags.indicator_on = 1;
1107 entry->flags.indicator_type = MI_CHECK;
1109 entry = wMenuAddCallback(menu, _("Autoraise"),
1110 toggleAutoRaiseLowerCallback, NULL);
1111 entry->flags.indicator = 1;
1112 entry->flags.indicator_on = 1;
1113 entry->flags.indicator_type = MI_CHECK;
1115 entry = wMenuAddCallback(menu, _("Autoattract Icons"),
1116 toggleAutoAttractCallback, NULL);
1117 entry->flags.indicator = 1;
1118 entry->flags.indicator_on = 1;
1119 entry->flags.indicator_type = MI_CHECK;
1121 menu->flags.realized = 0;
1122 wMenuRealize(menu);
1124 return menu;
1128 static WMenu*
1129 dockMenuCreate(WScreen *scr, int type)
1131 WMenu *menu;
1132 WMenuEntry *entry;
1134 if (type == WM_CLIP && scr->clip_menu)
1135 return scr->clip_menu;
1137 menu = wMenuCreate(scr, NULL, False);
1138 if (type != WM_CLIP) {
1139 entry = wMenuAddCallback(menu, _("Keep on Top"),
1140 toggleLoweredCallback, NULL);
1141 entry->flags.indicator = 1;
1142 entry->flags.indicator_on = 1;
1143 entry->flags.indicator_type = MI_CHECK;
1144 } else {
1145 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1146 scr->clip_options = makeClipOptionsMenu(scr);
1147 if (scr->clip_options)
1148 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1150 entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback,
1151 NULL);
1152 wfree(entry->text);
1153 entry->text = _("Rename Workspace");
1155 entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL);
1156 entry->flags.indicator = 1;
1157 entry->flags.indicator_on = 1;
1158 entry->flags.indicator_type = MI_CHECK;
1160 entry = wMenuAddCallback(menu, _("Select All Icons"),
1161 selectIconsCallback, NULL);
1162 wfree(entry->text);
1163 entry->text = _("Select All Icons");
1165 entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL);
1166 wfree(entry->text);
1167 entry->text = _("Keep Icon");
1169 entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL);
1170 wfree(entry->text);
1171 entry->text = _("Move Icon To");
1172 scr->clip_submenu = makeWorkspaceMenu(scr);
1173 if (scr->clip_submenu)
1174 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1176 entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback,
1177 NULL);
1178 wfree(entry->text);
1179 entry->text = _("Remove Icon");
1181 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1184 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1186 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1188 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1189 wfree(entry->text);
1190 entry->text = _("Hide");
1192 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1194 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1196 if (type == WM_CLIP)
1197 scr->clip_menu = menu;
1199 return menu;
1203 WDock*
1204 wDockCreate(WScreen *scr, int type)
1206 WDock *dock;
1207 WAppIcon *btn;
1208 int icon_count;
1210 make_keys();
1212 dock = wmalloc(sizeof(WDock));
1213 memset(dock, 0, sizeof(WDock));
1215 if (type == WM_CLIP)
1216 icon_count = CLIP_MAX_ICONS;
1217 else
1218 icon_count = scr->scr_height/wPreferences.icon_size;
1220 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1221 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1223 dock->max_icons = icon_count;
1225 btn = mainIconCreate(scr, type);
1227 btn->dock = dock;
1229 dock->x_pos = btn->x_pos;
1230 dock->y_pos = btn->y_pos;
1231 dock->screen_ptr = scr;
1232 dock->type = type;
1233 dock->icon_count = 1;
1234 dock->on_right_side = 1;
1235 dock->collapsed = 0;
1236 dock->auto_collapse = 0;
1237 dock->auto_collapse_magic = NULL;
1238 dock->auto_raise_lower = 0;
1239 dock->auto_lower_magic = NULL;
1240 dock->auto_raise_magic = NULL;
1241 dock->attract_icons = 0;
1242 dock->lowered = 1;
1243 dock->icon_array[0] = btn;
1244 wRaiseFrame(btn->icon->core);
1245 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1247 /* create dock menu */
1248 dock->menu = dockMenuCreate(scr, type);
1250 return dock;
1254 void
1255 wDockDestroy(WDock *dock)
1257 int i;
1258 WAppIcon *aicon;
1260 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1261 aicon = dock->icon_array[i];
1262 if (aicon) {
1263 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1264 wDockDetach(dock, aicon);
1265 if (keepit) {
1266 /* XXX: can: aicon->icon == NULL ? */
1267 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, wGetHeadForWindow(aicon->icon->owner));
1268 XMoveWindow(dpy, aicon->icon->core->window,
1269 aicon->x_pos, aicon->y_pos);
1270 if (!dock->mapped || dock->collapsed)
1271 XMapWindow(dpy, aicon->icon->core->window);
1275 if (wPreferences.auto_arrange_icons)
1276 wArrangeIcons(dock->screen_ptr, True);
1277 wfree(dock->icon_array);
1278 if (dock->menu && dock->type!=WM_CLIP)
1279 wMenuDestroy(dock->menu, True);
1280 if (dock->screen_ptr->last_dock == dock)
1281 dock->screen_ptr->last_dock = NULL;
1282 wfree(dock);
1286 void
1287 wClipIconPaint(WAppIcon *aicon)
1289 WScreen *scr = aicon->icon->core->screen_ptr;
1290 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1291 WMColor *color;
1292 Window win = aicon->icon->core->window;
1293 int length, nlength;
1294 char *ws_name, ws_number[10];
1295 int ty, tx;
1297 wIconPaint(aicon->icon);
1299 length = strlen(workspace->name);
1300 ws_name = wmalloc(length + 1);
1301 snprintf(ws_name, length+1, "%s", workspace->name);
1302 snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1);
1303 nlength = strlen(ws_number);
1305 if (!workspace->clip->collapsed)
1306 color = scr->clip_title_color[CLIP_NORMAL];
1307 else
1308 color = scr->clip_title_color[CLIP_COLLAPSED];
1310 ty = ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
1312 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1314 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1315 ty, ws_name, length);
1316 /*WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, 4,
1317 2, ws_name, length);*/
1319 tx = (ICON_SIZE/2 - WMWidthOfString(scr->clip_title_font, ws_number,
1320 nlength))/2;
1322 WMDrawString(scr->wmscreen, win, color, scr->clip_title_font, tx,
1323 2, ws_number, nlength);
1325 wfree(ws_name);
1327 if (aicon->launching) {
1328 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1329 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1331 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1332 aicon->dock->rclip_button_pushed);
1336 static void
1337 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1339 wClipIconPaint(desc->parent);
1343 static void
1344 dockIconPaint(WAppIcon *btn)
1346 if (btn == btn->icon->core->screen_ptr->clip_icon)
1347 wClipIconPaint(btn);
1348 else
1349 wAppIconPaint(btn);
1353 static WMPropList*
1354 make_icon_state(WAppIcon *btn)
1356 WMPropList *node = NULL;
1357 WMPropList *command, *autolaunch, *lock, *name, *forced, *host;
1358 WMPropList *position, *buggy, *omnipresent;
1359 char *tmp;
1360 char buffer[64];
1362 if (btn) {
1363 if (!btn->command)
1364 command = WMCreatePLString("-");
1365 else
1366 command = WMCreatePLString(btn->command);
1368 autolaunch = btn->auto_launch ? dYes : dNo;
1370 lock = btn->lock ? dYes : dNo;
1372 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1374 name = WMCreatePLString(tmp);
1376 wfree(tmp);
1378 forced = btn->forced_dock ? dYes : dNo;
1380 buggy = btn->buggy_app ? dYes : dNo;
1382 if (btn == btn->icon->core->screen_ptr->clip_icon)
1383 snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos);
1384 else
1385 snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex);
1386 position = WMCreatePLString(buffer);
1388 node = WMCreatePLDictionary(dCommand, command,
1389 dName, name,
1390 dAutoLaunch, autolaunch,
1391 dLock, lock,
1392 dForced, forced,
1393 dBuggyApplication, buggy,
1394 dPosition, position,
1395 NULL);
1396 WMReleasePropList(command);
1397 WMReleasePropList(name);
1398 WMReleasePropList(position);
1400 omnipresent = btn->omnipresent ? dYes : dNo;
1401 if (btn->dock != btn->icon->core->screen_ptr->dock &&
1402 (btn->xindex != 0 || btn->yindex != 0))
1403 WMPutInPLDictionary(node, dOmnipresent, omnipresent);
1405 #ifdef OFFIX_DND
1406 if (btn->dnd_command) {
1407 command = WMCreatePLString(btn->dnd_command);
1408 WMPutInPLDictionary(node, dDropCommand, command);
1409 WMReleasePropList(command);
1411 #endif /* OFFIX_DND */
1413 if (btn->paste_command) {
1414 command = WMCreatePLString(btn->paste_command);
1415 WMPutInPLDictionary(node, dPasteCommand, command);
1416 WMReleasePropList(command);
1419 if (btn->client_machine && btn->remote_start) {
1420 host = WMCreatePLString(btn->client_machine);
1421 WMPutInPLDictionary(node, dHost, host);
1422 WMReleasePropList(host);
1426 return node;
1430 static WMPropList*
1431 dockSaveState(WDock *dock)
1433 int i;
1434 WMPropList *icon_info;
1435 WMPropList *list=NULL, *dock_state=NULL;
1436 WMPropList *value, *key;
1437 char buffer[256];
1439 list = WMCreatePLArray(NULL);
1441 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1442 WAppIcon *btn = dock->icon_array[i];
1444 if (!btn || btn->attracted)
1445 continue;
1447 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1448 WMAddToPLArray(list, icon_info);
1449 WMReleasePropList(icon_info);
1453 dock_state = WMCreatePLDictionary(dApplications, list, NULL);
1455 if (dock->type == WM_DOCK) {
1456 snprintf(buffer, sizeof(buffer), "Applications%i", dock->screen_ptr->scr_height);
1457 key = WMCreatePLString(buffer);
1458 WMPutInPLDictionary(dock_state, key, list);
1459 WMReleasePropList(key);
1462 snprintf(buffer, sizeof(buffer), "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1463 dock->y_pos);
1464 value = WMCreatePLString(buffer);
1465 WMPutInPLDictionary(dock_state, dPosition, value);
1466 WMReleasePropList(value);
1468 WMReleasePropList(list);
1471 value = (dock->lowered ? dYes : dNo);
1472 WMPutInPLDictionary(dock_state, dLowered, value);
1474 if (dock->type == WM_CLIP) {
1475 value = (dock->collapsed ? dYes : dNo);
1476 WMPutInPLDictionary(dock_state, dCollapsed, value);
1478 value = (dock->auto_collapse ? dYes : dNo);
1479 WMPutInPLDictionary(dock_state, dAutoCollapse, value);
1481 value = (dock->auto_raise_lower ? dYes : dNo);
1482 WMPutInPLDictionary(dock_state, dAutoRaiseLower, value);
1484 value = (dock->attract_icons ? dYes : dNo);
1485 WMPutInPLDictionary(dock_state, dAutoAttractIcons, value);
1488 return dock_state;
1492 void
1493 wDockSaveState(WScreen *scr, WMPropList *old_state)
1495 WMPropList *dock_state;
1496 WMPropList *keys;
1498 dock_state = dockSaveState(scr->dock);
1501 * Copy saved states of docks with different sizes.
1503 if (old_state) {
1504 int i;
1505 WMPropList *tmp;
1507 keys = WMGetPLDictionaryKeys(old_state);
1508 for (i = 0; i < WMGetPropListItemCount(keys); i++) {
1509 tmp = WMGetFromPLArray(keys, i);
1511 if (strncasecmp(WMGetFromPLString(tmp), "applications", 12) == 0
1512 && !WMGetFromPLDictionary(dock_state, tmp)) {
1514 WMPutInPLDictionary(dock_state, tmp,
1515 WMGetFromPLDictionary(old_state, tmp));
1518 WMReleasePropList(keys);
1522 WMPutInPLDictionary(scr->session_state, dDock, dock_state);
1524 WMReleasePropList(dock_state);
1528 void
1529 wClipSaveState(WScreen *scr)
1531 WMPropList *clip_state;
1533 clip_state = make_icon_state(scr->clip_icon);
1535 WMPutInPLDictionary(scr->session_state, dClip, clip_state);
1537 WMReleasePropList(clip_state);
1541 WMPropList*
1542 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1544 return dockSaveState(scr->workspaces[workspace]->clip);
1548 static Bool
1549 getBooleanDockValue(WMPropList *value, WMPropList *key)
1551 if (value) {
1552 if (WMIsPLString(value)) {
1553 if (strcasecmp(WMGetFromPLString(value), "YES")==0)
1554 return True;
1555 } else {
1556 wwarning(_("bad value in docked icon state info %s"),
1557 WMGetFromPLString(key));
1560 return False;
1564 static WAppIcon*
1565 restore_icon_state(WScreen *scr, WMPropList *info, int type, int index)
1567 WAppIcon *aicon;
1568 WMPropList *cmd, *value;
1571 cmd = WMGetFromPLDictionary(info, dCommand);
1572 if (!cmd || !WMIsPLString(cmd)) {
1573 return NULL;
1576 /* parse window name */
1577 value = WMGetFromPLDictionary(info, dName);
1578 if (!value)
1579 return NULL;
1582 char *wclass, *winstance;
1583 char *command;
1585 ParseWindowName(value, &winstance, &wclass, "dock");
1587 if (!winstance && !wclass) {
1588 return NULL;
1591 /* get commands */
1593 if (cmd)
1594 command = wstrdup(WMGetFromPLString(cmd));
1595 else
1596 command = NULL;
1598 if (!command || strcmp(command, "-")==0) {
1599 if (command)
1600 wfree(command);
1601 if (wclass)
1602 wfree(wclass);
1603 if (winstance)
1604 wfree(winstance);
1606 return NULL;
1609 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1610 TILE_NORMAL);
1611 if (wclass)
1612 wfree(wclass);
1613 if (winstance)
1614 wfree(winstance);
1615 if (command)
1616 wfree(command);
1619 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1620 if (type == WM_CLIP) {
1621 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1622 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1624 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1625 aicon->icon->core->descriptor.parent = aicon;
1628 #ifdef OFFIX_DND
1629 cmd = WMGetFromPLDictionary(info, dDropCommand);
1630 if (cmd)
1631 aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
1632 #endif
1634 cmd = WMGetFromPLDictionary(info, dPasteCommand);
1635 if (cmd)
1636 aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
1638 /* check auto launch */
1639 value = WMGetFromPLDictionary(info, dAutoLaunch);
1641 aicon->auto_launch = getBooleanDockValue(value, dAutoLaunch);
1643 /* check lock */
1644 value = WMGetFromPLDictionary(info, dLock);
1646 aicon->lock = getBooleanDockValue(value, dLock);
1648 /* check if it wasn't normally docked */
1649 value = WMGetFromPLDictionary(info, dForced);
1651 aicon->forced_dock = getBooleanDockValue(value, dForced);
1653 /* check if we can rely on the stuff in the app */
1654 value = WMGetFromPLDictionary(info, dBuggyApplication);
1656 aicon->buggy_app = getBooleanDockValue(value, dBuggyApplication);
1658 /* get position in the dock */
1659 value = WMGetFromPLDictionary(info, dPosition);
1660 if (value && WMIsPLString(value)) {
1661 if (sscanf(WMGetFromPLString(value), "%hi,%hi", &aicon->xindex,
1662 &aicon->yindex)!=2)
1663 wwarning(_("bad value in docked icon state info %s"),
1664 WMGetFromPLString(dPosition));
1666 /* check position sanity */
1667 /* incomplete section! */
1668 if (type == WM_DOCK) {
1669 aicon->xindex = 0;
1670 if (aicon->yindex < 0)
1671 wwarning(_("bad value in docked icon position %i,%i"),
1672 aicon->xindex, aicon->yindex);
1674 } else {
1675 aicon->yindex = index;
1676 aicon->xindex = 0;
1679 /* check if icon is omnipresent */
1680 value = WMGetFromPLDictionary(info, dOmnipresent);
1682 aicon->omnipresent = getBooleanDockValue(value, dOmnipresent);
1684 aicon->running = 0;
1685 aicon->docked = 1;
1687 return aicon;
1691 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1694 WAppIcon*
1695 wClipRestoreState(WScreen *scr, WMPropList *clip_state)
1697 WAppIcon *icon;
1698 WMPropList *value;
1701 icon = mainIconCreate(scr, WM_CLIP);
1703 if (!clip_state)
1704 return icon;
1705 else
1706 WMRetainPropList(clip_state);
1708 /* restore position */
1710 value = WMGetFromPLDictionary(clip_state, dPosition);
1712 if (value) {
1713 if (!WMIsPLString(value))
1714 COMPLAIN("Position");
1715 else {
1716 WMRect rect;
1717 int flags;
1719 if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos,
1720 &icon->y_pos)!=2)
1721 COMPLAIN("Position");
1723 /* check position sanity */
1724 rect.pos.x = icon->x_pos;
1725 rect.pos.y = icon->y_pos;
1726 rect.size.width = rect.size.height = ICON_SIZE;
1728 wGetRectPlacementInfo(scr, rect, &flags);
1729 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
1730 wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos,
1731 ICON_SIZE, ICON_SIZE);
1735 #ifdef OFFIX_DND
1736 value = WMGetFromPLDictionary(clip_state, dDropCommand);
1737 if (value && WMIsPLString(value))
1738 icon->dnd_command = wstrdup(WMGetFromPLString(value));
1739 #endif
1741 value = WMGetFromPLDictionary(clip_state, dPasteCommand);
1742 if (value && WMIsPLString(value))
1743 icon->paste_command = wstrdup(WMGetFromPLString(value));
1745 WMReleasePropList(clip_state);
1747 return icon;
1751 WDock*
1752 wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type)
1754 WDock *dock;
1755 WMPropList *apps;
1756 WMPropList *value;
1757 WAppIcon *aicon, *old_top;
1758 int count, i;
1761 dock = wDockCreate(scr, type);
1763 if (!dock_state)
1764 return dock;
1766 if (dock_state)
1767 WMRetainPropList(dock_state);
1770 /* restore position */
1772 value = WMGetFromPLDictionary(dock_state, dPosition);
1774 if (value) {
1775 if (!WMIsPLString(value)) {
1776 COMPLAIN("Position");
1777 } else {
1778 WMRect rect;
1779 int flags;
1781 if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos,
1782 &dock->y_pos)!=2)
1783 COMPLAIN("Position");
1785 /* check position sanity */
1786 rect.pos.x = dock->x_pos;
1787 rect.pos.y = dock->y_pos;
1788 rect.size.width = rect.size.height = ICON_SIZE;
1790 wGetRectPlacementInfo(scr, rect, &flags);
1791 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) {
1792 int x = dock->x_pos;
1793 wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE);
1796 /* Is this needed any more? */
1797 if (type == WM_CLIP) {
1798 if (dock->x_pos < 0) {
1799 dock->x_pos = 0;
1800 } else if (dock->x_pos > scr->scr_width-ICON_SIZE) {
1801 dock->x_pos = scr->scr_width-ICON_SIZE;
1803 } else {
1804 if (dock->x_pos >= 0) {
1805 dock->x_pos = DOCK_EXTRA_SPACE;
1806 dock->on_right_side = 0;
1807 } else {
1808 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1809 dock->on_right_side = 1;
1815 /* restore lowered/raised state */
1817 dock->lowered = 0;
1819 value = WMGetFromPLDictionary(dock_state, dLowered);
1821 if (value) {
1822 if (!WMIsPLString(value)) {
1823 COMPLAIN("Lowered");
1824 } else {
1825 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1826 dock->lowered = 1;
1832 /* restore collapsed state */
1834 dock->collapsed = 0;
1836 value = WMGetFromPLDictionary(dock_state, dCollapsed);
1838 if (value) {
1839 if (!WMIsPLString(value)) {
1840 COMPLAIN("Collapsed");
1841 } else {
1842 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1843 dock->collapsed = 1;
1849 /* restore auto-collapsed state */
1851 value = WMGetFromPLDictionary(dock_state, dAutoCollapse);
1853 if (value) {
1854 if (!WMIsPLString(value)) {
1855 COMPLAIN("AutoCollapse");
1856 } else {
1857 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1858 dock->auto_collapse = 1;
1859 dock->collapsed = 1;
1865 /* restore auto-raise/lower state */
1867 value = WMGetFromPLDictionary(dock_state, dAutoRaiseLower);
1869 if (value) {
1870 if (!WMIsPLString(value)) {
1871 COMPLAIN("AutoRaiseLower");
1872 } else {
1873 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1874 dock->auto_raise_lower = 1;
1879 /* restore attract icons state */
1881 dock->attract_icons = 0;
1883 value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons);
1885 if (value) {
1886 if (!WMIsPLString(value)) {
1887 COMPLAIN("AutoAttractIcons");
1888 } else {
1889 if (strcasecmp(WMGetFromPLString(value), "YES")==0) {
1890 dock->attract_icons = 1;
1896 /* application list */
1899 WMPropList *tmp;
1900 char buffer[64];
1903 * When saving, it saves the dock state in
1904 * Applications and Applicationsnnn
1906 * When loading, it will first try Applicationsnnn.
1907 * If it does not exist, use Applications as default.
1910 snprintf(buffer, sizeof(buffer), "Applications%i", scr->scr_height);
1912 tmp = WMCreatePLString(buffer);
1913 apps = WMGetFromPLDictionary(dock_state, tmp);
1914 WMReleasePropList(tmp);
1916 if (!apps) {
1917 apps = WMGetFromPLDictionary(dock_state, dApplications);
1921 if (!apps) {
1922 goto finish;
1925 count = WMGetPropListItemCount(apps);
1927 if (count==0)
1928 goto finish;
1930 old_top = dock->icon_array[0];
1932 /* dock->icon_count is set to 1 when dock is created.
1933 * Since Clip is already restored, we want to keep it so for clip,
1934 * but for dock we may change the default top tile, so we set it to 0.
1936 if (type == WM_DOCK)
1937 dock->icon_count = 0;
1939 for (i=0; i<count; i++) {
1940 if (dock->icon_count >= dock->max_icons) {
1941 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1942 break;
1945 value = WMGetFromPLArray(apps, i);
1946 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1948 dock->icon_array[dock->icon_count] = aicon;
1950 if (aicon) {
1951 aicon->dock = dock;
1952 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1953 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1955 if (dock->lowered)
1956 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1957 else
1958 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1960 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1961 0, 0);
1963 if (!dock->collapsed)
1964 XMapWindow(dpy, aicon->icon->core->window);
1965 wRaiseFrame(aicon->icon->core);
1967 dock->icon_count++;
1968 } else if (dock->icon_count==0 && type==WM_DOCK)
1969 dock->icon_count++;
1972 /* if the first icon is not defined, use the default */
1973 if (dock->icon_array[0]==NULL) {
1974 /* update default icon */
1975 old_top->x_pos = dock->x_pos;
1976 old_top->y_pos = dock->y_pos;
1977 if (dock->lowered)
1978 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1979 else
1980 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1981 dock->icon_array[0] = old_top;
1982 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1983 /* we don't need to increment dock->icon_count here because it was
1984 * incremented in the loop above.
1986 } else if (old_top!=dock->icon_array[0]) {
1987 if (old_top == scr->clip_icon)
1988 scr->clip_icon = dock->icon_array[0];
1989 wAppIconDestroy(old_top);
1992 finish:
1993 if (dock_state)
1994 WMReleasePropList(dock_state);
1996 return dock;
2001 void
2002 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
2004 if (btn && btn->command && !btn->running && !btn->launching) {
2006 btn->drop_launch = 0;
2007 btn->paste_launch = 0;
2009 btn->pid = execCommand(btn, btn->command, state);
2011 if (btn->pid>0) {
2012 if (!btn->forced_dock && !btn->buggy_app) {
2013 btn->launching = 1;
2014 dockIconPaint(btn);
2017 } else {
2018 wfree(state);
2023 void
2024 wDockDoAutoLaunch(WDock *dock, int workspace)
2026 WAppIcon *btn;
2027 WSavedState *state;
2028 int i;
2030 for (i = 0; i < dock->max_icons; i++) {
2031 btn = dock->icon_array[i];
2032 if (!btn || !btn->auto_launch)
2033 continue;
2035 state = wmalloc(sizeof(WSavedState));
2036 memset(state, 0, sizeof(WSavedState));
2037 state->workspace = workspace;
2038 /* TODO: this is klugy and is very difficult to understand
2039 * what's going on. Try to clean up */
2040 wDockLaunchWithState(dock, btn, state);
2045 #ifdef OFFIX_DND
2046 static WDock*
2047 findDock(WScreen *scr, XEvent *event, int *icon_pos)
2049 WDock *dock;
2050 int i;
2052 *icon_pos = -1;
2053 if ((dock = scr->dock)!=NULL) {
2054 for (i=0; i<dock->max_icons; i++) {
2055 if (dock->icon_array[i]
2056 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2057 *icon_pos = i;
2058 break;
2062 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
2063 for (i=0; i<dock->max_icons; i++) {
2064 if (dock->icon_array[i]
2065 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
2066 *icon_pos = i;
2067 break;
2071 if(*icon_pos>=0)
2072 return dock;
2073 return NULL;
2078 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
2080 WDock *dock;
2081 WAppIcon *btn;
2082 int icon_pos;
2084 dock = findDock(scr, event, &icon_pos);
2085 if (!dock)
2086 return False;
2089 * Return True if the drop was on an application icon window.
2090 * In this case, let the ClientMessage handler redirect the
2091 * message to the app.
2093 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
2094 return True;
2096 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
2097 scr->flags.dnd_data_convertion_status = 0;
2099 btn = dock->icon_array[icon_pos];
2101 if (!btn->forced_dock) {
2102 btn->relaunching = btn->running;
2103 btn->running = 1;
2105 if (btn->wm_instance || btn->wm_class) {
2106 WWindowAttributes attr;
2107 memset(&attr, 0, sizeof(WWindowAttributes));
2108 wDefaultFillAttributes(btn->icon->core->screen_ptr,
2109 btn->wm_instance,
2110 btn->wm_class, &attr, NULL, True);
2112 if (!attr.no_appicon)
2113 btn->launching = 1;
2114 else
2115 btn->running = 0;
2118 btn->paste_launch = 0;
2119 btn->drop_launch = 1;
2120 scr->last_dock = dock;
2121 btn->pid = execCommand(btn, btn->dnd_command, NULL);
2122 if (btn->pid>0) {
2123 dockIconPaint(btn);
2124 } else {
2125 btn->launching = 0;
2126 if (!btn->relaunching) {
2127 btn->running = 0;
2131 return False;
2133 #endif /* OFFIX_DND */
2137 Bool
2138 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2140 WWindow *wwin;
2141 int index;
2143 wwin = icon->icon->owner;
2144 if (icon->command==NULL) {
2145 char *command;
2147 icon->editing = 0;
2149 command = GetCommandForWindow(wwin->client_win);
2150 if (command) {
2151 icon->command = command;
2152 } else {
2153 /* icon->forced_dock = 1;*/
2154 if (dock->type!=WM_CLIP || !icon->attracted) {
2155 icon->editing = 1;
2156 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
2157 _("Type the command used to launch the application"),
2158 &command)) {
2159 if (command && (command[0]==0 ||
2160 (command[0]=='-' && command[1]==0))) {
2161 wfree(command);
2162 command = NULL;
2164 icon->command = command;
2165 icon->editing = 0;
2166 } else {
2167 icon->editing = 0;
2168 if (command)
2169 wfree(command);
2170 /* If the target is the dock, reject the icon. If
2171 * the target is the clip, make it an attracted icon
2173 if (dock->type==WM_CLIP) {
2174 icon->attracted = 1;
2175 if (!icon->icon->shadowed) {
2176 icon->icon->shadowed = 1;
2177 icon->icon->force_paint = 1;
2179 } else {
2180 return False;
2185 } else {
2186 icon->editing = 0;
2189 for (index=1; index<dock->max_icons; index++)
2190 if (dock->icon_array[index] == NULL)
2191 break;
2192 /* if (index == dock->max_icons)
2193 return; */
2195 assert(index < dock->max_icons);
2197 dock->icon_array[index] = icon;
2198 icon->yindex = y;
2199 icon->xindex = x;
2201 icon->omnipresent = 0;
2203 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2204 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2206 dock->icon_count++;
2208 icon->running = 1;
2209 icon->launching = 0;
2210 icon->docked = 1;
2211 icon->dock = dock;
2212 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2213 if (dock->type == WM_CLIP) {
2214 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2215 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2217 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2218 icon->icon->core->descriptor.parent = icon;
2220 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2221 icon->icon->core);
2222 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2223 wAppIconPaint(icon);
2225 if (wPreferences.auto_arrange_icons)
2226 wArrangeIcons(dock->screen_ptr, True);
2228 #ifdef OFFIX_DND
2229 if (icon->command && !icon->dnd_command) {
2230 int len = strlen(icon->command)+8;
2231 icon->dnd_command = wmalloc(len);
2232 snprintf(icon->dnd_command, len, "%s %%d", icon->command);
2234 #endif
2236 if (icon->command && !icon->paste_command) {
2237 int len = strlen(icon->command)+8;
2238 icon->paste_command = wmalloc(len);
2239 snprintf(icon->paste_command, len, "%s %%s", icon->command);
2242 return True;
2246 void
2247 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2249 int index;
2251 for(index=1; index<dock->max_icons; index++) {
2252 if(dock->icon_array[index] == icon)
2253 break;
2255 assert(index < dock->max_icons);
2257 icon->yindex = y;
2258 icon->xindex = x;
2260 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2261 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2265 Bool
2266 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2268 WWindow *wwin;
2269 char *command;
2270 int index;
2272 if (src == dest)
2273 return True; /* No move needed, we're already there */
2275 if (dest == NULL)
2276 return False;
2278 wwin = icon->icon->owner;
2281 * For the moment we can't do this if we move icons in Clip from one
2282 * workspace to other, because if we move two or more icons without
2283 * command, the dialog box will not be able to tell us to which of the
2284 * moved icons it applies. -Dan
2286 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2287 command = GetCommandForWindow(wwin->client_win);
2288 if (command) {
2289 icon->command = command;
2290 } else {
2291 icon->editing = 1;
2292 /* icon->forced_dock = 1;*/
2293 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2294 _("Type the command used to launch the application"),
2295 &command)) {
2296 if (command && (command[0]==0 ||
2297 (command[0]=='-' && command[1]==0))) {
2298 wfree(command);
2299 command = NULL;
2301 icon->command = command;
2302 } else {
2303 icon->editing = 0;
2304 if (command)
2305 wfree(command);
2306 return False;
2308 icon->editing = 0;
2312 if (dest->type == WM_DOCK)
2313 wClipMakeIconOmnipresent(icon, False);
2315 for(index=1; index<src->max_icons; index++) {
2316 if(src->icon_array[index] == icon)
2317 break;
2319 assert(index < src->max_icons);
2321 src->icon_array[index] = NULL;
2322 src->icon_count--;
2324 for(index=1; index<dest->max_icons; index++) {
2325 if(dest->icon_array[index] == NULL)
2326 break;
2328 /* if (index == dest->max_icons)
2329 return; */
2331 assert(index < dest->max_icons);
2333 dest->icon_array[index] = icon;
2334 icon->dock = dest;
2336 /* deselect the icon */
2337 if (icon->icon->selected)
2338 wIconSelect(icon->icon);
2340 if (dest->type == WM_DOCK) {
2341 icon->icon->core->descriptor.handle_enternotify = NULL;
2342 icon->icon->core->descriptor.handle_leavenotify = NULL;
2343 } else {
2344 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2345 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2348 /* set it to be kept when moving to dock.
2349 * Unless the icon does not have a command set
2351 if (icon->command && dest->type==WM_DOCK) {
2352 icon->attracted = 0;
2353 if (icon->icon->shadowed) {
2354 icon->icon->shadowed = 0;
2355 icon->icon->force_paint = 1;
2359 if (src->auto_collapse || src->auto_raise_lower)
2360 clipLeave(src);
2362 icon->yindex = y;
2363 icon->xindex = x;
2365 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2366 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2368 dest->icon_count++;
2370 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2371 icon->icon->core);
2372 wAppIconPaint(icon);
2374 return True;
2378 void
2379 wDockDetach(WDock *dock, WAppIcon *icon)
2381 int index;
2383 /* make the settings panel be closed */
2384 if (icon->panel) {
2385 DestroyDockAppSettingsPanel(icon->panel);
2388 /* This must be called before icon->dock is set to NULL.
2389 * Don't move it. -Dan
2391 wClipMakeIconOmnipresent(icon, False);
2393 icon->docked = 0;
2394 icon->dock = NULL;
2395 icon->attracted = 0;
2396 icon->auto_launch = 0;
2397 if (icon->icon->shadowed) {
2398 icon->icon->shadowed = 0;
2399 icon->icon->force_paint = 1;
2402 /* deselect the icon */
2403 if (icon->icon->selected)
2404 wIconSelect(icon->icon);
2406 if (icon->command) {
2407 wfree(icon->command);
2408 icon->command = NULL;
2410 #ifdef OFFIX_DND
2411 if (icon->dnd_command) {
2412 wfree(icon->dnd_command);
2413 icon->dnd_command = NULL;
2415 #endif
2416 if (icon->paste_command) {
2417 wfree(icon->paste_command);
2418 icon->paste_command = NULL;
2421 for (index=1; index<dock->max_icons; index++)
2422 if (dock->icon_array[index] == icon)
2423 break;
2424 assert(index < dock->max_icons);
2425 dock->icon_array[index] = NULL;
2426 icon->yindex = -1;
2427 icon->xindex = -1;
2429 dock->icon_count--;
2431 /* if the dock is not attached to an application or
2432 * the the application did not set the approriate hints yet,
2433 * destroy the icon */
2434 if (!icon->running || !wApplicationOf(icon->main_window))
2435 wAppIconDestroy(icon);
2436 else {
2437 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2438 icon->icon->core->descriptor.handle_enternotify = NULL;
2439 icon->icon->core->descriptor.handle_leavenotify = NULL;
2440 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2441 icon->icon->core->descriptor.parent = icon;
2443 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2445 wAppIconPaint(icon);
2446 if (wPreferences.auto_arrange_icons) {
2447 wArrangeIcons(dock->screen_ptr, True);
2450 if (dock->auto_collapse || dock->auto_raise_lower)
2451 clipLeave(dock);
2456 * returns the closest Dock slot index for the passed
2457 * coordinates.
2459 * Returns False if icon can't be docked.
2461 * Note: this function should NEVER alter ret_x or ret_y, unless it will
2462 * return True. -Dan
2464 Bool
2465 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2466 int *ret_x, int *ret_y, int redocking)
2468 WScreen *scr = dock->screen_ptr;
2469 int dx, dy;
2470 int ex_x, ex_y;
2471 int i, offset = ICON_SIZE/2;
2472 WAppIcon *aicon = NULL;
2473 WAppIcon *nicon = NULL;
2474 int max_y_icons, max_x_icons;
2476 /* TODO: XINERAMA, for these */
2477 max_x_icons = scr->scr_width/ICON_SIZE;
2478 max_y_icons = scr->scr_height/ICON_SIZE-1;
2480 if (wPreferences.flags.noupdates)
2481 return False;
2483 dx = dock->x_pos;
2484 dy = dock->y_pos;
2486 /* if the dock is full */
2487 if (!redocking &&
2488 (dock->icon_count >= dock->max_icons)) {
2489 return False;
2492 /* exact position */
2493 if (req_y < dy)
2494 ex_y = (req_y - offset - dy)/ICON_SIZE;
2495 else
2496 ex_y = (req_y + offset - dy)/ICON_SIZE;
2498 if (req_x < dx)
2499 ex_x = (req_x - offset - dx)/ICON_SIZE;
2500 else
2501 ex_x = (req_x + offset - dx)/ICON_SIZE;
2503 /* check if the icon is outside the screen boundaries */
2505 WMRect rect;
2506 int flags;
2508 rect.pos.x = dx + ex_x*ICON_SIZE;
2509 rect.pos.y = dy + ex_y*ICON_SIZE;
2510 rect.size.width = rect.size.height = ICON_SIZE;
2512 wGetRectPlacementInfo(scr, rect, &flags);
2513 if (flags & (XFLAG_DEAD | XFLAG_PARTIAL))
2514 return False;
2517 if (dock->type == WM_DOCK) {
2518 if (icon->dock != dock && ex_x != 0)
2519 return False;
2521 aicon = NULL;
2522 for (i=0; i<dock->max_icons; i++) {
2523 nicon = dock->icon_array[i];
2524 if (nicon && nicon->yindex == ex_y) {
2525 aicon = nicon;
2526 break;
2530 if (redocking) {
2531 int sig, done, closest;
2533 /* Possible cases when redocking:
2535 * icon dragged out of range of any slot -> false
2536 * icon dragged to range of free slot
2537 * icon dragged to range of same slot
2538 * icon dragged to range of different icon
2540 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2541 return False;
2543 if (ex_y>=0 && ex_y<=max_y_icons && (aicon==icon || !aicon)) {
2544 *ret_x = 0;
2545 *ret_y = ex_y;
2546 return True;
2549 /* start looking at the upper slot or lower? */
2550 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2551 sig = 1;
2552 else
2553 sig = -1;
2555 closest = -1;
2556 done = 0;
2557 /* look for closest free slot */
2558 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2559 int j;
2561 done = 1;
2562 closest = sig*(i/2) + ex_y;
2563 /* check if this slot is used */
2564 if (closest >= 0) {
2565 for (j = 0; j<dock->max_icons; j++) {
2566 if (dock->icon_array[j]
2567 && dock->icon_array[j]->yindex==closest) {
2568 /* slot is used by someone else */
2569 if (dock->icon_array[j]!=icon)
2570 done = 0;
2571 break;
2575 sig = -sig;
2577 if (done && closest >= 0 && closest <= max_y_icons &&
2578 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2580 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2581 *ret_x = 0;
2582 *ret_y = closest;
2583 return True;
2585 } else { /* !redocking */
2587 /* if slot is free and the icon is close enough, return it */
2588 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2589 *ret_x = 0;
2590 *ret_y = ex_y;
2591 return True;
2594 } else { /* CLIP */
2595 int neighbours = 0;
2596 int start, stop, k;
2598 start = icon->omnipresent ? 0 : scr->current_workspace;
2599 stop = icon->omnipresent ? scr->workspace_count : start+1;
2601 aicon = NULL;
2602 for (k=start; k<stop; k++) {
2603 WDock *tmp = scr->workspaces[k]->clip;
2604 if (!tmp)
2605 continue;
2606 for (i=0; i<tmp->max_icons; i++) {
2607 nicon = tmp->icon_array[i];
2608 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2609 aicon = nicon;
2610 break;
2613 if (aicon)
2614 break;
2616 for (k=start; k<stop; k++) {
2617 WDock *tmp = scr->workspaces[k]->clip;
2618 if (!tmp)
2619 continue;
2620 for (i=0; i<tmp->max_icons; i++) {
2621 nicon = tmp->icon_array[i];
2622 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2623 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2624 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2625 neighbours = 1;
2626 break;
2629 if (neighbours)
2630 break;
2633 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2634 *ret_x = ex_x;
2635 *ret_y = ex_y;
2636 return True;
2639 return False;
2643 static int
2644 onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey)
2646 WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE);
2647 int flags;
2649 wGetRectPlacementInfo(scr, rect, &flags);
2651 return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL));
2656 * returns true if it can find a free slot in the dock,
2657 * in which case it changes x_pos and y_pos accordingly.
2658 * Else returns false.
2660 Bool
2661 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2663 WScreen *scr = dock->screen_ptr;
2664 WAppIcon *btn;
2665 WAppIconChain *chain;
2666 unsigned char *slot_map;
2667 int mwidth;
2668 int r;
2669 int x, y;
2670 int i, done = False;
2671 int corner;
2672 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2673 int extra_count=0;
2675 if (dock->type == WM_CLIP &&
2676 dock != scr->workspaces[scr->current_workspace]->clip)
2677 extra_count = scr->global_icon_count;
2679 /* if the dock is full */
2680 if (dock->icon_count+extra_count >= dock->max_icons) {
2681 return False;
2684 if (!wPreferences.flags.nodock && scr->dock) {
2685 if (scr->dock->on_right_side)
2686 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2687 else
2688 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2691 if (ex < dock->x_pos)
2692 ex = dock->x_pos;
2693 if (sx > dock->x_pos+ICON_SIZE)
2694 sx = dock->x_pos+ICON_SIZE;
2695 #define C_NONE 0
2696 #define C_NW 1
2697 #define C_NE 2
2698 #define C_SW 3
2699 #define C_SE 4
2701 /* check if clip is in a corner */
2702 if (dock->type==WM_CLIP) {
2703 if (dock->x_pos < 1 && dock->y_pos < 1)
2704 corner = C_NE;
2705 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2706 corner = C_SE;
2707 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2708 corner = C_SW;
2709 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2710 corner = C_NW;
2711 else
2712 corner = C_NONE;
2713 } else
2714 corner = C_NONE;
2716 /* If the clip is in the corner, use only slots that are in the border
2717 * of the screen */
2718 if (corner!=C_NONE) {
2719 char *hmap, *vmap;
2720 int hcount, vcount;
2722 hcount = WMIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2723 vcount = WMIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2724 hmap = wmalloc(hcount+1);
2725 memset(hmap, 0, hcount+1);
2726 vmap = wmalloc(vcount+1);
2727 memset(vmap, 0, vcount+1);
2729 /* mark used positions */
2730 switch (corner) {
2731 case C_NE:
2732 for (i=0; i<dock->max_icons; i++) {
2733 btn = dock->icon_array[i];
2734 if (!btn)
2735 continue;
2737 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2738 vmap[btn->yindex] = 1;
2739 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2740 hmap[btn->xindex] = 1;
2742 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2743 btn = chain->aicon;
2744 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2745 vmap[btn->yindex] = 1;
2746 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2747 hmap[btn->xindex] = 1;
2749 break;
2750 case C_NW:
2751 for (i=0; i<dock->max_icons; i++) {
2752 btn = dock->icon_array[i];
2753 if (!btn)
2754 continue;
2756 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2757 vmap[btn->yindex] = 1;
2758 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2759 hmap[-btn->xindex] = 1;
2761 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2762 btn = chain->aicon;
2763 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2764 vmap[btn->yindex] = 1;
2765 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2766 hmap[-btn->xindex] = 1;
2768 break;
2769 case C_SE:
2770 for (i=0; i<dock->max_icons; i++) {
2771 btn = dock->icon_array[i];
2772 if (!btn)
2773 continue;
2775 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2776 vmap[-btn->yindex] = 1;
2777 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2778 hmap[btn->xindex] = 1;
2780 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2781 btn = chain->aicon;
2782 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2783 vmap[-btn->yindex] = 1;
2784 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2785 hmap[btn->xindex] = 1;
2787 break;
2788 case C_SW:
2789 default:
2790 for (i=0; i<dock->max_icons; i++) {
2791 btn = dock->icon_array[i];
2792 if (!btn)
2793 continue;
2795 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2796 vmap[-btn->yindex] = 1;
2797 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2798 hmap[-btn->xindex] = 1;
2800 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2801 btn = chain->aicon;
2802 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2803 vmap[-btn->yindex] = 1;
2804 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2805 hmap[-btn->xindex] = 1;
2808 x=0; y=0;
2809 done = 0;
2810 /* search a vacant slot */
2811 for (i=1; i<WMAX(vcount, hcount); i++) {
2812 if (i < vcount && vmap[i]==0) {
2813 /* found a slot */
2814 x = 0;
2815 y = i;
2816 done = 1;
2817 break;
2818 } else if (i < hcount && hmap[i]==0) {
2819 /* found a slot */
2820 x = i;
2821 y = 0;
2822 done = 1;
2823 break;
2826 wfree(vmap);
2827 wfree(hmap);
2828 /* If found a slot, translate and return */
2829 if (done) {
2830 if (corner==C_NW || corner==C_NE) {
2831 *y_pos = y;
2832 } else {
2833 *y_pos = -y;
2835 if (corner==C_NE || corner==C_SE) {
2836 *x_pos = x;
2837 } else {
2838 *x_pos = -x;
2840 return True;
2842 /* else, try to find a slot somewhere else */
2845 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2846 * placed outside of screen */
2847 mwidth = (int)ceil(sqrt(dock->max_icons));
2849 /* In the worst case (the clip is in the corner of the screen),
2850 * the amount of icons that fit in the clip is smaller.
2851 * Double the map to get a safe value.
2853 mwidth += mwidth;
2855 r = (mwidth-1)/2;
2857 slot_map = wmalloc(mwidth*mwidth);
2858 memset(slot_map, 0, mwidth*mwidth);
2860 #define XY2OFS(x,y) (WMAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2862 /* mark used slots in the map. If the slot falls outside the map
2863 * (for example, when all icons are placed in line), ignore them. */
2864 for (i=0; i<dock->max_icons; i++) {
2865 btn = dock->icon_array[i];
2866 if (btn)
2867 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2869 for (chain=scr->global_icons; chain!=NULL; chain=chain->next) {
2870 slot_map[XY2OFS(chain->aicon->xindex, chain->aicon->yindex)] = 1;
2872 /* Find closest slot from the center that is free by scanning the
2873 * map from the center to outward in circular passes.
2874 * This will not result in a neat layout, but will be optimal
2875 * in the sense that there will not be holes left.
2877 done = 0;
2878 for (i = 1; i <= r && !done; i++) {
2879 int tx, ty;
2881 /* top and bottom parts of the ring */
2882 for (x = -i; x <= i && !done; x++) {
2883 tx = dock->x_pos + x*ICON_SIZE;
2884 y = -i;
2885 ty = dock->y_pos + y*ICON_SIZE;
2886 if (slot_map[XY2OFS(x,y)]==0
2887 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2888 *x_pos = x;
2889 *y_pos = y;
2890 done = 1;
2891 break;
2893 y = i;
2894 ty = dock->y_pos + y*ICON_SIZE;
2895 if (slot_map[XY2OFS(x,y)]==0
2896 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2897 *x_pos = x;
2898 *y_pos = y;
2899 done = 1;
2900 break;
2903 /* left and right parts of the ring */
2904 for (y = -i+1; y <= i-1; y++) {
2905 ty = dock->y_pos + y*ICON_SIZE;
2906 x = -i;
2907 tx = dock->x_pos + x*ICON_SIZE;
2908 if (slot_map[XY2OFS(x,y)]==0
2909 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2910 *x_pos = x;
2911 *y_pos = y;
2912 done = 1;
2913 break;
2915 x = i;
2916 tx = dock->x_pos + x*ICON_SIZE;
2917 if (slot_map[XY2OFS(x,y)]==0
2918 && onScreen(scr, tx, ty, sx, ex, sy, ey)) {
2919 *x_pos = x;
2920 *y_pos = y;
2921 done = 1;
2922 break;
2926 wfree(slot_map);
2927 #undef XY2OFS
2928 return done;
2932 static void
2933 moveDock(WDock *dock, int new_x, int new_y)
2935 WAppIcon *btn;
2936 int i;
2938 dock->x_pos = new_x;
2939 dock->y_pos = new_y;
2940 for (i=0; i<dock->max_icons; i++) {
2941 btn = dock->icon_array[i];
2942 if (btn) {
2943 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2944 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2945 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2951 static void
2952 swapDock(WDock *dock)
2954 WScreen *scr = dock->screen_ptr;
2955 WAppIcon *btn;
2956 int x, i;
2959 if (dock->on_right_side) {
2960 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2961 } else {
2962 x = dock->x_pos = DOCK_EXTRA_SPACE;
2965 for (i=0; i<dock->max_icons; i++) {
2966 btn = dock->icon_array[i];
2967 if (btn) {
2968 btn->x_pos = x;
2969 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2973 wScreenUpdateUsableArea(scr);
2977 static pid_t
2978 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2980 WScreen *scr = btn->icon->core->screen_ptr;
2981 pid_t pid;
2982 char **argv;
2983 int argc;
2984 char *cmdline;
2986 cmdline = ExpandOptions(scr, command);
2988 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2989 if (cmdline)
2990 wfree(cmdline);
2991 if (state)
2992 wfree(state);
2993 return 0;
2996 wtokensplit(cmdline, &argv, &argc);
2998 if (argv==NULL) {
2999 if (cmdline)
3000 wfree(cmdline);
3001 if (state)
3002 wfree(state);
3003 return 0;
3006 if ((pid=fork())==0) {
3007 char **args;
3008 int i;
3010 SetupEnvironment(scr);
3012 #ifdef HAVE_SETSID
3013 setsid();
3014 #endif
3016 args = malloc(sizeof(char*)*(argc+1));
3017 if (!args)
3018 exit(111);
3019 for (i=0; i<argc; i++) {
3020 args[i] = argv[i];
3022 args[argc] = NULL;
3023 execvp(argv[0], args);
3024 exit(111);
3026 wtokenfree(argv, argc);
3028 if (pid > 0) {
3029 if (!state) {
3030 state = wmalloc(sizeof(WSavedState));
3031 memset(state, 0, sizeof(WSavedState));
3032 state->hidden = -1;
3033 state->miniaturized = -1;
3034 state->shaded = -1;
3035 if (btn->dock==scr->dock || btn->omnipresent)
3036 state->workspace = -1;
3037 else
3038 state->workspace = scr->current_workspace;
3040 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
3041 state);
3042 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
3043 btn->dock);
3044 } else if (state) {
3045 wfree(state);
3047 wfree(cmdline);
3048 return pid;
3052 void
3053 wDockHideIcons(WDock *dock)
3055 int i;
3057 if (dock==NULL)
3058 return;
3060 for (i=1; i<dock->max_icons; i++) {
3061 if (dock->icon_array[i])
3062 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
3064 dock->mapped = 0;
3066 dockIconPaint(dock->icon_array[0]);
3070 void
3071 wDockShowIcons(WDock *dock)
3073 int i, newlevel;
3074 WAppIcon *btn;
3076 if (dock==NULL)
3077 return;
3079 btn = dock->icon_array[0];
3080 moveDock(dock, btn->x_pos, btn->y_pos);
3082 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
3083 ChangeStackingLevel(btn->icon->core, newlevel);
3085 for (i=1; i<dock->max_icons; i++) {
3086 if (dock->icon_array[i]) {
3087 MoveInStackListAbove(dock->icon_array[i]->icon->core,
3088 btn->icon->core);
3089 break;
3093 if (!dock->collapsed) {
3094 for (i=1; i<dock->max_icons; i++) {
3095 if (dock->icon_array[i]) {
3096 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
3100 dock->mapped = 1;
3102 dockIconPaint(btn);
3106 void
3107 wDockLower(WDock *dock)
3109 int i;
3111 for (i=0; i<dock->max_icons; i++) {
3112 if (dock->icon_array[i])
3113 wLowerFrame(dock->icon_array[i]->icon->core);
3118 void
3119 wDockRaise(WDock *dock)
3121 int i;
3123 for (i=dock->max_icons-1; i>=0; i--) {
3124 if (dock->icon_array[i])
3125 wRaiseFrame(dock->icon_array[i]->icon->core);
3130 void
3131 wDockRaiseLower(WDock *dock)
3133 if (!dock->icon_array[0]->icon->core->stacking->above
3134 ||(dock->icon_array[0]->icon->core->stacking->window_level
3135 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
3136 wDockLower(dock);
3137 else
3138 wDockRaise(dock);
3142 void
3143 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
3145 icon->launching = 0;
3146 icon->relaunching = 0;
3147 dockIconPaint(icon);
3151 WAppIcon*
3152 wDockFindIconForWindow(WDock *dock, Window window)
3154 WAppIcon *icon;
3155 int i;
3157 for (i=0; i<dock->max_icons; i++) {
3158 icon = dock->icon_array[i];
3159 if (icon && icon->main_window == window)
3160 return icon;
3162 return NULL;
3166 void
3167 wDockTrackWindowLaunch(WDock *dock, Window window)
3169 WAppIcon *icon;
3170 char *wm_class, *wm_instance;
3171 int i;
3172 Bool firstPass = True;
3173 Bool found = False;
3174 char *command = NULL;
3176 command = GetCommandForWindow(window);
3178 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
3179 (!wm_class && !wm_instance)) {
3181 if (command)
3182 wfree(command);
3183 return;
3186 retry:
3187 for (i=0; i<dock->max_icons; i++) {
3188 icon = dock->icon_array[i];
3189 if (!icon)
3190 continue;
3192 /* app is already attached to icon */
3193 if (icon->main_window == window) {
3194 found = True;
3195 break;
3198 if ((icon->wm_instance || icon->wm_class)
3199 && (icon->launching || !icon->running)) {
3201 if (icon->wm_instance && wm_instance &&
3202 strcmp(icon->wm_instance, wm_instance)!=0) {
3203 continue;
3205 if (icon->wm_class && wm_class &&
3206 strcmp(icon->wm_class, wm_class)!=0) {
3207 continue;
3209 if (firstPass && command && strcmp(icon->command, command)!=0) {
3210 continue;
3213 if (!icon->relaunching) {
3214 WApplication *wapp;
3216 /* Possibly an application that was docked with dockit,
3217 * but the user did not update WMState to indicate that
3218 * it was docked by force */
3219 wapp = wApplicationOf(window);
3220 if (!wapp) {
3221 icon->forced_dock = 1;
3222 icon->running = 0;
3224 if (!icon->forced_dock) {
3225 icon->main_window = window;
3228 found = True;
3229 if (!wPreferences.no_animations && !icon->launching &&
3230 !dock->screen_ptr->flags.startup && !dock->collapsed) {
3231 WAppIcon *aicon;
3232 int x0, y0;
3234 icon->launching = 1;
3235 dockIconPaint(icon);
3237 aicon = wAppIconCreateForDock(dock->screen_ptr, NULL,
3238 wm_instance, wm_class,
3239 TILE_NORMAL);
3240 /* XXX: can: aicon->icon == NULL ? */
3241 PlaceIcon(dock->screen_ptr, &x0, &y0, wGetHeadForWindow(aicon->icon->owner));
3242 wAppIconMove(aicon, x0, y0);
3243 /* Should this always be lowered? -Dan */
3244 if (dock->lowered)
3245 wLowerFrame(aicon->icon->core);
3246 XMapWindow(dpy, aicon->icon->core->window);
3247 aicon->launching = 1;
3248 wAppIconPaint(aicon);
3249 SlideWindow(aicon->icon->core->window, x0, y0,
3250 icon->x_pos, icon->y_pos);
3251 XUnmapWindow(dpy, aicon->icon->core->window);
3252 wAppIconDestroy(aicon);
3254 wDockFinishLaunch(dock, icon);
3255 break;
3259 if (firstPass && !found) {
3260 firstPass = False;
3261 goto retry;
3264 if (command)
3265 wfree(command);
3267 if (wm_class)
3268 XFree(wm_class);
3269 if (wm_instance)
3270 XFree(wm_instance);
3275 void
3276 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3278 if (!wPreferences.flags.noclip) {
3279 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3280 if (scr->current_workspace != workspace) {
3281 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3282 WAppIconChain *chain = scr->global_icons;
3284 while (chain) {
3285 moveIconBetweenDocks(chain->aicon->dock,
3286 scr->workspaces[workspace]->clip,
3287 chain->aicon, chain->aicon->xindex,
3288 chain->aicon->yindex);
3289 if (scr->workspaces[workspace]->clip->collapsed)
3290 XUnmapWindow(dpy, chain->aicon->icon->core->window);
3291 chain = chain->next;
3294 wDockHideIcons(old_clip);
3295 if (old_clip->auto_raise_lower) {
3296 if (old_clip->auto_raise_magic) {
3297 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3298 old_clip->auto_raise_magic = NULL;
3300 wDockLower(old_clip);
3302 if (old_clip->auto_collapse) {
3303 if (old_clip->auto_expand_magic) {
3304 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3305 old_clip->auto_expand_magic = NULL;
3307 old_clip->collapsed = 1;
3309 wDockShowIcons(scr->workspaces[workspace]->clip);
3311 if (scr->flags.clip_balloon_mapped)
3312 showClipBalloon(scr->clip_icon->dock, workspace);
3318 static void
3319 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3321 WAppIcon *icon;
3322 int i;
3324 for (i=0; i<dock->max_icons; i++) {
3325 icon = dock->icon_array[i];
3326 if (!icon)
3327 continue;
3329 if (icon->launching && icon->pid == pid) {
3330 if (!icon->relaunching) {
3331 icon->running = 0;
3332 icon->main_window = None;
3334 wDockFinishLaunch(dock, icon);
3335 icon->pid = 0;
3336 if (status==111) {
3337 char msg[PATH_MAX];
3338 char *cmd;
3340 if (icon->drop_launch)
3341 cmd = icon->dnd_command;
3342 else if (icon->paste_launch)
3343 cmd = icon->paste_command;
3344 else
3345 cmd = icon->command;
3347 snprintf(msg, sizeof(msg),
3348 _("Could not execute command \"%s\""), cmd);
3350 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3351 _("OK"), NULL, NULL);
3353 break;
3359 static void
3360 toggleLowered(WDock *dock)
3362 WAppIcon *tmp;
3363 int newlevel, i;
3365 /* lower/raise Dock */
3366 if (!dock->lowered) {
3367 newlevel = WMNormalLevel;
3368 dock->lowered = 1;
3369 } else {
3370 newlevel = WMDockLevel;
3371 dock->lowered = 0;
3374 for (i=0; i<dock->max_icons; i++) {
3375 tmp = dock->icon_array[i];
3376 if (!tmp)
3377 continue;
3379 ChangeStackingLevel(tmp->icon->core, newlevel);
3380 if (dock->lowered)
3381 wLowerFrame(tmp->icon->core);
3384 if (dock->type == WM_DOCK)
3385 wScreenUpdateUsableArea(dock->screen_ptr);
3389 static void
3390 toggleCollapsed(WDock *dock)
3392 if (dock->collapsed) {
3393 dock->collapsed = 0;
3394 wDockShowIcons(dock);
3396 else {
3397 dock->collapsed = 1;
3398 wDockHideIcons(dock);
3403 static void
3404 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3406 WScreen *scr = dock->screen_ptr;
3407 WObjDescriptor *desc;
3408 WMenuEntry *entry;
3409 WApplication *wapp = NULL;
3410 int index = 0;
3411 int x_pos;
3412 int n_selected;
3413 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3415 if (dock->type == WM_DOCK) {
3416 /* keep on top */
3417 entry = dock->menu->entries[index];
3418 entry->flags.indicator_on = !dock->lowered;
3419 entry->clientdata = dock;
3420 dock->menu->flags.realized = 0;
3421 } else {
3422 /* clip options */
3423 if (scr->clip_options)
3424 updateClipOptionsMenu(scr->clip_options, dock);
3426 n_selected = numberOfSelectedIcons(dock);
3428 /* Rename Workspace */
3429 entry = dock->menu->entries[++index];
3430 if (aicon == scr->clip_icon) {
3431 entry->callback = renameCallback;
3432 entry->clientdata = dock;
3433 entry->flags.indicator = 0;
3434 entry->text = _("Rename Workspace");
3435 } else {
3436 entry->callback = omnipresentCallback;
3437 entry->clientdata = aicon;
3438 if (n_selected > 0) {
3439 entry->flags.indicator = 0;
3440 entry->text = _("Toggle Omnipresent");
3441 } else {
3442 entry->flags.indicator = 1;
3443 entry->flags.indicator_on = aicon->omnipresent;
3444 entry->flags.indicator_type = MI_CHECK;
3445 entry->text = _("Omnipresent");
3449 /* select/unselect icon */
3450 entry = dock->menu->entries[++index];
3451 entry->clientdata = aicon;
3452 entry->flags.indicator_on = aicon->icon->selected;
3453 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3455 /* select/unselect all icons */
3456 entry = dock->menu->entries[++index];
3457 entry->clientdata = aicon;
3458 if (n_selected > 0)
3459 entry->text = _("Unselect All Icons");
3460 else
3461 entry->text = _("Select All Icons");
3462 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3464 /* keep icon(s) */
3465 entry = dock->menu->entries[++index];
3466 entry->clientdata = aicon;
3467 if (n_selected > 1)
3468 entry->text = _("Keep Icons");
3469 else
3470 entry->text = _("Keep Icon");
3471 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3473 /* this is the workspace submenu part */
3474 entry = dock->menu->entries[++index];
3475 if (n_selected > 1)
3476 entry->text = _("Move Icons To");
3477 else
3478 entry->text = _("Move Icon To");
3479 if (scr->clip_submenu)
3480 updateWorkspaceMenu(scr->clip_submenu, aicon);
3481 wMenuSetEnabled(dock->menu, index, !aicon->omnipresent);
3483 /* remove icon(s) */
3484 entry = dock->menu->entries[++index];
3485 entry->clientdata = aicon;
3486 if (n_selected > 1)
3487 entry->text = _("Remove Icons");
3488 else
3489 entry->text = _("Remove Icon");
3490 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3492 /* attract icon(s) */
3493 entry = dock->menu->entries[++index];
3494 entry->clientdata = aicon;
3496 dock->menu->flags.realized = 0;
3497 wMenuRealize(dock->menu);
3501 if (aicon->icon->owner) {
3502 wapp = wApplicationOf(aicon->icon->owner->main_window);
3503 } else {
3504 wapp = NULL;
3507 /* launch */
3508 entry = dock->menu->entries[++index];
3509 entry->clientdata = aicon;
3510 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3512 /* unhide here */
3513 entry = dock->menu->entries[++index];
3514 entry->clientdata = aicon;
3515 if (wapp && wapp->flags.hidden) {
3516 entry->text = _("Unhide Here");
3517 } else {
3518 entry->text = _("Bring Here");
3520 wMenuSetEnabled(dock->menu, index, appIsRunning);
3522 /* hide */
3523 entry = dock->menu->entries[++index];
3524 entry->clientdata = aicon;
3525 if (wapp && wapp->flags.hidden) {
3526 entry->text = _("Unhide");
3527 } else {
3528 entry->text = _("Hide");
3530 wMenuSetEnabled(dock->menu, index, appIsRunning);
3532 /* settings */
3533 entry = dock->menu->entries[++index];
3534 entry->clientdata = aicon;
3535 wMenuSetEnabled(dock->menu, index, !aicon->editing
3536 && !wPreferences.flags.noupdates);
3538 /* kill */
3539 entry = dock->menu->entries[++index];
3540 entry->clientdata = aicon;
3541 wMenuSetEnabled(dock->menu, index, appIsRunning);
3543 if (!dock->menu->flags.realized)
3544 wMenuRealize(dock->menu);
3546 if (dock->type == WM_CLIP) {
3547 /*x_pos = event->xbutton.x_root+2;*/
3548 x_pos = event->xbutton.x_root - dock->menu->frame->core->width/2 - 1;
3549 if (x_pos < 0) {
3550 x_pos = 0;
3551 } else if (x_pos + dock->menu->frame->core->width > scr->scr_width-2) {
3552 x_pos = scr->scr_width - dock->menu->frame->core->width - 4;
3554 } else {
3555 x_pos = dock->on_right_side ?
3556 scr->scr_width - dock->menu->frame->core->width - 3 : 0;
3559 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3561 /* allow drag select */
3562 event->xany.send_event = True;
3563 desc = &dock->menu->menu->descriptor;
3564 (*desc->handle_mousedown)(desc, event);
3568 /******************************************************************/
3569 static void
3570 iconDblClick(WObjDescriptor *desc, XEvent *event)
3572 WAppIcon *btn = desc->parent;
3573 WDock *dock = btn->dock;
3574 WApplication *wapp = NULL;
3575 int unhideHere = 0;
3577 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3578 wapp = wApplicationOf(btn->icon->owner->main_window);
3580 assert(wapp!=NULL);
3582 unhideHere = (event->xbutton.state & ShiftMask);
3584 /* go to the last workspace that the user worked on the app */
3585 if (wapp->last_workspace != dock->screen_ptr->current_workspace
3586 && !unhideHere) {
3587 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3590 wUnhideApplication(wapp, event->xbutton.button==Button2, unhideHere);
3592 if (event->xbutton.state & MOD_MASK) {
3593 wHideOtherApplications(btn->icon->owner);
3595 } else {
3596 if (event->xbutton.button==Button1) {
3598 if (event->xbutton.state & MOD_MASK) {
3599 /* raise/lower dock */
3600 toggleLowered(dock);
3601 } else if (btn == dock->screen_ptr->clip_icon) {
3602 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3603 toggleCollapsed(dock);
3604 else
3605 handleClipChangeWorkspace(dock->screen_ptr, event);
3606 } else if (btn->command) {
3607 if (!btn->launching &&
3608 (!btn->running || (event->xbutton.state & ControlMask))) {
3609 launchDockedApplication(btn, False);
3611 } else if (btn->xindex==0 && btn->yindex==0 &&
3612 btn->dock->type==WM_DOCK) {
3613 wShowGNUstepPanel(dock->screen_ptr);
3621 static void
3622 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3624 WScreen *scr = dock->screen_ptr;
3625 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3626 int x, y;
3627 XEvent ev;
3628 int grabbed = 0, swapped = 0, done;
3629 Pixmap ghost = None;
3630 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3632 #ifdef DEBUG
3633 puts("moving dock");
3634 #endif
3635 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3636 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3637 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3638 wwarning("pointer grab failed for dock move");
3640 y = 0;
3641 for (x=0; x<dock->max_icons; x++) {
3642 if (dock->icon_array[x]!=NULL &&
3643 dock->icon_array[x]->yindex > y)
3644 y = dock->icon_array[x]->yindex;
3646 y++;
3647 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3649 done = 0;
3650 while (!done) {
3651 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3652 |ButtonMotionMask|ExposureMask, &ev);
3653 switch (ev.type) {
3654 case Expose:
3655 WMHandleEvent(&ev);
3656 break;
3658 case MotionNotify:
3659 if (!grabbed) {
3660 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3661 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3662 XChangeActivePointerGrab(dpy, ButtonMotionMask
3663 |ButtonReleaseMask|ButtonPressMask,
3664 wCursor[WCUR_MOVE], CurrentTime);
3665 grabbed=1;
3667 break;
3669 if (dock->type == WM_CLIP) {
3670 x = ev.xmotion.x_root - ofs_x;
3671 y = ev.xmotion.y_root - ofs_y;
3672 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3674 moveDock(dock, x, y);
3675 } else {
3676 /* move vertically if pointer is inside the dock*/
3677 if ((dock->on_right_side &&
3678 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3679 || (!dock->on_right_side &&
3680 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3682 x = ev.xmotion.x_root - ofs_x;
3683 y = ev.xmotion.y_root - ofs_y;
3684 wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE);
3685 moveDock(dock, dock->x_pos, y);
3687 /* move horizontally to change sides */
3688 x = ev.xmotion.x_root - ofs_x;
3689 if (!dock->on_right_side) {
3691 /* is on left */
3693 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3694 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3695 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3696 if (superfluous && ghost==None) {
3697 ghost = MakeGhostDock(dock, dock->x_pos,
3698 scr->scr_width-ICON_SIZE
3699 -DOCK_EXTRA_SPACE-1,
3700 dock->y_pos);
3701 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3702 ghost);
3703 XClearWindow(dpy, scr->dock_shadow);
3705 XMapRaised(dpy, scr->dock_shadow);
3706 swapped = 1;
3707 } else {
3708 if (superfluous && ghost!=None) {
3709 XFreePixmap(dpy, ghost);
3710 ghost = None;
3712 XUnmapWindow(dpy, scr->dock_shadow);
3713 swapped = 0;
3715 } else {
3716 /* is on right */
3717 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3718 XMoveWindow(dpy, scr->dock_shadow,
3719 DOCK_EXTRA_SPACE, dock->y_pos);
3720 if (superfluous && ghost==None) {
3721 ghost = MakeGhostDock(dock, dock->x_pos,
3722 DOCK_EXTRA_SPACE, dock->y_pos);
3723 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3724 ghost);
3725 XClearWindow(dpy, scr->dock_shadow);
3727 XMapRaised(dpy, scr->dock_shadow);
3728 swapped = -1;
3729 } else {
3730 XUnmapWindow(dpy, scr->dock_shadow);
3731 swapped = 0;
3732 if (superfluous && ghost!=None) {
3733 XFreePixmap(dpy, ghost);
3734 ghost = None;
3739 break;
3741 case ButtonPress:
3742 break;
3744 case ButtonRelease:
3745 if (ev.xbutton.button != event->xbutton.button)
3746 break;
3747 XUngrabPointer(dpy, CurrentTime);
3748 XUnmapWindow(dpy, scr->dock_shadow);
3749 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3750 if (dock->type == WM_DOCK) {
3751 if (swapped!=0) {
3752 if (swapped>0)
3753 dock->on_right_side = 1;
3754 else
3755 dock->on_right_side = 0;
3756 swapDock(dock);
3757 wArrangeIcons(scr, False);
3760 done = 1;
3761 break;
3764 if (superfluous) {
3765 if (ghost!=None)
3766 XFreePixmap(dpy, ghost);
3767 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3769 #ifdef DEBUG
3770 puts("End dock move");
3771 #endif
3776 static void
3777 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3779 WScreen *scr = dock->screen_ptr;
3780 Window wins[2];
3781 WIcon *icon = aicon->icon;
3782 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3783 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3784 XEvent ev;
3785 int x = aicon->x_pos, y = aicon->y_pos;
3786 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3787 int shad_x = x, shad_y = y;
3788 int ix = aicon->xindex, iy = aicon->yindex;
3789 int tmp;
3790 Pixmap ghost = None;
3791 Bool docked;
3792 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3793 int omnipresent = aicon->omnipresent; /* this must be cached!!! */
3796 if (wPreferences.flags.noupdates)
3797 return;
3799 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3800 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3801 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3802 #ifdef DEBUG0
3803 wwarning("pointer grab failed for icon move");
3804 #endif
3807 if (!(event->xbutton.state & MOD_MASK))
3808 wRaiseFrame(icon->core);
3810 if (!wPreferences.flags.noclip)
3811 clip = scr->workspaces[scr->current_workspace]->clip;
3813 if (dock == scr->dock && !wPreferences.flags.noclip)
3814 dock2 = clip;
3815 else if (dock != scr->dock && !wPreferences.flags.nodock)
3816 dock2 = scr->dock;
3818 wins[0] = icon->core->window;
3819 wins[1] = scr->dock_shadow;
3820 XRestackWindows(dpy, wins, 2);
3821 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3822 ICON_SIZE, ICON_SIZE);
3823 if (superfluous) {
3824 if (icon->pixmap!=None)
3825 ghost = MakeGhostIcon(scr, icon->pixmap);
3826 else
3827 ghost = MakeGhostIcon(scr, icon->core->window);
3829 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3830 XClearWindow(dpy, scr->dock_shadow);
3832 XMapWindow(dpy, scr->dock_shadow);
3834 ondock = 1;
3837 while(1) {
3838 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3839 |ButtonMotionMask|ExposureMask, &ev);
3840 switch (ev.type) {
3841 case Expose:
3842 WMHandleEvent(&ev);
3843 break;
3845 case MotionNotify:
3846 if (!grabbed) {
3847 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3848 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3849 XChangeActivePointerGrab(dpy, ButtonMotionMask
3850 |ButtonReleaseMask|ButtonPressMask,
3851 wCursor[WCUR_MOVE], CurrentTime);
3852 grabbed=1;
3853 } else {
3854 break;
3858 if (omnipresent) {
3859 int i;
3860 for (i=0; i<scr->workspace_count; i++) {
3861 if (i == scr->current_workspace)
3862 continue;
3863 wDockShowIcons(scr->workspaces[i]->clip);
3867 x = ev.xmotion.x_root - ofs_x;
3868 y = ev.xmotion.y_root - ofs_y;
3869 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3870 if (tmp && dock2) {
3871 change_dock = 0;
3872 if (last_dock != dock && collapsed) {
3873 last_dock->collapsed = 1;
3874 wDockHideIcons(last_dock);
3875 collapsed = 0;
3877 if (!collapsed && (collapsed = dock->collapsed)) {
3878 dock->collapsed = 0;
3879 wDockShowIcons(dock);
3881 if (dock->auto_raise_lower)
3882 wDockRaise(dock);
3883 last_dock = dock;
3884 } else if (dock2) {
3885 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3886 if (tmp) {
3887 change_dock = 1;
3888 if (last_dock != dock2 && collapsed) {
3889 last_dock->collapsed = 1;
3890 wDockHideIcons(last_dock);
3891 collapsed = 0;
3893 if (!collapsed && (collapsed = dock2->collapsed)) {
3894 dock2->collapsed = 0;
3895 wDockShowIcons(dock2);
3897 if (dock2->auto_raise_lower)
3898 wDockRaise(dock2);
3899 last_dock = dock2;
3902 if (aicon->launching
3903 || aicon->lock
3904 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3905 || (!aicon->running && tmp)) {
3906 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3907 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3909 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3911 if (!ondock) {
3912 XMapWindow(dpy, scr->dock_shadow);
3914 ondock = 1;
3915 } else {
3916 if (ondock) {
3917 XUnmapWindow(dpy, scr->dock_shadow);
3919 ondock = 0;
3921 XMoveWindow(dpy, icon->core->window, x, y);
3922 break;
3924 case ButtonPress:
3925 break;
3927 case ButtonRelease:
3928 if (ev.xbutton.button != event->xbutton.button)
3929 break;
3930 XUngrabPointer(dpy, CurrentTime);
3931 if (ondock) {
3932 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3933 XUnmapWindow(dpy, scr->dock_shadow);
3934 if (!change_dock) {
3935 reattachIcon(dock, aicon, ix, iy);
3936 if (clip && dock!=clip && clip->auto_raise_lower)
3937 wDockLower(clip);
3938 } else {
3939 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3940 if (!docked) {
3941 /* Slide it back if dock rejected it */
3942 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3943 aicon->y_pos);
3944 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3946 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3947 collapsed = 0;
3950 } else {
3951 aicon->x_pos = x;
3952 aicon->y_pos = y;
3953 if (superfluous) {
3954 if (!aicon->running && !wPreferences.no_animations) {
3955 /* We need to deselect it, even if is deselected in
3956 * wDockDetach(), because else DoKaboom() will fail.
3958 if (aicon->icon->selected)
3959 wIconSelect(aicon->icon);
3961 wSoundPlay(WSOUND_KABOOM);
3962 DoKaboom(scr,aicon->icon->core->window, x, y);
3963 } else {
3964 wSoundPlay(WSOUND_UNDOCK);
3966 } else {
3967 wSoundPlay(WSOUND_UNDOCK);
3969 if (clip && clip->auto_raise_lower)
3970 wDockLower(clip);
3971 wDockDetach(dock, aicon);
3973 if (collapsed) {
3974 last_dock->collapsed = 1;
3975 wDockHideIcons(last_dock);
3976 collapsed = 0;
3978 if (superfluous) {
3979 if (ghost!=None)
3980 XFreePixmap(dpy, ghost);
3981 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3983 if (omnipresent) {
3984 int i;
3985 for (i=0; i<scr->workspace_count; i++) {
3986 if (i == scr->current_workspace)
3987 continue;
3988 wDockHideIcons(scr->workspaces[i]->clip);
3992 #ifdef DEBUG
3993 puts("End icon move");
3994 #endif
3995 return;
4001 static int
4002 getClipButton(int px, int py)
4004 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
4006 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
4007 return CLIP_IDLE;
4009 if (py <= pt-((int)ICON_SIZE-1-px))
4010 return CLIP_FORWARD;
4011 else if (px <= pt-((int)ICON_SIZE-1-py))
4012 return CLIP_REWIND;
4014 return CLIP_IDLE;
4018 static void
4019 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
4021 XEvent ev;
4022 int done, direction, new_ws;
4023 int new_dir;
4024 WDock *clip = scr->clip_icon->dock;
4026 direction = getClipButton(event->xbutton.x, event->xbutton.y);
4028 clip->lclip_button_pushed = direction==CLIP_REWIND;
4029 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4031 wClipIconPaint(scr->clip_icon);
4032 done = 0;
4033 while(!done) {
4034 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
4035 |ButtonPressMask, &ev);
4036 switch (ev.type) {
4037 case Expose:
4038 WMHandleEvent(&ev);
4039 break;
4041 case MotionNotify:
4042 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
4043 if (new_dir != direction) {
4044 direction = new_dir;
4045 clip->lclip_button_pushed = direction==CLIP_REWIND;
4046 clip->rclip_button_pushed = direction==CLIP_FORWARD;
4047 wClipIconPaint(scr->clip_icon);
4049 break;
4051 case ButtonPress:
4052 break;
4054 case ButtonRelease:
4055 if (ev.xbutton.button == event->xbutton.button)
4056 done = 1;
4060 clip->lclip_button_pushed = 0;
4061 clip->rclip_button_pushed = 0;
4063 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
4065 if (direction == CLIP_FORWARD) {
4066 if (scr->current_workspace < scr->workspace_count-1)
4067 wWorkspaceChange(scr, scr->current_workspace+1);
4068 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
4069 wWorkspaceChange(scr, scr->current_workspace+1);
4070 else if (wPreferences.ws_cycle)
4071 wWorkspaceChange(scr, 0);
4073 else if (direction == CLIP_REWIND) {
4074 if (scr->current_workspace > 0)
4075 wWorkspaceChange(scr, scr->current_workspace-1);
4076 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
4077 wWorkspaceChange(scr, scr->workspace_count-1);
4080 wClipIconPaint(scr->clip_icon);
4084 static void
4085 iconMouseDown(WObjDescriptor *desc, XEvent *event)
4087 WAppIcon *aicon = desc->parent;
4088 WDock *dock = aicon->dock;
4089 WScreen *scr = aicon->icon->core->screen_ptr;
4091 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
4092 return;
4094 scr->last_dock = dock;
4096 if (dock->menu->flags.mapped)
4097 wMenuUnmap(dock->menu);
4099 if (IsDoubleClick(scr, event)) {
4100 /* double-click was not in the main clip icon */
4101 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
4102 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
4103 iconDblClick(desc, event);
4104 return;
4108 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
4109 XUnmapWindow(dpy, scr->clip_balloon);
4110 scr->flags.clip_balloon_mapped = 0;
4113 #ifdef DEBUG
4114 puts("handling dock");
4115 #endif
4116 if (event->xbutton.button == Button1) {
4117 if (event->xbutton.state & MOD_MASK)
4118 wDockLower(dock);
4119 else
4120 wDockRaise(dock);
4122 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
4123 dock->type!=WM_DOCK) {
4124 wIconSelect(aicon->icon);
4125 return;
4128 if (aicon->yindex==0 && aicon->xindex==0) {
4129 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
4130 && dock->type==WM_CLIP)
4131 handleClipChangeWorkspace(scr, event);
4132 else
4133 handleDockMove(dock, aicon, event);
4134 } else
4135 handleIconMove(dock, aicon, event);
4137 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4138 aicon==scr->clip_icon) {
4139 if (!scr->clip_ws_menu) {
4140 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
4142 if (scr->clip_ws_menu) {
4143 WMenu *wsMenu = scr->clip_ws_menu;
4144 int xpos;
4146 wWorkspaceMenuUpdate(scr, wsMenu);
4148 xpos = event->xbutton.x_root - wsMenu->frame->core->width/2 - 1;
4149 if (xpos < 0) {
4150 xpos = 0;
4151 } else if (xpos + wsMenu->frame->core->width > scr->scr_width-2) {
4152 xpos = scr->scr_width - wsMenu->frame->core->width - 4;
4154 wMenuMapAt(wsMenu, xpos, event->xbutton.y_root+2, False);
4156 desc = &wsMenu->menu->descriptor;
4157 event->xany.send_event = True;
4158 (*desc->handle_mousedown)(desc, event);
4160 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
4161 (event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon) {
4162 wClipMakeIconOmnipresent(aicon, !aicon->omnipresent);
4163 } else if (event->xbutton.button == Button3) {
4164 if (event->xbutton.send_event &&
4165 XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
4166 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
4167 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
4168 wwarning("pointer grab failed for dockicon menu");
4169 return;
4172 openDockMenu(dock, aicon, event);
4173 } else if (event->xbutton.button == Button2) {
4174 WAppIcon *btn = desc->parent;
4176 if (!btn->launching &&
4177 (!btn->running || (event->xbutton.state & ControlMask))) {
4178 launchDockedApplication(btn, True);
4184 static void
4185 showClipBalloon(WDock *dock, int workspace)
4187 int w, h;
4188 int x, y;
4189 WScreen *scr = dock->screen_ptr;
4190 char *text;
4191 Window stack[2];
4193 scr->flags.clip_balloon_mapped = 1;
4194 XMapWindow(dpy, scr->clip_balloon);
4196 text = scr->workspaces[workspace]->name;
4198 w = WMWidthOfString(scr->clip_title_font, text, strlen(text));
4200 h = WMFontHeight(scr->clip_title_font);
4201 XResizeWindow(dpy, scr->clip_balloon, w, h);
4203 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
4204 y = dock->y_pos + ICON_SIZE - WMFontHeight(scr->clip_title_font) - 3;
4206 if (x+w > scr->scr_width) {
4207 x = scr->scr_width - w;
4208 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
4209 y = dock->y_pos - h - 1;
4210 else
4211 y = dock->y_pos + ICON_SIZE;
4212 XRaiseWindow(dpy, scr->clip_balloon);
4213 } else {
4214 stack[0] = scr->clip_icon->icon->core->window;
4215 stack[1] = scr->clip_balloon;
4216 XRestackWindows(dpy, stack, 2);
4218 XMoveWindow(dpy, scr->clip_balloon, x, y);
4219 XClearWindow(dpy, scr->clip_balloon);
4220 WMDrawString(scr->wmscreen, scr->clip_balloon,
4221 scr->clip_title_color[CLIP_NORMAL],
4222 scr->clip_title_font,
4223 0, 0, text, strlen(text));
4227 static void
4228 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
4230 WAppIcon *btn = (WAppIcon*)desc->parent;
4231 WDock *dock;
4232 WScreen *scr;
4234 assert(event->type==EnterNotify);
4236 if(desc->parent_type!=WCLASS_DOCK_ICON)
4237 return;
4239 scr = btn->icon->core->screen_ptr;
4240 if (!btn->omnipresent)
4241 dock = btn->dock;
4242 else
4243 dock = scr->workspaces[scr->current_workspace]->clip;
4245 if (!dock || dock->type!=WM_CLIP)
4246 return;
4248 /* The auto raise/lower code */
4249 if (dock->auto_lower_magic) {
4250 WMDeleteTimerHandler(dock->auto_lower_magic);
4251 dock->auto_lower_magic = NULL;
4253 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
4254 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
4255 clipAutoRaise,
4256 (void *)dock);
4259 /* The auto expand/collapse code */
4260 if (dock->auto_collapse_magic) {
4261 WMDeleteTimerHandler(dock->auto_collapse_magic);
4262 dock->auto_collapse_magic = NULL;
4264 if (dock->auto_collapse && !dock->auto_expand_magic) {
4265 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
4266 clipAutoExpand,
4267 (void *)dock);
4270 if (btn->xindex == 0 && btn->yindex == 0)
4271 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4272 else {
4273 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4274 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
4275 dock->screen_ptr->flags.clip_balloon_mapped = 0;
4281 static void
4282 clipLeave(WDock *dock)
4284 XEvent event;
4285 WObjDescriptor *desc = NULL;
4287 if (!dock || dock->type!=WM_CLIP)
4288 return;
4290 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
4291 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
4292 (XPointer *)&desc)!=XCNOENT
4293 && desc && desc->parent_type==WCLASS_DOCK_ICON
4294 && ((WAppIcon*)desc->parent)->dock
4295 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
4296 /* We didn't left the Clip yet */
4297 XPutBackEvent(dpy, &event);
4298 return;
4301 XPutBackEvent(dpy, &event);
4302 } else {
4303 /* We entered a withdrawn window, so we're still in Clip */
4304 return;
4307 if (dock->auto_raise_magic) {
4308 WMDeleteTimerHandler(dock->auto_raise_magic);
4309 dock->auto_raise_magic = NULL;
4311 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
4312 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
4313 clipAutoLower,
4314 (void *)dock);
4317 if (dock->auto_expand_magic) {
4318 WMDeleteTimerHandler(dock->auto_expand_magic);
4319 dock->auto_expand_magic = NULL;
4321 if (dock->auto_collapse && !dock->auto_collapse_magic) {
4322 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
4323 clipAutoCollapse,
4324 (void *)dock);
4329 static void
4330 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
4332 WAppIcon *btn = (WAppIcon*)desc->parent;
4334 assert(event->type==LeaveNotify);
4336 if(desc->parent_type!=WCLASS_DOCK_ICON)
4337 return;
4339 clipLeave(btn->dock);
4343 static void
4344 clipAutoCollapse(void *cdata)
4346 WDock *dock = (WDock *)cdata;
4348 if (dock->type!=WM_CLIP)
4349 return;
4351 if (dock->auto_collapse) {
4352 dock->collapsed = 1;
4353 wDockHideIcons(dock);
4355 dock->auto_collapse_magic = NULL;
4359 static void
4360 clipAutoExpand(void *cdata)
4362 WDock *dock = (WDock *)cdata;
4364 if (dock->type!=WM_CLIP)
4365 return;
4367 if (dock->auto_collapse) {
4368 dock->collapsed = 0;
4369 wDockShowIcons(dock);
4371 dock->auto_expand_magic = NULL;
4375 static void
4376 clipAutoLower(void *cdata)
4378 WDock *dock = (WDock *)cdata;
4380 if (dock->type!=WM_CLIP)
4381 return;
4383 if (dock->auto_raise_lower)
4384 wDockLower(dock);
4386 dock->auto_lower_magic = NULL;
4390 static void
4391 clipAutoRaise(void *cdata)
4393 WDock *dock = (WDock *)cdata;
4395 if (dock->type!=WM_CLIP)
4396 return;
4398 if (dock->auto_raise_lower)
4399 wDockRaise(dock);
4401 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4402 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4405 dock->auto_raise_magic = NULL;
4409 static Bool
4410 iconCanBeOmnipresent(WAppIcon *aicon)
4412 WScreen *scr = aicon->icon->core->screen_ptr;
4413 WDock *clip;
4414 WAppIcon *btn;
4415 int i, j;
4417 for (i=0; i<scr->workspace_count; i++) {
4418 clip = scr->workspaces[i]->clip;
4420 if (clip == aicon->dock)
4421 continue;
4423 if (clip->icon_count + scr->global_icon_count >= clip->max_icons)
4424 return False; /* Clip is full in some workspace */
4426 for (j=0; j<clip->max_icons; j++) {
4427 btn = clip->icon_array[j];
4428 if(btn && btn->xindex==aicon->xindex && btn->yindex==aicon->yindex)
4429 return False;
4433 return True;
4438 wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent)
4440 WScreen *scr = aicon->icon->core->screen_ptr;
4441 WAppIconChain *new_entry, *tmp, *tmp1;
4442 int status = WO_SUCCESS;
4444 if ((scr->dock && aicon->dock==scr->dock) || aicon==scr->clip_icon) {
4445 return WO_NOT_APPLICABLE;
4448 if (aicon->omnipresent == omnipresent)
4449 return WO_SUCCESS;
4451 if (omnipresent) {
4452 if (iconCanBeOmnipresent(aicon)) {
4453 aicon->omnipresent = 1;
4454 new_entry = wmalloc(sizeof(WAppIconChain));
4455 new_entry->aicon = aicon;
4456 new_entry->next = scr->global_icons;
4457 scr->global_icons = new_entry;
4458 scr->global_icon_count++;
4459 } else {
4460 aicon->omnipresent = 0;
4461 status = WO_FAILED;
4463 } else {
4464 aicon->omnipresent = 0;
4465 if (aicon == scr->global_icons->aicon) {
4466 tmp = scr->global_icons->next;
4467 wfree(scr->global_icons);
4468 scr->global_icons = tmp;
4469 scr->global_icon_count--;
4470 } else {
4471 tmp = scr->global_icons;
4472 while (tmp->next) {
4473 if (tmp->next->aicon == aicon) {
4474 tmp1 = tmp->next->next;
4475 wfree(tmp->next);
4476 tmp->next = tmp1;
4477 scr->global_icon_count--;
4478 break;
4480 tmp = tmp->next;
4485 wAppIconPaint(aicon);
4487 return status;