*** empty log message ***
[wmaker-crm.git] / src / dock.c
blob8805dfdebab894aba46f6a520e98011a2d6bbf2c
1 /* dock.c- built-in Dock module for WindowMaker
2 *
3 * Window Maker window manager
4 *
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 #include "wconfig.h"
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <math.h>
32 #include <limits.h>
34 #ifndef PATH_MAX
35 #define PATH_MAX DEFAULT_PATH_MAX
36 #endif
38 #include "WindowMaker.h"
39 #include "wcore.h"
40 #include "window.h"
41 #include "icon.h"
42 #include "appicon.h"
43 #include "actions.h"
44 #include "stacking.h"
45 #include "dock.h"
46 #include "dialog.h"
47 #include "funcs.h"
48 #include "properties.h"
49 #include "menu.h"
50 #include "client.h"
51 #include "defaults.h"
52 #include "workspace.h"
53 #include "framewin.h"
54 #include "superfluous.h"
56 #include "list.h"
58 #include <proplist.h>
63 /**** Local variables ****/
64 #define CLIP_REWIND 1
65 #define CLIP_IDLE 0
66 #define CLIP_FORWARD 2
68 #define CLIP_BUTTON_SIZE 23
71 /**** Global variables ****/
73 /* in dockedapp.c */
74 extern void DestroyDockAppSettingsPanel();
76 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
79 extern XContext wWinContext;
81 extern Cursor wCursor[WCUR_LAST];
83 extern WPreferences wPreferences;
85 extern XContext wWinContext;
87 #ifdef OFFIX_DND
88 extern Atom _XA_DND_PROTOCOL;
89 #endif
92 #define MOD_MASK wPreferences.modifier_mask
94 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
96 #define ICON_SIZE wPreferences.icon_size
99 /***** Local variables ****/
101 static proplist_t dCommand=NULL;
102 #ifdef OFFIX_DND
103 static proplist_t dDropCommand=NULL;
104 #endif
105 static proplist_t dAutoLaunch, dName, dForced, dBuggyApplication, dYes, dNo;
106 static proplist_t dHost, dDock, dClip;
107 static proplist_t dAutoAttractIcons, dKeepAttracted;
109 static proplist_t dPosition, dApplications, dLowered, dCollapsed, dAutoCollapse;
111 static proplist_t dAutoRaiseLower;
113 static void dockIconPaint(WAppIcon *btn);
115 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
117 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
119 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
121 static int getClipButton(int px, int py);
123 static void toggleLowered(WDock *dock);
125 static void toggleCollapsed(WDock *dock);
127 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
129 static void clipLeave(WDock *dock);
131 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
133 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
135 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
136 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
137 static void clipAutoCollapse(void *cdata);
138 static void clipAutoExpand(void *cdata);
139 static void launchDockedApplication(WAppIcon *btn);
141 static void clipAutoLower(void *cdata);
142 static void clipAutoRaise(void *cdata);
144 static void showClipBalloon(WDock *dock, int workspace);
146 #ifdef OFFIX_DND
148 #define DndNotDnd -1
149 #define DndUnknown 0
150 #define DndRawData 1
151 #define DndFile 2
152 #define DndFiles 3
153 #define DndText 4
154 #define DndDir 5
155 #define DndLink 6
156 #define DndExe 7
158 #define DndEND 8
160 #endif /* OFFIX_DND */
164 static void
165 make_keys()
167 if (dCommand!=NULL)
168 return;
170 dCommand = PLRetain(PLMakeString("Command"));
171 #ifdef OFFIX_DND
172 dDropCommand = PLRetain(PLMakeString("DropCommand"));
173 #endif
174 dAutoLaunch = PLRetain(PLMakeString("AutoLaunch"));
175 dName = PLRetain(PLMakeString("Name"));
176 dForced = PLRetain(PLMakeString("Forced"));
177 dBuggyApplication = PLRetain(PLMakeString("BuggyApplication"));
178 dYes = PLRetain(PLMakeString("Yes"));
179 dNo = PLRetain(PLMakeString("No"));
180 dHost = PLRetain(PLMakeString("Host"));
182 dPosition = PLMakeString("Position");
183 dApplications = PLMakeString("Applications");
184 dLowered = PLMakeString("Lowered");
185 dCollapsed = PLMakeString("Collapsed");
186 dAutoCollapse = PLMakeString("AutoCollapse");
187 dAutoRaiseLower = PLMakeString("AutoRaiseLower");
188 dAutoAttractIcons = PLMakeString("AutoAttractIcons");
189 dKeepAttracted = PLMakeString("KeepAttracted");
191 dDock = PLMakeString("Dock");
192 dClip = PLMakeString("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 sprintf(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 free(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);
236 static void
237 killCallback(WMenu *menu, WMenuEntry *entry)
239 WAppIcon *icon;
240 #ifdef REDUCE_APPICONS
241 WAppIconAppList *tapplist;
243 extern Atom _XA_WM_DELETE_WINDOW;
244 #else
245 char *buffer;
246 #endif
248 if (!WCHECK_STATE(WSTATE_NORMAL))
249 return;
251 assert(entry->clientdata!=NULL);
253 icon = (WAppIcon*)entry->clientdata;
255 icon->editing = 1;
257 WCHANGE_STATE(WSTATE_MODAL);
259 #ifdef REDUCE_APPICONS
260 /* Send a delete message to the main window of each application
261 * bound to this docked appicon. - cls
263 tapplist = icon->applist;
264 while (tapplist != NULL) {
265 if (tapplist->wapp->main_window_desc != NULL) {
266 if (tapplist->wapp->main_window_desc->protocols.DELETE_WINDOW) {
267 wClientSendProtocol(tapplist->wapp->main_window_desc,
268 _XA_WM_DELETE_WINDOW, CurrentTime);
269 } else {
270 wClientKill(tapplist->wapp->main_window_desc);
273 tapplist = tapplist->next;
275 #else
276 buffer = wstrappend(icon->wm_class,
277 _(" will be forcibly closed.\n"
278 "Any unsaved changes will be lost.\n"
279 "Please confirm."));
281 if (wPreferences.dont_confirm_kill
282 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
283 buffer, _("Yes"), _("No"), NULL)==WAPRDefault) {
284 if (icon->icon && icon->icon->owner) {
285 wClientKill(icon->icon->owner);
288 #endif /* !REDUCE_APPICONS */
290 icon->editing = 0;
292 WCHANGE_STATE(WSTATE_NORMAL);}
295 static LinkedList*
296 getSelected(WDock *dock)
298 LinkedList *ret=NULL;
299 WAppIcon *btn;
300 int i;
302 for (i=1; i<dock->max_icons; i++) {
303 btn = dock->icon_array[i];
304 if (btn && btn->icon->selected) {
305 ret = list_cons(btn, ret);
309 return ret;
313 static void
314 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
316 Window win = clipIcon->icon->core->window;
317 WScreen *scr = clipIcon->icon->core->screen_ptr;
318 XPoint p[4];
319 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
320 int tp = ICON_SIZE - pt;
321 int as = pt - 15; /* 15 = 5+5+5 */
324 if (rpushed) {
325 p[0].x = tp+1;
326 p[0].y = 1;
327 p[1].x = ICON_SIZE-2;
328 p[1].y = 1;
329 p[2].x = ICON_SIZE-2;
330 p[2].y = pt-1;
331 } else if (lpushed) {
332 p[0].x = 1;
333 p[0].y = tp;
334 p[1].x = pt;
335 p[1].y = ICON_SIZE-2;
336 p[2].x = 1;
337 p[2].y = ICON_SIZE-2;
339 if (lpushed || rpushed) {
340 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
341 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
342 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
345 p[0].x = p[3].x = ICON_SIZE-6-as;
346 p[0].y = p[3].y = 5;
347 p[1].x = ICON_SIZE-6;
348 p[1].y = 5;
349 p[2].x = ICON_SIZE-6;
350 p[2].y = 5+as;
351 if (rpushed) {
352 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
353 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
354 } else {
355 XFillPolygon(dpy, win, scr->clip_title_gc, p,3,Convex,CoordModeOrigin);
356 XDrawLines(dpy, win, scr->clip_title_gc, p,4,CoordModeOrigin);
359 p[0].x = p[3].x = 5;
360 p[0].y = p[3].y = ICON_SIZE-6-as;
361 p[1].x = 5;
362 p[1].y = ICON_SIZE-6;
363 p[2].x = 5+as;
364 p[2].y = ICON_SIZE-6;
365 if (lpushed) {
366 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
367 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
368 } else {
369 XFillPolygon(dpy, win, scr->clip_title_gc, p,3,Convex,CoordModeOrigin);
370 XDrawLines(dpy, win, scr->clip_title_gc, p,4,CoordModeOrigin);
375 RImage*
376 wClipMakeTile(WScreen *scr, RImage *normalTile)
378 RImage *tile = RCloneImage(normalTile);
379 RColor black;
380 RColor dark;
381 RColor light;
382 int pt, tp;
384 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
385 tp = wPreferences.icon_size-1 - pt;
387 black.alpha = 255;
388 black.red = black.green = black.blue = 0;
390 dark.alpha = 0;
391 dark.red = dark.green = dark.blue = 80;
393 light.alpha = 0;
394 light.red = light.green = light.blue = 80;
397 /* top right */
398 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
399 pt-1, &dark);
400 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
401 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
402 pt, &light);
405 /* bottom left */
406 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
407 wPreferences.icon_size-3, &dark);
408 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
409 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
410 wPreferences.icon_size-2, &light);
412 return tile;
416 static void
417 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
419 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
420 WDock *dock;
421 WAppIcon *aicon;
422 LinkedList *selectedIcons;
423 int keepit;
425 assert(clickedIcon!=NULL);
427 dock = clickedIcon->dock;
429 selectedIcons = getSelected(dock);
431 if (selectedIcons) {
432 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
433 _("All selected icons will be removed!"),
434 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
435 return;
437 } else {
438 if (clickedIcon->xindex==0 && clickedIcon->yindex==0)
439 return;
440 selectedIcons = list_cons(clickedIcon, NULL);
443 while (selectedIcons) {
444 aicon = selectedIcons->head;
445 keepit = aicon->running && wApplicationOf(aicon->main_window);
446 wDockDetach(dock, aicon);
447 if (keepit) {
448 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
449 XMoveWindow(dpy, aicon->icon->core->window,
450 aicon->x_pos, aicon->y_pos);
451 if (!dock->mapped || dock->collapsed)
452 XMapWindow(dpy, aicon->icon->core->window);
454 list_remove_head(&selectedIcons);
457 if (wPreferences.auto_arrange_icons)
458 wArrangeIcons(dock->screen_ptr, True);
462 static void
463 keepIconsCallback(WMenu *menu, WMenuEntry *entry)
465 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
466 WDock *dock;
467 WAppIcon *aicon;
468 LinkedList *selectedIcons;
470 assert(clickedIcon!=NULL);
471 dock = clickedIcon->dock;
473 selectedIcons = getSelected(dock);
475 if (!selectedIcons && clickedIcon!=dock->screen_ptr->clip_icon) {
476 char *command = NULL;
478 if (!clickedIcon->command && !clickedIcon->editing) {
479 clickedIcon->editing = 1;
480 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
481 _("Type the command used to launch the application"),
482 &command)) {
483 if (command && (command[0]==0 ||
484 (command[0]=='-' && command[1]==0))) {
485 free(command);
486 command = NULL;
488 clickedIcon->command = command;
489 clickedIcon->editing = 0;
490 } else {
491 clickedIcon->editing = 0;
492 if (command)
493 free(command);
494 return;
498 selectedIcons = list_cons(clickedIcon, NULL);
501 while (selectedIcons) {
502 aicon = selectedIcons->head;
503 if (aicon->icon->selected)
504 wIconSelect(aicon->icon);
505 if (aicon && aicon->attracted && aicon->command) {
506 aicon->attracted = 0;
507 if (aicon->icon->shadowed) {
508 aicon->icon->shadowed = 0;
509 aicon->icon->force_paint = 1;
510 wAppIconPaint(aicon);
513 list_remove_head(&selectedIcons);
520 static void
521 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
523 WDock *dock = (WDock*)entry->clientdata;
525 assert(entry->clientdata!=NULL);
527 dock->attract_icons = !dock->attract_icons;
528 /*if (!dock->attract_icons)
529 dock->keep_attracted = 0;*/
531 entry->flags.indicator_on = dock->attract_icons;
533 wMenuPaint(menu);
537 static void
538 toggleKeepCallback(WMenu *menu, WMenuEntry *entry)
540 WDock *dock = (WDock*)entry->clientdata;
541 WAppIcon *btn;
542 int i;
544 assert(entry->clientdata!=NULL);
546 dock->keep_attracted = !dock->keep_attracted;
548 if (dock->keep_attracted) {
549 for (i=0; i< dock->max_icons; i++) {
550 btn = dock->icon_array[i];
551 if (btn && btn->attracted && btn->command) {
552 btn->attracted = 0;
553 if (btn->icon->shadowed) {
554 btn->icon->shadowed = 0;
555 btn->icon->force_paint = 1;
556 wAppIconPaint(btn);
562 entry->flags.indicator_on = dock->keep_attracted;
564 wMenuPaint(menu);
568 static void
569 selectCallback(WMenu *menu, WMenuEntry *entry)
571 WAppIcon *icon = (WAppIcon*)entry->clientdata;
573 assert(icon!=NULL);
575 wIconSelect(icon->icon);
577 wMenuPaint(menu);
581 static void
582 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
584 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
585 WDock *clip;
586 WAppIcon *aicon;
587 int x, y, x_pos, y_pos;
589 assert(entry->clientdata!=NULL);
590 clip = clickedIcon->dock;
592 aicon = clip->screen_ptr->app_icon_list;
594 while (aicon) {
595 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
596 x_pos = clip->x_pos + x*ICON_SIZE;
597 y_pos = clip->y_pos + y*ICON_SIZE;
598 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
599 #ifdef ANIMATIONS
600 if (wPreferences.no_animations) {
601 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
602 } else {
603 SlideWindow(aicon->icon->core->window,
604 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
606 #else
607 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
608 #endif /* ANIMATIONS */
610 aicon->attracted = 1;
611 if (!clip->keep_attracted && !aicon->icon->shadowed) {
612 aicon->icon->shadowed = 1;
613 aicon->icon->force_paint = 1;
614 /* We don't do an wAppIconPaint() here because it's in
615 * wDockAttachIcon(). -Dan
618 wDockAttachIcon(clip, aicon, x, y);
619 if (clip->collapsed || !clip->mapped)
620 XUnmapWindow(dpy, aicon->icon->core->window);
622 aicon = aicon->next;
627 static void
628 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
630 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
631 WDock *dock;
632 LinkedList *selectedIcons;
633 WAppIcon *btn;
634 int i;
636 assert(clickedIcon!=NULL);
637 dock = clickedIcon->dock;
639 selectedIcons = getSelected(dock);
641 if (!selectedIcons) {
642 for (i=1; i<dock->max_icons; i++) {
643 btn = dock->icon_array[i];
644 if (btn && !btn->icon->selected) {
645 wIconSelect(btn->icon);
648 } else {
649 while(selectedIcons) {
650 btn = selectedIcons->head;
651 wIconSelect(btn->icon);
652 list_remove_head(&selectedIcons);
656 wMenuPaint(menu);
660 static void
661 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
663 assert(entry->clientdata!=NULL);
665 toggleCollapsed(entry->clientdata);
667 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
669 wMenuPaint(menu);
673 static void
674 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
676 WDock *dock;
677 assert(entry->clientdata!=NULL);
679 dock = (WDock*) entry->clientdata;
681 dock->auto_collapse = !dock->auto_collapse;
683 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
685 wMenuPaint(menu);
689 static void
690 toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry)
692 WDock *dock;
693 assert(entry->clientdata!=NULL);
695 dock = (WDock*) entry->clientdata;
697 dock->auto_raise_lower = !dock->auto_raise_lower;
699 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_raise_lower;
701 wMenuPaint(menu);
705 static void
706 launchCallback(WMenu *menu, WMenuEntry *entry)
708 WAppIcon *btn = (WAppIcon*)entry->clientdata;
710 launchDockedApplication(btn);
714 static void
715 settingsCallback(WMenu *menu, WMenuEntry *entry)
717 WAppIcon *btn = (WAppIcon*)entry->clientdata;
719 if (btn->editing)
720 return;
721 ShowDockAppSettingsPanel(btn);
725 static void
726 hideCallback(WMenu *menu, WMenuEntry *entry)
728 WApplication *wapp;
729 WAppIcon *btn = (WAppIcon*)entry->clientdata;
731 wapp = wApplicationOf(btn->icon->owner->main_window);
733 if (wapp->flags.hidden) {
734 wWorkspaceChange(btn->icon->core->screen_ptr,wapp->last_workspace);
735 wUnhideApplication(wapp, False, False);
736 } else {
737 wHideApplication(wapp);
742 static void
743 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
745 WApplication *wapp;
746 WAppIcon *btn = (WAppIcon*)entry->clientdata;
748 wapp = wApplicationOf(btn->icon->owner->main_window);
750 wUnhideApplication(wapp, False, True);
754 WAppIcon*
755 mainIconCreate(WScreen *scr, int type)
757 WAppIcon *btn;
758 int x_pos;
760 if (type == WM_CLIP) {
761 if (scr->clip_icon)
762 return scr->clip_icon;
763 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
764 btn->icon->core->descriptor.handle_expose = clipIconExpose;
765 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
766 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
767 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
768 x_pos = 0;
769 } else {
770 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
771 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
774 btn->xindex = 0;
775 btn->yindex = 0;
777 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
778 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
779 btn->icon->core->descriptor.parent = btn;
780 /*ChangeStackingLevel(btn->icon->core, WMDockLevel);*/
781 XMapWindow(dpy, btn->icon->core->window);
782 btn->x_pos = x_pos;
783 btn->y_pos = 0;
784 btn->docked = 1;
785 if (type == WM_CLIP)
786 scr->clip_icon = btn;
788 return btn;
792 static void
793 switchWSCommand(WMenu *menu, WMenuEntry *entry)
795 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
796 WScreen *scr = icon->icon->core->screen_ptr;
797 WDock *src, *dest;
798 LinkedList *selectedIcons;
799 int x, y;
801 if (entry->order == scr->current_workspace)
802 return;
803 src = icon->dock;
804 dest = scr->workspaces[entry->order]->clip;
806 selectedIcons = getSelected(src);
808 if (selectedIcons) {
809 while(selectedIcons) {
810 btn = selectedIcons->head;
811 if (wDockFindFreeSlot(dest, &x, &y)) {
812 moveIconBetweenDocks(src, dest, btn, x, y);
813 XUnmapWindow(dpy, btn->icon->core->window);
815 list_remove_head(&selectedIcons);
817 } else if (icon != scr->clip_icon) {
818 if (wDockFindFreeSlot(dest, &x, &y)) {
819 moveIconBetweenDocks(src, dest, icon, x, y);
820 XUnmapWindow(dpy, icon->icon->core->window);
827 static void
828 launchDockedApplication(WAppIcon *btn)
830 WScreen *scr = btn->icon->core->screen_ptr;
832 if (!btn->launching && btn->command!=NULL) {
833 if (!btn->forced_dock) {
834 btn->relaunching = btn->running;
835 btn->running = 1;
837 if (btn->wm_instance || btn->wm_class) {
838 WWindowAttributes attr;
839 memset(&attr, 0, sizeof(WWindowAttributes));
840 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class,
841 &attr, NULL, True);
843 if (!attr.no_appicon && !btn->buggy_app)
844 btn->launching = 1;
845 else
846 btn->running = 0;
848 btn->drop_launch = 0;
849 scr->last_dock = btn->dock;
850 btn->pid = execCommand(btn, btn->command, NULL);
851 if (btn->pid>0) {
852 if (btn->buggy_app) {
853 /* give feedback that the app was launched */
854 btn->launching = 1;
855 dockIconPaint(btn);
856 btn->launching = 0;
857 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
858 } else {
859 dockIconPaint(btn);
861 } else {
862 wwarning(_("could not launch application %s\n"), btn->command);
863 btn->launching = 0;
864 if (!btn->relaunching)
865 btn->running = 0;
872 static void
873 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
875 WScreen *scr = menu->frame->screen_ptr;
876 char title[MAX_WORKSPACENAME_WIDTH+1];
877 int i;
879 if (!menu || !icon)
880 return;
882 for (i=0; i<scr->workspace_count; i++) {
883 if (i < menu->entry_no) {
884 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
885 free(menu->entries[i]->text);
886 strcpy(title, scr->workspaces[i]->name);
887 menu->entries[i]->text = wstrdup(title);
888 menu->flags.realized = 0;
890 menu->entries[i]->clientdata = (void*)icon;
891 } else {
892 strcpy(title, scr->workspaces[i]->name);
894 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
896 menu->flags.realized = 0;
898 if (i == scr->current_workspace) {
899 wMenuSetEnabled(menu, i, False);
900 } else {
901 wMenuSetEnabled(menu, i, True);
905 if (!menu->flags.realized)
906 wMenuRealize(menu);
910 static WMenu*
911 makeWorkspaceMenu(WScreen *scr)
913 WMenu *menu;
915 menu = wMenuCreate(scr, NULL, False);
916 if (!menu)
917 wwarning(_("could not create workspace submenu for Clip menu"));
919 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
921 menu->flags.realized = 0;
922 wMenuRealize(menu);
924 return menu;
928 static void
929 updateClipOptionsMenu(WMenu *menu, WDock *dock)
931 WMenuEntry *entry;
932 int index = 0;
934 if (!menu || !dock)
935 return;
937 /* keep on top */
938 entry = menu->entries[index];
939 entry->flags.indicator_on = !dock->lowered;
940 entry->clientdata = dock;
942 /* collapsed */
943 entry = menu->entries[++index];
944 entry->flags.indicator_on = dock->collapsed;
945 entry->clientdata = dock;
947 /* auto-collapse */
948 entry = menu->entries[++index];
949 entry->flags.indicator_on = dock->auto_collapse;
950 entry->clientdata = dock;
952 /* auto-raise/lower */
953 entry = menu->entries[++index];
954 entry->flags.indicator_on = dock->auto_raise_lower;
955 entry->clientdata = dock;
957 /* attract icons */
958 entry = menu->entries[++index];
959 entry->flags.indicator_on = dock->attract_icons;
960 entry->clientdata = dock;
962 /* keep attracted icons */
963 entry = menu->entries[++index];
964 entry->flags.indicator_on = dock->keep_attracted;
965 entry->clientdata = dock;
967 menu->flags.realized = 0;
968 wMenuRealize(menu);
972 static WMenu*
973 makeClipOptionsMenu(WScreen *scr)
975 WMenu *menu;
976 WMenuEntry *entry;
978 menu = wMenuCreate(scr, NULL, False);
979 if (!menu) {
980 wwarning(_("could not create options submenu for Clip menu"));
981 return NULL;
984 entry = wMenuAddCallback(menu, _("Keep on top"),
985 toggleLoweredCallback, NULL);
986 entry->flags.indicator = 1;
987 entry->flags.indicator_on = 1;
988 entry->flags.indicator_type = MI_CHECK;
990 entry = wMenuAddCallback(menu, _("Collapsed"),
991 toggleCollapsedCallback, NULL);
992 entry->flags.indicator = 1;
993 entry->flags.indicator_on = 1;
994 entry->flags.indicator_type = MI_CHECK;
996 entry = wMenuAddCallback(menu, _("AutoCollapse"),
997 toggleAutoCollapseCallback, NULL);
998 entry->flags.indicator = 1;
999 entry->flags.indicator_on = 1;
1000 entry->flags.indicator_type = MI_CHECK;
1002 entry = wMenuAddCallback(menu, _("AutoRaiseLower"),
1003 toggleAutoRaiseLowerCallback, NULL);
1004 entry->flags.indicator = 1;
1005 entry->flags.indicator_on = 1;
1006 entry->flags.indicator_type = MI_CHECK;
1008 entry = wMenuAddCallback(menu, _("AutoAttract Icons"),
1009 toggleAutoAttractCallback, NULL);
1010 entry->flags.indicator = 1;
1011 entry->flags.indicator_on = 1;
1012 entry->flags.indicator_type = MI_CHECK;
1014 entry = wMenuAddCallback(menu, _("Keep Attracted Icons"),
1015 toggleKeepCallback, NULL);
1016 entry->flags.indicator = 1;
1017 entry->flags.indicator_on = 1;
1018 entry->flags.indicator_type = MI_CHECK;
1020 menu->flags.realized = 0;
1021 wMenuRealize(menu);
1023 return menu;
1027 static WMenu*
1028 dockMenuCreate(WScreen *scr, int type)
1030 WMenu *menu;
1031 WMenuEntry *entry;
1033 if (type == WM_CLIP && scr->clip_menu)
1034 return scr->clip_menu;
1036 menu = wMenuCreate(scr, NULL, False);
1037 if (type != WM_CLIP) {
1038 entry = wMenuAddCallback(menu, _("Keep on top"),
1039 toggleLoweredCallback, NULL);
1040 entry->flags.indicator = 1;
1041 entry->flags.indicator_on = 1;
1042 entry->flags.indicator_type = MI_CHECK;
1043 } else {
1044 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1045 scr->clip_options = makeClipOptionsMenu(scr);
1046 if (scr->clip_options)
1047 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1049 wMenuAddCallback(menu, _("Rename Workspace"), renameCallback, NULL);
1051 wMenuAddCallback(menu, _("(Un)Select Icon"), selectCallback, NULL);
1053 wMenuAddCallback(menu, _("(Un)Select All Icons"), selectIconsCallback,
1054 NULL);
1056 wMenuAddCallback(menu, _("Keep Icon(s)"), keepIconsCallback, NULL);
1058 entry = wMenuAddCallback(menu, _("Move Icon(s) To"), NULL, NULL);
1059 scr->clip_submenu = makeWorkspaceMenu(scr);
1060 if (scr->clip_submenu)
1061 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1063 wMenuAddCallback(menu, _("Remove Icon(s)"), removeIconsCallback, NULL);
1065 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1068 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1070 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1072 entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL);
1073 free(entry->text);
1074 entry->text = _("Hide");
1076 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1078 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1080 if (type == WM_CLIP)
1081 scr->clip_menu = menu;
1083 return menu;
1087 WDock*
1088 wDockCreate(WScreen *scr, int type)
1090 WDock *dock;
1091 WAppIcon *btn;
1092 int icon_count;
1094 make_keys();
1096 dock = wmalloc(sizeof(WDock));
1097 memset(dock, 0, sizeof(WDock));
1099 if (type == WM_CLIP)
1100 icon_count = CLIP_MAX_ICONS;
1101 else
1102 icon_count = scr->scr_height/wPreferences.icon_size;
1104 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1105 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1107 dock->max_icons = icon_count;
1109 btn = mainIconCreate(scr, type);
1111 btn->dock = dock;
1113 dock->x_pos = btn->x_pos;
1114 dock->y_pos = btn->y_pos;
1115 dock->screen_ptr = scr;
1116 dock->type = type;
1117 dock->icon_count = 1;
1118 dock->on_right_side = 1;
1119 dock->collapsed = 0;
1120 dock->auto_collapse = 0;
1121 dock->auto_collapse_magic = NULL;
1122 dock->auto_raise_lower = 0;
1123 dock->auto_lower_magic = NULL;
1124 dock->auto_raise_magic = NULL;
1125 dock->attract_icons = 0;
1126 dock->keep_attracted = 0;
1127 dock->lowered = 1;
1128 dock->icon_array[0] = btn;
1129 wRaiseFrame(btn->icon->core);
1130 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1132 /* create dock menu */
1133 dock->menu = dockMenuCreate(scr, type);
1135 return dock;
1139 void
1140 wDockDestroy(WDock *dock)
1142 int i;
1143 WAppIcon *aicon;
1145 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1146 aicon = dock->icon_array[i];
1147 if (aicon) {
1148 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1149 wDockDetach(dock, aicon);
1150 if (keepit) {
1151 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
1152 XMoveWindow(dpy, aicon->icon->core->window,
1153 aicon->x_pos, aicon->y_pos);
1154 if (!dock->mapped || dock->collapsed)
1155 XMapWindow(dpy, aicon->icon->core->window);
1159 if (wPreferences.auto_arrange_icons)
1160 wArrangeIcons(dock->screen_ptr, True);
1161 free(dock->icon_array);
1162 if (dock->menu && dock->type!=WM_CLIP)
1163 wMenuDestroy(dock->menu, True);
1164 free(dock);
1168 void
1169 wClipIconPaint(WAppIcon *aicon)
1171 WScreen *scr = aicon->icon->core->screen_ptr;
1172 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1173 GC gc;
1174 Window win = aicon->icon->core->window;
1175 int length, nlength;
1176 char *ws_name, ws_number[10];
1177 int ty, tx;
1179 wIconPaint(aicon->icon);
1181 length = strlen(workspace->name);
1182 ws_name = malloc(length + 1);
1183 sprintf(ws_name, "%s", workspace->name);
1184 sprintf(ws_number, "%i", scr->current_workspace + 1);
1185 nlength = strlen(ws_number);
1187 gc = scr->clip_title_gc;
1189 if (!workspace->clip->collapsed)
1190 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_NORMAL]);
1191 else
1192 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_COLLAPSED]);
1194 ty = ICON_SIZE - scr->clip_title_font->height - 3;
1196 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1198 wDrawString(win, scr->clip_title_font, gc, tx,
1199 ty + scr->clip_title_font->y, ws_name, length);
1201 tx = (ICON_SIZE/2 - wTextWidth(scr->clip_title_font->font, ws_number, nlength))/2;
1203 wDrawString(win, scr->clip_title_font, gc, tx,
1204 scr->clip_title_font->y + 2, ws_number, nlength);
1206 free(ws_name);
1208 if (aicon->launching) {
1209 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1210 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1212 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1213 aicon->dock->rclip_button_pushed);
1217 static void
1218 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1220 wClipIconPaint(desc->parent);
1224 static void
1225 dockIconPaint(WAppIcon *btn)
1227 if (btn == btn->icon->core->screen_ptr->clip_icon)
1228 wClipIconPaint(btn);
1229 else
1230 wAppIconPaint(btn);
1234 static proplist_t
1235 make_icon_state(WAppIcon *btn)
1237 proplist_t node = NULL;
1238 proplist_t command, autolaunch, name, forced, host, position, buggy;
1239 char *tmp;
1240 char buffer[64];
1242 if (btn) {
1243 if (!btn->command)
1244 command = PLMakeString("-");
1245 else
1246 command = PLMakeString(btn->command);
1248 autolaunch = btn->auto_launch ? dYes : dNo;
1250 tmp = EscapeWM_CLASS(btn->wm_instance, btn->wm_class);
1252 name = PLMakeString(tmp);
1254 free(tmp);
1256 forced = btn->forced_dock ? dYes : dNo;
1258 buggy = btn->buggy_app ? dYes: dNo;
1260 if (btn == btn->icon->core->screen_ptr->clip_icon)
1261 sprintf(buffer, "%i,%i", btn->x_pos, btn->y_pos);
1262 else
1263 sprintf(buffer, "%hi,%hi", btn->xindex, btn->yindex);
1264 position = PLMakeString(buffer);
1266 node = PLMakeDictionaryFromEntries(dCommand, command,
1267 dName, name,
1268 dAutoLaunch, autolaunch,
1269 dForced, forced,
1270 dBuggyApplication, buggy,
1271 dPosition, position,
1272 NULL);
1273 PLRelease(command);
1274 PLRelease(name);
1275 PLRelease(position);
1276 #ifdef OFFIX_DND
1277 if (btn->dnd_command) {
1278 command = PLMakeString(btn->dnd_command);
1279 PLInsertDictionaryEntry(node, dDropCommand, command);
1280 PLRelease(command);
1282 #endif /* OFFIX_DND */
1284 if (btn->client_machine && btn->remote_start) {
1285 host = PLMakeString(btn->client_machine);
1286 PLInsertDictionaryEntry(node, dHost, host);
1287 PLRelease(host);
1291 return node;
1295 static proplist_t
1296 dockSaveState(WDock *dock)
1298 int i;
1299 proplist_t icon_info;
1300 proplist_t list=NULL, dock_state=NULL;
1301 proplist_t value;
1302 char buffer[256];
1304 list = PLMakeArrayFromElements(NULL);
1306 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1307 WAppIcon *btn = dock->icon_array[i];
1309 if (!btn || (btn->attracted && !dock->keep_attracted))
1310 continue;
1312 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1313 list = PLAppendArrayElement(list, icon_info);
1314 PLRelease(icon_info);
1318 dock_state = PLMakeDictionaryFromEntries(dApplications, list, NULL);
1320 PLRelease(list);
1322 if (dock->type == WM_DOCK) {
1323 sprintf(buffer, "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1324 dock->y_pos);
1325 value = PLMakeString(buffer);
1326 PLInsertDictionaryEntry(dock_state, dPosition, value);
1327 PLRelease(value);
1330 value = (dock->lowered ? dYes : dNo);
1331 PLInsertDictionaryEntry(dock_state, dLowered, value);
1333 if (dock->type == WM_CLIP) {
1334 value = (dock->collapsed ? dYes : dNo);
1335 PLInsertDictionaryEntry(dock_state, dCollapsed, value);
1337 value = (dock->auto_collapse ? dYes : dNo);
1338 PLInsertDictionaryEntry(dock_state, dAutoCollapse, value);
1340 value = (dock->auto_raise_lower ? dYes : dNo);
1341 PLInsertDictionaryEntry(dock_state, dAutoRaiseLower, value);
1343 value = (dock->attract_icons ? dYes : dNo);
1344 PLInsertDictionaryEntry(dock_state, dAutoAttractIcons, value);
1346 value = (dock->keep_attracted ? dYes : dNo);
1347 PLInsertDictionaryEntry(dock_state, dKeepAttracted, value);
1350 return dock_state;
1354 void
1355 wDockSaveState(WScreen *scr)
1357 proplist_t dock_state;
1359 dock_state = dockSaveState(scr->dock);
1361 PLInsertDictionaryEntry(scr->session_state, dDock, dock_state);
1363 PLRelease(dock_state);
1367 void
1368 wClipSaveState(WScreen *scr)
1370 proplist_t clip_state;
1372 clip_state = make_icon_state(scr->clip_icon);
1374 PLInsertDictionaryEntry(scr->session_state, dClip, clip_state);
1376 PLRelease(clip_state);
1380 proplist_t
1381 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1383 return dockSaveState(scr->workspaces[workspace]->clip);
1387 static WAppIcon*
1388 restore_icon_state(WScreen *scr, proplist_t info, int type, int index)
1390 WAppIcon *aicon;
1391 char *wclass, *winstance;
1392 proplist_t cmd, value;
1393 char *command;
1396 cmd = PLGetDictionaryEntry(info, dCommand);
1397 if (!cmd || !PLIsString(cmd)) {
1398 return NULL;
1401 /* parse window name */
1402 value = PLGetDictionaryEntry(info, dName);
1403 if (!value)
1404 return NULL;
1406 ParseWindowName(value, &winstance, &wclass, "dock");
1408 if (!winstance && !wclass) {
1409 return NULL;
1412 /* get commands */
1414 if (cmd)
1415 command = wstrdup(PLGetString(cmd));
1416 else
1417 command = NULL;
1419 if (!command || strcmp(command, "-")==0) {
1420 if (command)
1421 free(command);
1422 if (wclass)
1423 free(wclass);
1424 if (winstance)
1425 free(winstance);
1427 return NULL;
1430 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1431 TILE_NORMAL);
1432 if (wclass)
1433 free(wclass);
1434 if (winstance)
1435 free(winstance);
1437 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1438 if (type == WM_CLIP) {
1439 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1440 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1442 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1443 aicon->icon->core->descriptor.parent = aicon;
1446 #ifdef OFFIX_DND
1447 cmd = PLGetDictionaryEntry(info, dDropCommand);
1448 if (cmd)
1449 aicon->dnd_command = wstrdup(PLGetString(cmd));
1450 #endif
1452 /* check auto launch */
1453 value = PLGetDictionaryEntry(info, dAutoLaunch);
1455 aicon->auto_launch = 0;
1456 if (value) {
1457 if (PLIsString(value)) {
1458 if (strcasecmp(PLGetString(value), "YES")==0)
1459 aicon->auto_launch = 1;
1460 } else {
1461 wwarning(_("bad value in docked icon state info %s"),
1462 PLGetString(dAutoLaunch));
1466 /* check if it wasn't normally docked */
1467 value = PLGetDictionaryEntry(info, dForced);
1469 aicon->forced_dock = 0;
1470 if (value) {
1471 if (PLIsString(value)) {
1472 if (strcasecmp(PLGetString(value), "YES")==0)
1473 aicon->forced_dock = 1;
1474 } else {
1475 wwarning(_("bad value in docked icon state info %s"),
1476 PLGetString(dForced));
1480 /* check if we can rely on the stuff in the app */
1481 value = PLGetDictionaryEntry(info, dBuggyApplication);
1483 aicon->buggy_app = 0;
1484 if (value) {
1485 if (PLIsString(value)) {
1486 if (strcasecmp(PLGetString(value), "YES")==0)
1487 aicon->buggy_app = 1;
1488 } else {
1489 wwarning(_("bad value in docked icon state info %s"),
1490 PLGetString(dBuggyApplication));
1494 /* get position in the dock */
1495 value = PLGetDictionaryEntry(info, dPosition);
1496 if (value && PLIsString(value)) {
1497 if (sscanf(PLGetString(value), "%hi,%hi", &aicon->xindex,
1498 &aicon->yindex)!=2)
1499 wwarning(_("bad value in docked icon state info %s"),
1500 PLGetString(dPosition));
1502 /* check position sanity */
1503 /* incomplete section! */
1504 if (type == WM_DOCK) {
1505 aicon->xindex = 0;
1506 if (aicon->yindex < 0)
1507 wwarning(_("bad value in docked icon position %i,%i"),
1508 aicon->xindex, aicon->yindex);
1510 } else {
1511 aicon->yindex = index;
1512 aicon->xindex = 0;
1515 aicon->running = 0;
1516 aicon->docked = 1;
1518 return aicon;
1522 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1525 WAppIcon*
1526 wClipRestoreState(WScreen *scr, proplist_t clip_state)
1528 WAppIcon *icon;
1529 proplist_t value;
1532 icon = mainIconCreate(scr, WM_CLIP);
1534 if (!clip_state)
1535 return icon;
1536 else
1537 PLRetain(clip_state);
1539 /* restore position */
1541 value = PLGetDictionaryEntry(clip_state, dPosition);
1543 if (value) {
1544 if (!PLIsString(value))
1545 COMPLAIN("Position");
1546 else {
1547 if (sscanf(PLGetString(value), "%i,%i", &icon->x_pos,
1548 &icon->y_pos)!=2)
1549 COMPLAIN("Position");
1551 /* check position sanity */
1552 if (icon->y_pos < 0)
1553 icon->y_pos = 0;
1554 else if (icon->y_pos > scr->scr_height-ICON_SIZE)
1555 icon->y_pos = scr->scr_height-ICON_SIZE;
1557 if (icon->x_pos < 0)
1558 icon->x_pos = 0;
1559 else if (icon->x_pos > scr->scr_width-ICON_SIZE)
1560 icon->x_pos = scr->scr_width-ICON_SIZE;
1564 #ifdef OFFIX_DND
1565 value = PLGetDictionaryEntry(clip_state, dDropCommand);
1566 if (value && PLIsString(value))
1567 icon->dnd_command = wstrdup(PLGetString(value));
1568 #endif
1570 PLRelease(clip_state);
1572 return icon;
1576 WDock*
1577 wDockRestoreState(WScreen *scr, proplist_t dock_state, int type)
1579 WDock *dock;
1580 proplist_t apps;
1581 proplist_t value;
1582 WAppIcon *aicon, *old_top;
1583 int count, i;
1586 dock = wDockCreate(scr, type);
1588 if (!dock_state)
1589 return dock;
1591 if (dock_state)
1592 PLRetain(dock_state);
1595 /* restore position */
1597 value = PLGetDictionaryEntry(dock_state, dPosition);
1599 if (value) {
1600 if (!PLIsString(value))
1601 COMPLAIN("Position");
1602 else {
1603 if (sscanf(PLGetString(value), "%i,%i", &dock->x_pos,
1604 &dock->y_pos)!=2)
1605 COMPLAIN("Position");
1607 /* check position sanity */
1608 if (dock->y_pos < 0)
1609 dock->y_pos = 0;
1610 else if (dock->y_pos > scr->scr_height-ICON_SIZE)
1611 dock->y_pos = scr->scr_height - ICON_SIZE;
1613 /* This is no more needed. ??? */
1614 if (type == WM_CLIP) {
1615 if (dock->x_pos < 0)
1616 dock->x_pos = 0;
1617 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1618 dock->x_pos = scr->scr_width-ICON_SIZE;
1619 } else {
1620 if (dock->x_pos >= 0) {
1621 dock->x_pos = DOCK_EXTRA_SPACE;
1622 dock->on_right_side = 0;
1623 } else {
1624 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1625 dock->on_right_side = 1;
1631 /* restore lowered/raised state */
1633 dock->lowered = 0;
1635 value = PLGetDictionaryEntry(dock_state, dLowered);
1637 if (value) {
1638 if (!PLIsString(value))
1639 COMPLAIN("Lowered");
1640 else {
1641 if (strcasecmp(PLGetString(value), "YES")==0)
1642 dock->lowered = 1;
1647 /* restore collapsed state */
1649 dock->collapsed = 0;
1651 value = PLGetDictionaryEntry(dock_state, dCollapsed);
1653 if (value) {
1654 if (!PLIsString(value))
1655 COMPLAIN("Collapsed");
1656 else {
1657 if (strcasecmp(PLGetString(value), "YES")==0)
1658 dock->collapsed = 1;
1663 /* restore auto-collapsed state */
1665 value = PLGetDictionaryEntry(dock_state, dAutoCollapse);
1667 if (value) {
1668 if (!PLIsString(value))
1669 COMPLAIN("AutoCollapse");
1670 else {
1671 if (strcasecmp(PLGetString(value), "YES")==0) {
1672 dock->auto_collapse = 1;
1673 dock->collapsed = 1;
1679 /* restore auto-raise/lower state */
1681 value = PLGetDictionaryEntry(dock_state, dAutoRaiseLower);
1683 if (value) {
1684 if (!PLIsString(value))
1685 COMPLAIN("AutoRaiseLower");
1686 else {
1687 if (strcasecmp(PLGetString(value), "YES")==0) {
1688 dock->auto_raise_lower = 1;
1694 /* restore attract icons state */
1696 dock->attract_icons = 0;
1698 value = PLGetDictionaryEntry(dock_state, dAutoAttractIcons);
1700 if (value) {
1701 if (!PLIsString(value))
1702 COMPLAIN("AutoAttractIcons");
1703 else {
1704 if (strcasecmp(PLGetString(value), "YES")==0)
1705 dock->attract_icons = 1;
1710 /* restore keep attracted icons state */
1712 dock->keep_attracted = 0;
1714 value = PLGetDictionaryEntry(dock_state, dKeepAttracted);
1716 if (value) {
1717 if (!PLIsString(value))
1718 COMPLAIN("KeepAttracted");
1719 else {
1720 if (strcasecmp(PLGetString(value), "YES")==0)
1721 dock->keep_attracted = 1;
1726 /* application list */
1728 apps = PLGetDictionaryEntry(dock_state, dApplications);
1730 if (!apps) {
1731 goto finish;
1734 count = PLGetNumberOfElements(apps);
1736 if (count==0)
1737 goto finish;
1739 old_top = dock->icon_array[0];
1741 /* dock->icon_count is set to 1 when dock is created.
1742 * Since Clip is already restored, we want to keep it so for clip,
1743 * but for dock we may change the default top tile, so we set it to 0.
1745 if (type == WM_DOCK)
1746 dock->icon_count = 0;
1748 for (i=0; i<count; i++) {
1749 if (dock->icon_count >= dock->max_icons) {
1750 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1751 break;
1754 value = PLGetArrayElement(apps, i);
1755 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1757 dock->icon_array[dock->icon_count] = aicon;
1759 if (aicon) {
1760 aicon->dock = dock;
1761 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1762 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1764 if (dock->lowered)
1765 ChangeStackingLevel(aicon->icon->core, WMNormalLevel);
1766 else
1767 ChangeStackingLevel(aicon->icon->core, WMDockLevel);
1769 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1770 0, 0);
1772 if (!dock->collapsed)
1773 XMapWindow(dpy, aicon->icon->core->window);
1774 wRaiseFrame(aicon->icon->core);
1776 dock->icon_count++;
1777 } else if (dock->icon_count==0 && type==WM_DOCK)
1778 dock->icon_count++;
1781 /* if the first icon is not defined, use the default */
1782 if (dock->icon_array[0]==NULL) {
1783 /* update default icon */
1784 old_top->x_pos = dock->x_pos;
1785 old_top->y_pos = dock->y_pos;
1786 if (dock->lowered)
1787 ChangeStackingLevel(old_top->icon->core, WMNormalLevel);
1788 else
1789 ChangeStackingLevel(old_top->icon->core, WMDockLevel);
1790 dock->icon_array[0] = old_top;
1791 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1792 /* we don't need to increment dock->icon_count here because it was
1793 * incremented in the loop above.
1795 } else if (old_top!=dock->icon_array[0]) {
1796 if (old_top == scr->clip_icon)
1797 scr->clip_icon = dock->icon_array[0];
1798 wAppIconDestroy(old_top);
1801 finish:
1802 if (dock_state)
1803 PLRelease(dock_state);
1805 return dock;
1810 void
1811 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1813 if (btn && btn->command && !btn->running && !btn->launching) {
1815 btn->drop_launch = 0;
1817 btn->pid = execCommand(btn, btn->command, state);
1819 if (btn->pid>0) {
1820 if (!btn->forced_dock && !btn->buggy_app) {
1821 btn->launching = 1;
1822 dockIconPaint(btn);
1825 } else {
1826 free(state);
1831 void
1832 wDockDoAutoLaunch(WDock *dock, int workspace)
1834 WAppIcon *btn;
1835 WSavedState *state;
1836 int i;
1838 for (i = 0; i < dock->max_icons; i++) {
1839 btn = dock->icon_array[i];
1840 if (!btn || !btn->auto_launch)
1841 continue;
1843 state = wmalloc(sizeof(WSavedState));
1844 memset(state, 0, sizeof(WSavedState));
1845 state->workspace = workspace;
1846 /* TODO: this is klugy and is very difficult to understand
1847 * what's going on. Try to clean up */
1848 wDockLaunchWithState(dock, btn, state);
1852 #ifdef REDUCE_APPICONS
1853 void
1854 wDockSimulateLaunch(WDock *dock, WAppIcon *btn)
1856 if ((btn == NULL) || (dock == NULL))
1857 return;
1859 if (!btn->running) {
1860 if ((btn->icon->owner == NULL) && (btn->applist))
1861 btn->icon->owner = btn->applist->wapp->main_window_desc;
1862 if (!btn->forced_dock)
1863 btn->launching = 1;
1864 dockIconPaint(btn);
1865 wusleep(5000);
1868 #endif
1870 #ifdef OFFIX_DND
1871 static WDock*
1872 findDock(WScreen *scr, XEvent *event, int *icon_pos)
1874 WDock *dock;
1875 int i;
1877 *icon_pos = -1;
1878 if ((dock = scr->dock)!=NULL) {
1879 for (i=0; i<dock->max_icons; i++) {
1880 if (dock->icon_array[i]
1881 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
1882 *icon_pos = i;
1883 break;
1887 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
1888 for (i=0; i<dock->max_icons; i++) {
1889 if (dock->icon_array[i]
1890 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
1891 *icon_pos = i;
1892 break;
1896 if(*icon_pos>=0)
1897 return dock;
1898 return NULL;
1903 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
1905 WDock *dock;
1906 WAppIcon *btn;
1907 int icon_pos;
1909 dock = findDock(scr, event, &icon_pos);
1910 if (!dock)
1911 return False;
1914 * Return True if the drop was on an application icon window.
1915 * In this case, let the ClientMessage handler redirect the
1916 * message to the app.
1918 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
1919 return True;
1921 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
1922 scr->flags.dnd_data_convertion_status = 0;
1924 btn = dock->icon_array[icon_pos];
1926 if (!btn->forced_dock) {
1927 btn->relaunching = btn->running;
1928 btn->running = 1;
1930 if (btn->wm_instance || btn->wm_class) {
1931 WWindowAttributes attr;
1932 memset(&attr, 0, sizeof(WWindowAttributes));
1933 wDefaultFillAttributes(btn->icon->core->screen_ptr,
1934 btn->wm_instance,
1935 btn->wm_class, &attr, NULL, True);
1937 if (!attr.no_appicon)
1938 btn->launching = 1;
1939 else
1940 btn->running = 0;
1943 btn->drop_launch = 1;
1944 scr->last_dock = dock;
1945 btn->pid = execCommand(btn, btn->dnd_command, NULL);
1946 if (btn->pid>0) {
1947 dockIconPaint(btn);
1948 } else {
1949 btn->launching = 0;
1950 if (!btn->relaunching) {
1951 btn->running = 0;
1955 return False;
1957 #endif /* OFFIX_DND */
1961 Bool
1962 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
1964 WWindow *wwin;
1965 char **argv;
1966 int argc;
1967 int index;
1969 wwin = icon->icon->owner;
1970 if (icon->command==NULL) {
1971 icon->editing = 0;
1972 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
1974 icon->command = FlattenStringList(argv, argc);
1975 XFreeStringList(argv);
1976 } else {
1977 char *command=NULL;
1979 /* icon->forced_dock = 1;*/
1980 if (!icon->attracted || dock->type!=WM_CLIP || dock->keep_attracted) {
1981 icon->editing = 1;
1982 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
1983 _("Type the command used to launch the application"),
1984 &command)) {
1985 if (command && (command[0]==0 ||
1986 (command[0]=='-' && command[1]==0))) {
1987 free(command);
1988 command = NULL;
1990 icon->command = command;
1991 icon->editing = 0;
1992 } else {
1993 icon->editing = 0;
1994 if (command)
1995 free(command);
1996 /* If the target is the dock, reject the icon. If
1997 * the target is the clip, make it an attracted icon
1999 if (dock->type==WM_CLIP) {
2000 icon->attracted = 1;
2001 if (!icon->icon->shadowed) {
2002 icon->icon->shadowed = 1;
2003 icon->icon->force_paint = 1;
2005 } else {
2006 return False;
2011 } else {
2012 icon->editing = 0;
2015 for (index=1; index<dock->max_icons; index++)
2016 if (dock->icon_array[index] == NULL)
2017 break;
2018 /* if (index == dock->max_icons)
2019 return; */
2021 assert(index < dock->max_icons);
2023 dock->icon_array[index] = icon;
2024 icon->yindex = y;
2025 icon->xindex = x;
2027 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2028 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2030 dock->icon_count++;
2032 icon->running = 1;
2033 icon->launching = 0;
2034 icon->docked = 1;
2035 icon->dock = dock;
2036 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
2037 if (dock->type == WM_CLIP) {
2038 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2039 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2041 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
2042 icon->icon->core->descriptor.parent = icon;
2044 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
2045 icon->icon->core);
2046 wAppIconMove(icon, icon->x_pos, icon->y_pos);
2047 wAppIconPaint(icon);
2049 if (wPreferences.auto_arrange_icons)
2050 wArrangeIcons(dock->screen_ptr, True);
2052 #ifdef OFFIX_DND
2053 if (icon->command && !icon->dnd_command) {
2054 icon->dnd_command = wmalloc(strlen(icon->command)+8);
2055 sprintf(icon->dnd_command, "%s %%d", icon->command);
2057 #endif
2059 return True;
2063 void
2064 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2066 int index;
2068 for(index=1; index<dock->max_icons; index++) {
2069 if(dock->icon_array[index] == icon)
2070 break;
2072 assert(index < dock->max_icons);
2074 icon->yindex = y;
2075 icon->xindex = x;
2077 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2078 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2082 Bool
2083 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2085 WWindow *wwin;
2086 char **argv;
2087 int argc;
2088 int index;
2090 if (dest == NULL)
2091 return False;
2093 wwin = icon->icon->owner;
2096 * For the moment we can't do this if we move icons in Clip from one
2097 * workspace to other, because if we move two or more icons without
2098 * command, the dialog box will not be able to tell us to which of the
2099 * moved icons it applies. -Dan
2101 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2102 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2104 icon->command = FlattenStringList(argv, argc);
2105 XFreeStringList(argv);
2106 } else {
2107 char *command=NULL;
2109 icon->editing = 1;
2110 /* icon->forced_dock = 1;*/
2111 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2112 _("Type the command used to launch the application"),
2113 &command)) {
2114 if (command && (command[0]==0 ||
2115 (command[0]=='-' && command[1]==0))) {
2116 free(command);
2117 command = NULL;
2119 icon->command = command;
2120 } else {
2121 icon->editing = 0;
2122 if (command)
2123 free(command);
2124 return False;
2126 icon->editing = 0;
2130 for(index=1; index<src->max_icons; index++) {
2131 if(src->icon_array[index] == icon)
2132 break;
2134 assert(index < src->max_icons);
2136 src->icon_array[index] = NULL;
2137 src->icon_count--;
2139 for(index=1; index<dest->max_icons; index++) {
2140 if(dest->icon_array[index] == NULL)
2141 break;
2143 /* if (index == dest->max_icons)
2144 return; */
2146 assert(index < dest->max_icons);
2148 dest->icon_array[index] = icon;
2149 icon->dock = dest;
2151 /* deselect the icon */
2152 if (icon->icon->selected)
2153 wIconSelect(icon->icon);
2155 if (dest->type == WM_DOCK) {
2156 icon->icon->core->descriptor.handle_enternotify = NULL;
2157 icon->icon->core->descriptor.handle_leavenotify = NULL;
2158 } else {
2159 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2160 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2163 /* set it to be kept when moving to dock, or to a clip that keep the
2164 * attracted icons.
2165 * Unless the icon does not have a command set
2167 if (icon->command && (dest->type==WM_DOCK || dest->keep_attracted)) {
2168 icon->attracted = 0;
2169 if (icon->icon->shadowed) {
2170 icon->icon->shadowed = 0;
2171 icon->icon->force_paint = 1;
2175 if (src->auto_collapse || src->auto_raise_lower)
2176 clipLeave(src);
2178 icon->yindex = y;
2179 icon->xindex = x;
2181 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2182 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2184 dest->icon_count++;
2186 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2187 icon->icon->core);
2188 wAppIconPaint(icon);
2190 return True;
2194 void
2195 wDockDetach(WDock *dock, WAppIcon *icon)
2197 int index;
2199 /* make the settings panel be closed */
2200 if (icon->panel) {
2201 DestroyDockAppSettingsPanel(icon->panel);
2204 icon->docked = 0;
2205 icon->dock = NULL;
2206 icon->attracted = 0;
2207 if (icon->icon->shadowed) {
2208 icon->icon->shadowed = 0;
2209 icon->icon->force_paint = 1;
2212 /* deselect the icon */
2213 if (icon->icon->selected)
2214 wIconSelect(icon->icon);
2216 if (icon->command) {
2217 free(icon->command);
2218 icon->command = NULL;
2220 #ifdef OFFIX_DND
2221 if (icon->dnd_command) {
2222 free(icon->dnd_command);
2223 icon->dnd_command = NULL;
2225 #endif
2227 for (index=1; index<dock->max_icons; index++)
2228 if (dock->icon_array[index] == icon)
2229 break;
2230 assert(index < dock->max_icons);
2231 dock->icon_array[index] = NULL;
2232 icon->yindex = -1;
2233 icon->xindex = -1;
2234 dock->icon_count--;
2236 /* if the dock is not attached to an application or
2237 * the the application did not set the approriate hints yet,
2238 * destroy the icon */
2239 #ifdef REDUCE_APPICONS
2240 if ((icon->num_apps == 0) && (!icon->running || !wApplicationOf(icon->main_window)) )
2241 #else
2242 if (!icon->running || !wApplicationOf(icon->main_window))
2243 #endif
2244 wAppIconDestroy(icon);
2245 else {
2246 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2247 icon->icon->core->descriptor.handle_enternotify = NULL;
2248 icon->icon->core->descriptor.handle_leavenotify = NULL;
2249 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2250 icon->icon->core->descriptor.parent = icon;
2252 ChangeStackingLevel(icon->icon->core, NORMAL_ICON_LEVEL);
2254 wAppIconPaint(icon);
2255 if (wPreferences.auto_arrange_icons) {
2256 wArrangeIcons(dock->screen_ptr, True);
2259 if (dock->auto_collapse || dock->auto_raise_lower)
2260 clipLeave(dock);
2265 * returns the closest Dock slot index for the passed
2266 * coordinates.
2268 * Returns False if icon can't be docked.
2270 Bool
2271 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2272 int *ret_x, int *ret_y, int redocking)
2274 WScreen *scr = dock->screen_ptr;
2275 int dx, dy;
2276 int ex_x, ex_y;
2277 int i, offset = ICON_SIZE/2;
2278 WAppIcon *aicon = NULL;
2279 WAppIcon *nicon = NULL;
2280 int max_y_icons, max_x_icons;
2282 max_x_icons = scr->scr_width/ICON_SIZE;
2283 max_y_icons = scr->scr_height/ICON_SIZE-1;
2285 if (wPreferences.flags.noupdates)
2286 return False;
2288 dx = dock->x_pos;
2289 dy = dock->y_pos;
2291 /* if the dock is full */
2292 if (!redocking &&
2293 (dock->icon_count >= dock->max_icons)) {
2294 return False;
2297 /* exact position */
2298 if (req_y < dy)
2299 ex_y = (req_y - offset - dy)/ICON_SIZE;
2300 else
2301 ex_y = (req_y + offset - dy)/ICON_SIZE;
2303 if (req_x < dx)
2304 ex_x = (req_x - offset - dx)/ICON_SIZE;
2305 else
2306 ex_x = (req_x + offset - dx)/ICON_SIZE;
2308 /* check if the icon is outside the screen boundaries */
2309 if (dx + ex_x*ICON_SIZE < -ICON_SIZE+2 ||
2310 dx + ex_x*ICON_SIZE >= scr->scr_width-1 ||
2311 dy + ex_y*ICON_SIZE < -ICON_SIZE+2 ||
2312 dy + ex_y*ICON_SIZE >= scr->scr_height-1)
2313 return False;
2315 if (dock->type == WM_DOCK) {
2316 if (icon->dock != dock && ex_x != 0)
2317 return False;
2319 for (i=0; i<dock->max_icons; i++) {
2320 nicon = dock->icon_array[i];
2321 if (nicon && nicon->yindex == ex_y) {
2322 aicon = nicon;
2323 break;
2327 *ret_x = 0;
2329 if (redocking) {
2330 int sig, done, closest;
2332 /* Possible cases when redocking:
2334 * icon dragged out of range of any slot -> false
2335 * icon dragged to range of free slot
2336 * icon dragged to range of same slot
2337 * icon dragged to range of different icon
2339 if (abs(ex_x) > DOCK_DETTACH_THRESHOLD)
2340 return False;
2342 if (ex_y >= 0 && ex_y <= max_y_icons && (aicon == icon || !aicon)) {
2344 *ret_y = ex_y;
2347 return True;
2350 /* start looking at the upper slot or lower? */
2351 if (ex_y*ICON_SIZE < (req_y + offset - dy))
2352 sig = 1;
2353 else
2354 sig = -1;
2356 closest = -1;
2357 done = 0;
2358 /* look for closest free slot */
2359 for (i=0; i<(DOCK_DETTACH_THRESHOLD+1)*2 && !done; i++) {
2360 int j;
2362 done = 1;
2363 closest = sig*(i/2) + ex_y;
2364 /* check if this slot is used */
2365 if (closest >= 0) {
2366 for (j = 0; j<dock->max_icons; j++) {
2367 if (dock->icon_array[j]
2368 && dock->icon_array[j]->yindex==closest) {
2369 /* slot is used by someone else */
2370 if (dock->icon_array[j]!=icon)
2371 done = 0;
2372 break;
2376 sig = -sig;
2378 if (done && closest >= 0 && closest <= max_y_icons &&
2379 ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD+1)
2381 (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD+1))) {
2383 *ret_y = closest;
2385 return True;
2387 } else { /* !redocking */
2389 /* if slot is free and the icon is close enough, return it */
2390 if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) {
2391 *ret_y = ex_y;
2392 return True;
2395 } else { /* CLIP */
2396 int neighbours = 0;
2398 for (i=0; i<dock->max_icons; i++) {
2399 nicon = dock->icon_array[i];
2400 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2401 aicon = nicon;
2402 break;
2406 for (i=0; i<dock->max_icons; i++) {
2407 nicon = dock->icon_array[i];
2408 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2409 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2410 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2411 neighbours = 1;
2412 break;
2416 if (neighbours && (aicon==NULL || (redocking && aicon == icon))) {
2417 *ret_x = ex_x;
2418 *ret_y = ex_y;
2419 return True;
2422 return False;
2425 #define MIN(x, y) ((x) > (y) ? (y) : (x))
2426 #define MAX(x, y) ((x) < (y) ? (y) : (x))
2428 #define ON_SCREEN(x, y, sx, ex, sy, ey) \
2429 ((((x)+ICON_SIZE/2) >= (sx)) && (((y)+ICON_SIZE/2) >= (sy)) && \
2430 (((x) + (ICON_SIZE/2)) <= (ex)) && (((y) + (ICON_SIZE/2)) <= ey))
2434 * returns true if it can find a free slot in the dock,
2435 * in which case it changes x_pos and y_pos accordingly.
2436 * Else returns false.
2438 Bool
2439 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2441 WScreen *scr = dock->screen_ptr;
2442 WAppIcon *btn;
2443 unsigned char *slot_map;
2444 int mwidth;
2445 int r;
2446 int x, y;
2447 int i, done = False;
2448 int corner;
2449 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2452 /* if the dock is full */
2453 if (dock->icon_count >= dock->max_icons) {
2454 return False;
2457 if (!wPreferences.flags.nodock && scr->dock) {
2458 if (scr->dock->on_right_side)
2459 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2460 else
2461 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2464 if (ex < dock->x_pos)
2465 ex = dock->x_pos;
2466 if (sx > dock->x_pos+ICON_SIZE)
2467 sx = dock->x_pos+ICON_SIZE;
2468 #define C_NONE 0
2469 #define C_NW 1
2470 #define C_NE 2
2471 #define C_SW 3
2472 #define C_SE 4
2474 /* check if clip is in a corner */
2475 if (dock->type==WM_CLIP) {
2476 if (dock->x_pos < 1 && dock->y_pos < 1)
2477 corner = C_NE;
2478 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2479 corner = C_SE;
2480 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2481 corner = C_SW;
2482 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2483 corner = C_NW;
2484 else
2485 corner = C_NONE;
2486 } else
2487 corner = C_NONE;
2489 /* If the clip is in the corner, use only slots that are in the border
2490 * of the screen */
2491 if (corner!=C_NONE) {
2492 char *hmap, *vmap;
2493 int hcount, vcount;
2495 hcount = MIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2496 vcount = MIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2497 hmap = wmalloc(hcount+1);
2498 memset(hmap, 0, hcount+1);
2499 vmap = wmalloc(vcount+1);
2500 memset(vmap, 0, vcount+1);
2502 /* mark used positions */
2503 switch (corner) {
2504 case C_NE:
2505 for (i=0; i<dock->max_icons; i++) {
2506 btn = dock->icon_array[i];
2507 if (!btn)
2508 continue;
2510 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2511 vmap[btn->yindex] = 1;
2512 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2513 hmap[btn->xindex] = 1;
2515 case C_NW:
2516 for (i=0; i<dock->max_icons; i++) {
2517 btn = dock->icon_array[i];
2518 if (!btn)
2519 continue;
2521 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2522 vmap[btn->yindex] = 1;
2523 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2524 hmap[-btn->xindex] = 1;
2526 case C_SE:
2527 for (i=0; i<dock->max_icons; i++) {
2528 btn = dock->icon_array[i];
2529 if (!btn)
2530 continue;
2532 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2533 vmap[-btn->yindex] = 1;
2534 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2535 hmap[btn->xindex] = 1;
2537 case C_SW:
2538 default:
2539 for (i=0; i<dock->max_icons; i++) {
2540 btn = dock->icon_array[i];
2541 if (!btn)
2542 continue;
2544 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2545 vmap[-btn->yindex] = 1;
2546 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2547 hmap[-btn->xindex] = 1;
2550 x=0; y=0;
2551 done = 0;
2552 /* search a vacant slot */
2553 for (i=1; i<MAX(vcount, hcount); i++) {
2554 if (i < vcount && vmap[i]==0) {
2555 /* found a slot */
2556 x = 0;
2557 y = i;
2558 done = 1;
2559 break;
2560 } else if (i < hcount && hmap[i]==0) {
2561 /* found a slot */
2562 x = i;
2563 y = 0;
2564 done = 1;
2565 break;
2568 free(vmap);
2569 free(hmap);
2570 /* If found a slot, translate and return */
2571 if (done) {
2572 if (corner==C_NW || corner==C_NE) {
2573 *y_pos = y;
2574 } else {
2575 *y_pos = -y;
2577 if (corner==C_NE || corner==C_SE) {
2578 *x_pos = x;
2579 } else {
2580 *x_pos = -x;
2582 return True;
2584 /* else, try to find a slot somewhere else */
2587 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2588 * placed outside of screen */
2589 mwidth = (int)ceil(sqrt(dock->max_icons));
2591 /* In the worst case (the clip is in the corner of the screen),
2592 * the amount of icons that fit in the clip is smaller.
2593 * Double the map to get a safe value.
2595 mwidth += mwidth;
2597 r = (mwidth-1)/2;
2599 slot_map = wmalloc(mwidth*mwidth);
2600 memset(slot_map, 0, mwidth*mwidth);
2602 #define XY2OFS(x,y) (MAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2604 /* mark used slots in the map. If the slot falls outside the map
2605 * (for example, when all icons are placed in line), ignore them. */
2606 for (i=0; i<dock->max_icons; i++) {
2607 btn = dock->icon_array[i];
2608 if (btn)
2609 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2611 /* Find closest slot from the center that is free by scanning the
2612 * map from the center to outward in circular passes.
2613 * This will not result in a neat layout, but will be optimal
2614 * in the sense that there will not be holes left.
2616 done = 0;
2617 for (i = 1; i <= r && !done; i++) {
2618 int tx, ty;
2620 /* top and bottom parts of the ring */
2621 for (x = -i; x <= i && !done; x++) {
2622 tx = dock->x_pos + x*ICON_SIZE;
2623 y = -i;
2624 ty = dock->y_pos + y*ICON_SIZE;
2625 if (slot_map[XY2OFS(x,y)]==0
2626 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2627 *x_pos = x;
2628 *y_pos = y;
2629 done = 1;
2630 break;
2632 y = i;
2633 ty = dock->y_pos + y*ICON_SIZE;
2634 if (slot_map[XY2OFS(x,y)]==0
2635 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2636 *x_pos = x;
2637 *y_pos = y;
2638 done = 1;
2639 break;
2642 /* left and right parts of the ring */
2643 for (y = -i+1; y <= i-1; y++) {
2644 ty = dock->y_pos + y*ICON_SIZE;
2645 x = -i;
2646 tx = dock->x_pos + x*ICON_SIZE;
2647 if (slot_map[XY2OFS(x,y)]==0
2648 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2649 *x_pos = x;
2650 *y_pos = y;
2651 done = 1;
2652 break;
2654 x = i;
2655 tx = dock->x_pos + x*ICON_SIZE;
2656 if (slot_map[XY2OFS(x,y)]==0
2657 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2658 *x_pos = x;
2659 *y_pos = y;
2660 done = 1;
2661 break;
2665 free(slot_map);
2666 #undef XY2OFS
2667 return done;
2671 static void
2672 moveDock(WDock *dock, int new_x, int new_y)
2674 WAppIcon *btn;
2675 int i;
2677 dock->x_pos = new_x;
2678 dock->y_pos = new_y;
2679 for (i=0; i<dock->max_icons; i++) {
2680 btn = dock->icon_array[i];
2681 if (btn) {
2682 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2683 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2684 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2690 static void
2691 swapDock(WDock *dock)
2693 WScreen *scr = dock->screen_ptr;
2694 WAppIcon *btn;
2695 int x, i;
2698 if (dock->on_right_side) {
2699 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2700 } else {
2701 x = dock->x_pos = DOCK_EXTRA_SPACE;
2704 for (i=0; i<dock->max_icons; i++) {
2705 btn = dock->icon_array[i];
2706 if (btn) {
2707 btn->x_pos = x;
2708 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2712 wScreenUpdateUsableArea(scr);
2716 static pid_t
2717 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2719 WScreen *scr = btn->icon->core->screen_ptr;
2720 pid_t pid;
2721 char **argv;
2722 int argc;
2723 char *cmdline;
2725 cmdline = ExpandOptions(scr, command);
2727 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2728 if (cmdline)
2729 free(cmdline);
2730 if (state)
2731 free(state);
2732 return 0;
2735 ParseCommand(cmdline, &argv, &argc);
2737 if (argv==NULL) {
2738 if (cmdline)
2739 free(cmdline);
2740 if (state)
2741 free(state);
2742 return 0;
2745 if ((pid=fork())==0) {
2746 char **args;
2747 int i;
2749 SetupEnvironment(scr);
2751 #ifdef HAVE_SETPGID
2752 setpgid(0, 0);
2753 #endif
2755 args = malloc(sizeof(char*)*(argc+1));
2756 if (!args)
2757 exit(111);
2758 for (i=0; i<argc; i++) {
2759 args[i] = argv[i];
2761 args[argc] = NULL;
2762 execvp(argv[0], args);
2763 exit(111);
2765 while (argc > 0)
2766 free(argv[--argc]);
2767 free(argv);
2769 if (pid > 0) {
2770 if (!state) {
2771 state = wmalloc(sizeof(WSavedState));
2772 memset(state, 0, sizeof(WSavedState));
2773 state->hidden = -1;
2774 state->miniaturized = -1;
2775 state->shaded = -1;
2776 if (btn->dock == scr->dock)
2777 state->workspace = -1;
2778 else
2779 state->workspace = scr->current_workspace;
2781 wWindowAddSavedState(btn->wm_instance, btn->wm_class, cmdline, pid,
2782 state);
2783 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
2784 btn->dock);
2785 } else if (state) {
2786 free(state);
2788 free(cmdline);
2789 return pid;
2793 void
2794 wDockHideIcons(WDock *dock)
2796 int i;
2797 WAppIcon *btn;
2799 if (dock==NULL)
2800 return;
2802 btn = dock->icon_array[0];
2804 for (i=1; i<dock->max_icons; i++) {
2805 if (dock->icon_array[i])
2806 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
2808 dock->mapped = 0;
2810 dockIconPaint(btn);
2814 void
2815 wDockShowIcons(WDock *dock)
2817 int i, newlevel;
2818 WAppIcon *btn;
2820 if (dock==NULL)
2821 return;
2823 btn = dock->icon_array[0];
2824 moveDock(dock, btn->x_pos, btn->y_pos);
2826 newlevel = dock->lowered ? WMNormalLevel : WMDockLevel;
2827 ChangeStackingLevel(btn->icon->core, newlevel);
2829 for (i=1; i<dock->max_icons; i++) {
2830 if (dock->icon_array[i]) {
2831 MoveInStackListAbove(dock->icon_array[i]->icon->core,
2832 btn->icon->core);
2833 break;
2837 if (!dock->collapsed) {
2838 for (i=1; i<dock->max_icons; i++) {
2839 if (dock->icon_array[i]) {
2840 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
2844 dock->mapped = 1;
2846 dockIconPaint(btn);
2850 void
2851 wDockLower(WDock *dock)
2853 int i;
2855 for (i=0; i<dock->max_icons; i++) {
2856 if (dock->icon_array[i])
2857 wLowerFrame(dock->icon_array[i]->icon->core);
2862 void
2863 wDockRaise(WDock *dock)
2865 int i;
2867 for (i=dock->max_icons-1; i>=0; i--) {
2868 if (dock->icon_array[i])
2869 wRaiseFrame(dock->icon_array[i]->icon->core);
2874 void
2875 wDockRaiseLower(WDock *dock)
2877 if (!dock->icon_array[0]->icon->core->stacking->above
2878 ||(dock->icon_array[0]->icon->core->stacking->window_level
2879 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
2880 wDockLower(dock);
2881 else
2882 wDockRaise(dock);
2886 void
2887 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
2889 icon->launching = 0;
2890 icon->relaunching = 0;
2891 dockIconPaint(icon);
2895 WAppIcon*
2896 wDockFindIconFor(WDock *dock, Window window)
2898 WAppIcon *icon;
2899 int i;
2901 for (i=0; i<dock->max_icons; i++) {
2902 icon = dock->icon_array[i];
2903 if (icon && icon->main_window == window)
2904 return icon;
2906 return NULL;
2910 void
2911 wDockTrackWindowLaunch(WDock *dock, Window window)
2913 WAppIcon *icon;
2914 #ifdef REDUCE_APPICONS
2915 WAppIconAppList *tapplist;
2916 #endif
2917 char *wm_class, *wm_instance;
2918 int i;
2919 Bool firstPass = True;
2920 Bool found = False;
2921 char *command = NULL;
2924 int argc;
2925 char **argv;
2927 if (XGetCommand(dpy, window, &argv, &argc)) {
2928 if (argc > 0 && argv != NULL)
2929 command = FlattenStringList(argv,argc);
2930 if (argv) {
2931 XFreeStringList(argv);
2936 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
2937 (!wm_class && !wm_instance))
2938 return;
2940 retry:
2941 for (i=0; i<dock->max_icons; i++) {
2942 icon = dock->icon_array[i];
2943 if (!icon)
2944 continue;
2946 /* app is already attached to icon */
2947 if (icon->main_window == window) {
2948 found = True;
2949 break;
2952 if ((icon->wm_instance || icon->wm_class)
2953 && (icon->launching
2954 || (dock->screen_ptr->flags.startup && !icon->running))) {
2956 if (icon->wm_instance && wm_instance &&
2957 strcmp(icon->wm_instance, wm_instance)!=0) {
2958 continue;
2960 if (icon->wm_class && wm_class &&
2961 strcmp(icon->wm_class, wm_class)!=0) {
2962 continue;
2964 if (firstPass && command && strcmp(icon->command, command)!=0) {
2965 continue;
2968 if (!icon->relaunching) {
2969 WApplication *wapp;
2971 /* Possibly an application that was docked with dockit,
2972 * but the user did not update WMState to indicate that
2973 * it was docked by force */
2974 wapp = wApplicationOf(window);
2975 if (!wapp) {
2976 icon->forced_dock = 1;
2977 icon->running = 0;
2979 if (!icon->forced_dock)
2980 icon->main_window = window;
2982 #ifdef REDUCE_APPICONS
2983 tapplist = wmalloc(sizeof(WAppIconAppList));
2984 memset(tapplist, 0, sizeof(WAppIconAppList));
2985 tapplist->next = icon->applist;
2986 if (icon->applist)
2987 icon->applist->prev = tapplist;
2988 icon->applist = tapplist;
2989 tapplist->wapp = wApplicationOf(window);
2990 icon->num_apps++;
2991 #endif
2993 found = True;
2994 wDockFinishLaunch(dock, icon);
2995 break;
2999 if (firstPass && !found) {
3000 firstPass = False;
3001 goto retry;
3004 if (command)
3005 free(command);
3007 if (wm_class)
3008 XFree(wm_class);
3009 if (wm_instance)
3010 XFree(wm_instance);
3015 void
3016 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
3018 if (!wPreferences.flags.noclip) {
3019 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
3020 if (scr->current_workspace != workspace) {
3021 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
3023 wDockHideIcons(old_clip);
3024 if (old_clip->auto_raise_lower) {
3025 if (old_clip->auto_raise_magic) {
3026 WMDeleteTimerHandler(old_clip->auto_raise_magic);
3027 old_clip->auto_raise_magic = NULL;
3029 wDockLower(old_clip);
3031 if (old_clip->auto_collapse) {
3032 if (old_clip->auto_expand_magic) {
3033 WMDeleteTimerHandler(old_clip->auto_expand_magic);
3034 old_clip->auto_expand_magic = NULL;
3036 old_clip->collapsed = 1;
3038 wDockShowIcons(scr->workspaces[workspace]->clip);
3040 if (scr->flags.clip_balloon_mapped)
3041 showClipBalloon(scr->clip_icon->dock, workspace);
3047 static void
3048 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
3050 WAppIcon *icon;
3051 int i;
3053 for (i=0; i<dock->max_icons; i++) {
3054 icon = dock->icon_array[i];
3055 if (!icon)
3056 continue;
3058 if (icon->launching && icon->pid == pid) {
3059 if (!icon->relaunching) {
3060 icon->running = 0;
3061 icon->main_window = None;
3063 wDockFinishLaunch(dock, icon);
3064 icon->pid = 0;
3065 if (status==111) {
3066 char msg[PATH_MAX];
3067 #ifdef OFFIX_DND
3068 sprintf(msg, _("Could not execute command \"%s\""),
3069 icon->drop_launch && icon->dnd_command
3070 ? icon->dnd_command : icon->command);
3071 #else
3072 sprintf(msg, _("Could not execute command \"%s\""),
3073 icon->command);
3074 #endif
3075 wMessageDialog(dock->screen_ptr, _("Error"), msg,
3076 _("OK"), NULL, NULL);
3078 break;
3084 static void
3085 toggleLowered(WDock *dock)
3087 WAppIcon *tmp;
3088 int newlevel, i;
3090 /* lower/raise Dock */
3091 if (!dock->lowered) {
3092 newlevel = WMNormalLevel;
3093 dock->lowered = 1;
3094 } else {
3095 newlevel = WMDockLevel;
3096 dock->lowered = 0;
3099 for (i=0; i<dock->max_icons; i++) {
3100 tmp = dock->icon_array[i];
3101 if (!tmp)
3102 continue;
3104 ChangeStackingLevel(tmp->icon->core, newlevel);
3105 if (dock->lowered)
3106 wLowerFrame(tmp->icon->core);
3109 if (dock->type == WM_DOCK)
3110 wScreenUpdateUsableArea(dock->screen_ptr);
3114 static void
3115 toggleCollapsed(WDock *dock)
3117 if (dock->collapsed) {
3118 dock->collapsed = 0;
3119 wDockShowIcons(dock);
3121 else {
3122 dock->collapsed = 1;
3123 wDockHideIcons(dock);
3128 static void
3129 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3131 WScreen *scr = dock->screen_ptr;
3132 WObjDescriptor *desc;
3133 WMenuEntry *entry;
3134 WApplication *wapp = NULL;
3135 int index = 0;
3136 int x_pos;
3137 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3139 if (dock->type == WM_DOCK) {
3140 /* keep on top */
3141 entry = dock->menu->entries[index];
3142 entry->flags.indicator_on = !dock->lowered;
3143 entry->clientdata = dock;
3144 } else {
3145 /* clip options */
3146 if (scr->clip_options)
3147 updateClipOptionsMenu(scr->clip_options, dock);
3149 /* Rename Workspace */
3150 entry = dock->menu->entries[++index];
3151 entry->clientdata = dock;
3153 /* select icon */
3154 entry = dock->menu->entries[++index];
3155 entry->clientdata = aicon;
3156 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3158 /* (un)select all icons */
3159 entry = dock->menu->entries[++index];
3160 entry->clientdata = aicon;
3161 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3163 /* keep icon(s) */
3164 entry = dock->menu->entries[++index];
3165 entry->clientdata = aicon;
3166 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3168 /* this is the workspace submenu part */
3169 if (scr->clip_submenu)
3170 updateWorkspaceMenu(scr->clip_submenu, aicon);
3171 index++;
3173 /* remove icon(s) */
3174 entry = dock->menu->entries[++index];
3175 entry->clientdata = aicon;
3176 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3178 /* attract icon(s) */
3179 entry = dock->menu->entries[++index];
3180 entry->clientdata = aicon;
3183 /* launch */
3184 entry = dock->menu->entries[++index];
3185 entry->clientdata = aicon;
3186 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3188 /* unhide here */
3189 entry = dock->menu->entries[++index];
3190 entry->clientdata = aicon;
3191 wMenuSetEnabled(dock->menu, index, appIsRunning);
3193 /* hide */
3194 entry = dock->menu->entries[++index];
3195 entry->clientdata = aicon;
3196 if (aicon->icon->owner) {
3197 wapp = wApplicationOf(aicon->icon->owner->main_window);
3198 if (wapp && wapp->flags.hidden)
3199 entry->text = _("Unhide");
3200 else
3201 entry->text = _("Hide");
3202 } else {
3203 entry->text = _("Hide");
3205 wMenuSetEnabled(dock->menu, index, appIsRunning);
3207 /* settings */
3208 entry = dock->menu->entries[++index];
3209 entry->clientdata = aicon;
3210 wMenuSetEnabled(dock->menu, index, !aicon->editing
3211 && !wPreferences.flags.noupdates);
3213 /* kill */
3214 entry = dock->menu->entries[++index];
3215 entry->clientdata = aicon;
3216 wMenuSetEnabled(dock->menu, index, appIsRunning);
3218 if (!dock->menu->flags.realized)
3219 wMenuRealize(dock->menu);
3221 if (dock->type == WM_CLIP) {
3222 x_pos = event->xbutton.x_root+2;
3223 } else {
3224 x_pos = dock->on_right_side ?
3225 scr->scr_width - dock->menu->frame->core->width - 2 : 0;
3228 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3230 /* allow drag select */
3231 event->xany.send_event = True;
3232 desc = &dock->menu->menu->descriptor;
3233 (*desc->handle_mousedown)(desc, event);
3237 static void
3238 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3240 if (!scr->clip_ws_menu) {
3241 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3243 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3244 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3248 /******************************************************************/
3249 static void
3250 iconDblClick(WObjDescriptor *desc, XEvent *event)
3252 WAppIcon *btn = desc->parent;
3253 WDock *dock = btn->dock;
3254 WApplication *wapp = NULL;
3255 int unhideHere = 0;
3257 #ifdef REDUCE_APPICONS
3258 if ((btn->icon->owner && !(event->xbutton.state & ControlMask)) ||
3259 ((btn->icon->owner == NULL) && (btn->applist != NULL))) {
3260 if (btn->icon->owner == NULL)
3261 btn->icon->owner = btn->applist->wapp->main_window_desc;
3262 #ifdef I_HATE_THIS
3264 #endif
3265 #else
3266 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3267 #endif
3268 wapp = wApplicationOf(btn->icon->owner->main_window);
3270 assert(wapp!=NULL);
3272 unhideHere = (event->xbutton.state & ShiftMask);
3274 /* go to the last workspace that the user worked on the app */
3275 if (!unhideHere) {
3276 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3279 wUnhideApplication(wapp, event->xbutton.button==Button2,
3280 unhideHere);
3282 if (event->xbutton.state & MOD_MASK) {
3283 wHideOtherApplications(btn->icon->owner);
3285 } else {
3286 if (event->xbutton.button==Button1) {
3288 if (event->xbutton.state & MOD_MASK) {
3289 /* raise/lower dock */
3290 toggleLowered(dock);
3291 } else if (btn == dock->screen_ptr->clip_icon) {
3292 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3293 toggleCollapsed(dock);
3294 else
3295 handleClipChangeWorkspace(dock->screen_ptr, event);
3296 } else if (btn->command) {
3297 if (!btn->launching &&
3298 (!btn->running || (event->xbutton.state & ControlMask))) {
3299 launchDockedApplication(btn);
3307 static void
3308 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3310 WScreen *scr = dock->screen_ptr;
3311 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3312 int x, y;
3313 XEvent ev;
3314 int grabbed = 0, swapped = 0, done;
3315 Pixmap ghost = None;
3316 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3318 #ifdef DEBUG
3319 puts("moving dock");
3320 #endif
3321 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3322 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3323 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3324 wwarning("pointer grab failed for dock move");
3326 y = 0;
3327 for (x=0; x<dock->max_icons; x++) {
3328 if (dock->icon_array[x]!=NULL &&
3329 dock->icon_array[x]->yindex > y)
3330 y = dock->icon_array[x]->yindex;
3332 y++;
3333 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3335 done = 0;
3336 while (!done) {
3337 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3338 |ButtonMotionMask|ExposureMask, &ev);
3339 switch (ev.type) {
3340 case Expose:
3341 WMHandleEvent(&ev);
3342 break;
3344 case MotionNotify:
3345 if (!grabbed) {
3346 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3347 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3348 XChangeActivePointerGrab(dpy, ButtonMotionMask
3349 |ButtonReleaseMask|ButtonPressMask,
3350 wCursor[WCUR_MOVE], CurrentTime);
3351 grabbed=1;
3353 break;
3355 if (dock->type == WM_CLIP) {
3356 if (ev.xmotion.x_root - ofs_x < 0) {
3357 x = 0;
3358 } else if (ev.xmotion.x_root - ofs_x + ICON_SIZE >
3359 scr->scr_width) {
3360 x = scr->scr_width - ICON_SIZE;
3361 } else {
3362 x = ev.xmotion.x_root - ofs_x;
3364 if (ev.xmotion.y_root - ofs_y < 0) {
3365 y = 0;
3366 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3367 scr->scr_height) {
3368 y = scr->scr_height - ICON_SIZE;
3369 } else {
3370 y = ev.xmotion.y_root - ofs_y;
3372 moveDock(dock, x, y);
3373 } else {
3374 /* move vertically if pointer is inside the dock*/
3375 if ((dock->on_right_side &&
3376 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3377 || (!dock->on_right_side &&
3378 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3380 if (ev.xmotion.y_root - ofs_y < 0) {
3381 y = 0;
3382 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3383 scr->scr_height) {
3384 y = scr->scr_height - ICON_SIZE;
3385 } else {
3386 y = ev.xmotion.y_root - ofs_y;
3388 moveDock(dock, dock->x_pos, y);
3390 /* move horizontally to change sides */
3391 x = ev.xmotion.x_root - ofs_x;
3392 if (!dock->on_right_side) {
3394 /* is on left */
3396 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3397 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3398 -DOCK_EXTRA_SPACE-1, dock->y_pos);
3399 if (superfluous) {
3400 if (ghost==None) {
3401 ghost = MakeGhostDock(dock, dock->x_pos,
3402 scr->scr_width-ICON_SIZE
3403 -DOCK_EXTRA_SPACE-1,
3404 dock->y_pos);
3405 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3406 ghost);
3407 XClearWindow(dpy, scr->dock_shadow);
3410 XMapRaised(dpy, scr->dock_shadow);
3411 swapped = 1;
3412 } else {
3413 if (superfluous && ghost!=None) {
3414 XFreePixmap(dpy, ghost);
3415 ghost = None;
3417 XUnmapWindow(dpy, scr->dock_shadow);
3418 swapped = 0;
3420 } else {
3421 /* is on right */
3422 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3423 XMoveWindow(dpy, scr->dock_shadow,
3424 DOCK_EXTRA_SPACE, dock->y_pos);
3425 if (superfluous) {
3426 if (ghost==None) {
3427 ghost = MakeGhostDock(dock, dock->x_pos,
3428 DOCK_EXTRA_SPACE, dock->y_pos);
3429 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3430 ghost);
3431 XClearWindow(dpy, scr->dock_shadow);
3434 XMapRaised(dpy, scr->dock_shadow);
3435 swapped = -1;
3436 } else {
3437 XUnmapWindow(dpy, scr->dock_shadow);
3438 swapped = 0;
3439 if (superfluous && ghost!=None) {
3440 XFreePixmap(dpy, ghost);
3441 ghost = None;
3446 break;
3448 case ButtonPress:
3449 break;
3451 case ButtonRelease:
3452 if (ev.xbutton.button != event->xbutton.button)
3453 break;
3454 XUngrabPointer(dpy, CurrentTime);
3455 XUnmapWindow(dpy, scr->dock_shadow);
3456 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3457 if (dock->type == WM_DOCK) {
3458 if (swapped!=0) {
3459 if (swapped>0)
3460 dock->on_right_side = 1;
3461 else
3462 dock->on_right_side = 0;
3463 swapDock(dock);
3464 wArrangeIcons(scr, False);
3467 done = 1;
3468 break;
3471 if (superfluous) {
3472 if (ghost!=None)
3473 XFreePixmap(dpy, ghost);
3474 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3476 #ifdef DEBUG
3477 puts("End dock move");
3478 #endif
3483 static void
3484 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3486 WScreen *scr = dock->screen_ptr;
3487 Window wins[2];
3488 WIcon *icon = aicon->icon;
3489 WDock *dock2 = NULL, *last_dock = dock, *clip = NULL;
3490 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3491 XEvent ev;
3492 int x = aicon->x_pos, y = aicon->y_pos;
3493 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3494 int shad_x = x, shad_y = y;
3495 int ix = aicon->xindex, iy = aicon->yindex;
3496 int tmp;
3497 Pixmap ghost = None;
3498 Bool docked;
3499 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3501 if (wPreferences.flags.noupdates)
3502 return;
3504 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3505 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3506 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3507 #ifdef DEBUG0
3508 wwarning("pointer grab failed for icon move");
3509 #endif
3512 wRaiseFrame(icon->core);
3514 if (!wPreferences.flags.noclip)
3515 clip = scr->workspaces[scr->current_workspace]->clip;
3517 if (dock == scr->dock && !wPreferences.flags.noclip)
3518 dock2 = clip;
3519 else if (dock != scr->dock && !wPreferences.flags.nodock)
3520 dock2 = scr->dock;
3522 wins[0] = icon->core->window;
3523 wins[1] = scr->dock_shadow;
3524 XRestackWindows(dpy, wins, 2);
3525 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3526 ICON_SIZE, ICON_SIZE);
3527 if (superfluous) {
3528 if (icon->pixmap!=None)
3529 ghost = MakeGhostIcon(scr, icon->pixmap);
3530 else
3531 ghost = MakeGhostIcon(scr, icon->core->window);
3533 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3534 XClearWindow(dpy, scr->dock_shadow);
3536 XMapWindow(dpy, scr->dock_shadow);
3538 ondock = 1;
3541 while(1) {
3542 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3543 |ButtonMotionMask|ExposureMask, &ev);
3544 switch (ev.type) {
3545 case Expose:
3546 WMHandleEvent(&ev);
3547 break;
3549 case MotionNotify:
3550 if (!grabbed) {
3551 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3552 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3553 XChangeActivePointerGrab(dpy, ButtonMotionMask
3554 |ButtonReleaseMask|ButtonPressMask,
3555 wCursor[WCUR_MOVE], CurrentTime);
3556 grabbed=1;
3557 } else {
3558 break;
3562 x = ev.xmotion.x_root - ofs_x;
3563 y = ev.xmotion.y_root - ofs_y;
3564 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3565 if (tmp && dock2) {
3566 change_dock = 0;
3567 if (last_dock != dock && collapsed) {
3568 last_dock->collapsed = 1;
3569 wDockHideIcons(last_dock);
3570 collapsed = 0;
3572 if (!collapsed && (collapsed = dock->collapsed)) {
3573 dock->collapsed = 0;
3574 wDockShowIcons(dock);
3576 if (dock->auto_raise_lower)
3577 wDockRaise(dock);
3578 last_dock = dock;
3580 else if (dock2) {
3581 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3582 if (tmp) {
3583 change_dock = 1;
3584 if (last_dock != dock2 && collapsed) {
3585 last_dock->collapsed = 1;
3586 wDockHideIcons(last_dock);
3587 collapsed = 0;
3589 if (!collapsed && (collapsed = dock2->collapsed)) {
3590 dock2->collapsed = 0;
3591 wDockShowIcons(dock2);
3593 if (dock2->auto_raise_lower)
3594 wDockRaise(dock2);
3595 last_dock = dock2;
3598 if (aicon->launching
3599 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3600 || (!aicon->running && tmp)) {
3601 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3602 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3604 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3606 if (!ondock) {
3607 XMapWindow(dpy, scr->dock_shadow);
3608 #if 0
3609 if (!collapsed && (collapsed = last_dock->collapsed)) {
3610 last_dock->collapsed = 0;
3611 wDockShowIcons(last_dock);
3613 #endif
3615 ondock = 1;
3616 } else {
3617 if (ondock) {
3618 XUnmapWindow(dpy, scr->dock_shadow);
3619 #if 0
3620 if (last_dock && collapsed &&
3621 aicon->running && (ev.xmotion.state & MOD_MASK)) {
3622 last_dock->collapsed = 1;
3623 wDockHideIcons(last_dock);
3624 collapsed = 0;
3626 #endif
3628 ondock = 0;
3630 XMoveWindow(dpy, icon->core->window, x, y);
3631 break;
3633 case ButtonPress:
3634 break;
3636 case ButtonRelease:
3637 if (ev.xbutton.button != event->xbutton.button)
3638 break;
3639 XUngrabPointer(dpy, CurrentTime);
3640 if (ondock) {
3641 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3642 XUnmapWindow(dpy, scr->dock_shadow);
3643 if (!change_dock) {
3644 reattachIcon(dock, aicon, ix, iy);
3645 if (clip && dock!=clip && clip->auto_raise_lower)
3646 wDockLower(clip);
3647 } else {
3648 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3649 if (!docked) {
3650 /* Slide it back if dock rejected it */
3651 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3652 aicon->y_pos);
3653 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3655 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3656 collapsed = 0;
3659 } else {
3660 aicon->x_pos = x;
3661 aicon->y_pos = y;
3662 if (superfluous) {
3663 if (!aicon->running && !wPreferences.no_animations) {
3664 /* We need to deselect it, even if is deselected in
3665 * wDockDetach(), because else DoKaboom() will fail.
3667 if (aicon->icon->selected)
3668 wIconSelect(aicon->icon);
3669 DoKaboom(scr,aicon->icon->core->window, x, y);
3672 if (clip && clip->auto_raise_lower)
3673 wDockLower(clip);
3674 wDockDetach(dock, aicon);
3676 if (collapsed) {
3677 last_dock->collapsed = 1;
3678 wDockHideIcons(last_dock);
3679 collapsed = 0;
3681 if (superfluous) {
3682 if (ghost!=None)
3683 XFreePixmap(dpy, ghost);
3684 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3686 #ifdef DEBUG
3687 puts("End icon move");
3688 #endif
3689 return;
3695 static int
3696 getClipButton(int px, int py)
3698 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
3700 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
3701 return CLIP_IDLE;
3703 if (py <= pt-((int)ICON_SIZE-1-px))
3704 return CLIP_FORWARD;
3705 else if (px <= pt-((int)ICON_SIZE-1-py))
3706 return CLIP_REWIND;
3708 return CLIP_IDLE;
3712 static void
3713 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
3715 XEvent ev;
3716 int done, direction, new_ws;
3717 int new_dir;
3718 WDock *clip = scr->clip_icon->dock;
3720 direction = getClipButton(event->xbutton.x, event->xbutton.y);
3722 clip->lclip_button_pushed = direction==CLIP_REWIND;
3723 clip->rclip_button_pushed = direction==CLIP_FORWARD;
3725 wClipIconPaint(scr->clip_icon);
3726 done = 0;
3727 while(!done) {
3728 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
3729 |ButtonPressMask, &ev);
3730 switch (ev.type) {
3731 case Expose:
3732 WMHandleEvent(&ev);
3733 break;
3735 case MotionNotify:
3736 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
3737 if (new_dir != direction) {
3738 direction = new_dir;
3739 clip->lclip_button_pushed = direction==CLIP_REWIND;
3740 clip->rclip_button_pushed = direction==CLIP_FORWARD;
3741 wClipIconPaint(scr->clip_icon);
3743 break;
3745 case ButtonPress:
3746 break;
3748 case ButtonRelease:
3749 if (ev.xbutton.button == event->xbutton.button)
3750 done = 1;
3754 clip->lclip_button_pushed = 0;
3755 clip->rclip_button_pushed = 0;
3757 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
3759 if (direction == CLIP_FORWARD) {
3760 if (scr->current_workspace < scr->workspace_count-1)
3761 wWorkspaceChange(scr, scr->current_workspace+1);
3762 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
3763 wWorkspaceChange(scr, scr->current_workspace+1);
3764 else if (wPreferences.ws_cycle)
3765 wWorkspaceChange(scr, 0);
3767 else if (direction == CLIP_REWIND) {
3768 if (scr->current_workspace > 0)
3769 wWorkspaceChange(scr, scr->current_workspace-1);
3770 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
3771 wWorkspaceChange(scr, scr->workspace_count-1);
3774 wClipIconPaint(scr->clip_icon);
3778 static void
3779 iconMouseDown(WObjDescriptor *desc, XEvent *event)
3781 WAppIcon *aicon = desc->parent;
3782 WDock *dock = aicon->dock;
3783 WScreen *scr = aicon->icon->core->screen_ptr;
3785 if (aicon->editing || WCHECK_STATE(WSTATE_MODAL))
3786 return;
3788 scr->last_dock = dock;
3790 if (dock->menu->flags.mapped)
3791 wMenuUnmap(dock->menu);
3793 if (IsDoubleClick(scr, event)) {
3794 /* double-click was not in the main clip icon */
3795 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
3796 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
3797 iconDblClick(desc, event);
3798 return;
3802 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
3803 XUnmapWindow(dpy, scr->clip_balloon);
3804 scr->flags.clip_balloon_mapped = 0;
3807 #ifdef DEBUG
3808 puts("handling dock");
3809 #endif
3810 if (event->xbutton.button == Button1) {
3811 if (event->xbutton.state & MOD_MASK)
3812 wDockLower(dock);
3813 else
3814 wDockRaise(dock);
3816 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
3817 dock->type!=WM_DOCK) {
3818 wIconSelect(aicon->icon);
3819 return;
3822 if (aicon->yindex==0 && aicon->xindex==0) {
3823 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
3824 && dock->type==WM_CLIP)
3825 handleClipChangeWorkspace(scr, event);
3826 else
3827 handleDockMove(dock, aicon, event);
3828 } else
3829 handleIconMove(dock, aicon, event);
3831 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
3832 aicon->xindex==0 && aicon->yindex==0) {
3833 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
3834 event->xbutton.y_root+2);
3835 if (scr->clip_ws_menu) {
3836 WMenu *menu;
3837 menu = scr->clip_ws_menu;
3838 desc = &menu->menu->descriptor;
3840 event->xany.send_event = True;
3841 (*desc->handle_mousedown)(desc, event);
3843 } else if (event->xbutton.button == Button3) {
3844 openDockMenu(dock, aicon, event);
3849 static void
3850 showClipBalloon(WDock *dock, int workspace)
3852 int w, h;
3853 int x, y;
3854 WScreen *scr = dock->screen_ptr;
3855 char *text;
3856 Window stack[2];
3858 scr->flags.clip_balloon_mapped = 1;
3859 XMapWindow(dpy, scr->clip_balloon);
3861 text = scr->workspaces[workspace]->name;
3863 w = wTextWidth(scr->clip_title_font->font, text, strlen(text));
3865 h = scr->clip_title_font->height;
3866 XResizeWindow(dpy, scr->clip_balloon, w, h);
3868 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
3869 y = dock->y_pos + ICON_SIZE-scr->clip_title_font->height - 3;
3871 if (x+w > scr->scr_width) {
3872 x = scr->scr_width - w;
3873 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
3874 y = dock->y_pos - h - 1;
3875 else
3876 y = dock->y_pos + ICON_SIZE;
3877 XRaiseWindow(dpy, scr->clip_balloon);
3878 } else {
3879 stack[0] = scr->clip_icon->icon->core->window;
3880 stack[1] = scr->clip_balloon;
3881 XRestackWindows(dpy, stack, 2);
3883 XMoveWindow(dpy, scr->clip_balloon, x, y);
3884 XSetForeground(dpy, scr->clip_title_gc,
3885 scr->clip_title_pixel[CLIP_NORMAL]);
3886 XClearWindow(dpy, scr->clip_balloon);
3887 wDrawString(scr->clip_balloon, scr->clip_title_font, scr->clip_title_gc,
3888 0, scr->clip_title_font->y, text, strlen(text));
3892 static void
3893 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
3895 WAppIcon *btn = (WAppIcon*)desc->parent;
3896 WDock *dock;
3898 assert(event->type==EnterNotify);
3900 if(desc->parent_type!=WCLASS_DOCK_ICON)
3901 return;
3903 dock = btn->dock;
3904 if (!dock || dock->type!=WM_CLIP)
3905 return;
3907 /* The auto raise/lower code */
3908 if (dock->auto_lower_magic) {
3909 WMDeleteTimerHandler(dock->auto_lower_magic);
3910 dock->auto_lower_magic = NULL;
3912 if (dock->auto_raise_lower && !dock->auto_raise_magic) {
3913 dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY,
3914 clipAutoRaise,
3915 (void *)dock);
3918 /* The auto expand/collapse code */
3919 if (dock->auto_collapse_magic) {
3920 WMDeleteTimerHandler(dock->auto_collapse_magic);
3921 dock->auto_collapse_magic = NULL;
3923 if (dock->auto_collapse && !dock->auto_expand_magic) {
3924 dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY,
3925 clipAutoExpand,
3926 (void *)dock);
3929 if (btn->xindex == 0 && btn->yindex == 0)
3930 showClipBalloon(dock, dock->screen_ptr->current_workspace);
3931 else {
3932 if (dock->screen_ptr->flags.clip_balloon_mapped) {
3933 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
3934 dock->screen_ptr->flags.clip_balloon_mapped = 0;
3940 static void
3941 clipLeave(WDock *dock)
3943 XEvent event;
3944 WObjDescriptor *desc = NULL;
3946 if (!dock || dock->type!=WM_CLIP)
3947 return;
3949 if (XCheckTypedEvent(dpy, EnterNotify, &event)!=False) {
3950 if (XFindContext(dpy, event.xcrossing.window, wWinContext,
3951 (XPointer *)&desc)!=XCNOENT
3952 && desc && desc->parent_type==WCLASS_DOCK_ICON
3953 && ((WAppIcon*)desc->parent)->dock
3954 && ((WAppIcon*)desc->parent)->dock->type==WM_CLIP) {
3955 /* We didn't left the Clip yet */
3956 XPutBackEvent(dpy, &event);
3957 return;
3960 XPutBackEvent(dpy, &event);
3961 } else {
3962 /* We entered a withdrawn window, so we're still in Clip */
3963 return;
3966 if (dock->auto_raise_magic) {
3967 WMDeleteTimerHandler(dock->auto_raise_magic);
3968 dock->auto_raise_magic = NULL;
3970 if (dock->auto_raise_lower && !dock->auto_lower_magic) {
3971 dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY,
3972 clipAutoLower,
3973 (void *)dock);
3976 if (dock->auto_expand_magic) {
3977 WMDeleteTimerHandler(dock->auto_expand_magic);
3978 dock->auto_expand_magic = NULL;
3980 if (dock->auto_collapse && !dock->auto_collapse_magic) {
3981 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
3982 clipAutoCollapse,
3983 (void *)dock);
3988 static void
3989 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
3991 WAppIcon *btn = (WAppIcon*)desc->parent;
3993 assert(event->type==LeaveNotify);
3995 if(desc->parent_type!=WCLASS_DOCK_ICON)
3996 return;
3998 clipLeave(btn->dock);
4002 static void
4003 clipAutoCollapse(void *cdata)
4005 WDock *dock = (WDock *)cdata;
4007 if (dock->type!=WM_CLIP)
4008 return;
4010 if (dock->auto_collapse) {
4011 dock->collapsed = 1;
4012 wDockHideIcons(dock);
4014 dock->auto_collapse_magic = NULL;
4018 static void
4019 clipAutoExpand(void *cdata)
4021 WDock *dock = (WDock *)cdata;
4023 if (dock->type!=WM_CLIP)
4024 return;
4026 if (dock->auto_collapse) {
4027 dock->collapsed = 0;
4028 wDockShowIcons(dock);
4030 dock->auto_expand_magic = NULL;
4034 static void
4035 clipAutoLower(void *cdata)
4037 WDock *dock = (WDock *)cdata;
4039 if (dock->type!=WM_CLIP)
4040 return;
4042 if (dock->auto_raise_lower)
4043 wDockLower(dock);
4045 dock->auto_lower_magic = NULL;
4049 static void
4050 clipAutoRaise(void *cdata)
4052 WDock *dock = (WDock *)cdata;
4054 if (dock->type!=WM_CLIP)
4055 return;
4057 if (dock->auto_raise_lower)
4058 wDockRaise(dock);
4060 if (dock->screen_ptr->flags.clip_balloon_mapped) {
4061 showClipBalloon(dock, dock->screen_ptr->current_workspace);
4064 dock->auto_raise_magic = NULL;