Initial revision
[wmaker-crm.git] / src / dock.c
blobf21cd4bc3e5abb9b2375c5909af32766ec5f9dc2
1 /* dock.c- built-in Dock module for WindowMaker
2 *
3 * WindowMaker 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 #ifdef WMSOUND
59 #include "wmsound.h"
60 #endif
62 #include <proplist.h>
67 /**** Local variables ****/
68 #define CLIP_REWIND 1
69 #define CLIP_IDLE 0
70 #define CLIP_FORWARD 2
72 /* This should be 24. It looks better.
73 * Proof: the golden ratio is ~1.6:
74 * 64/1.6 = 24, thus 24 will look better than 22 :)
75 * -Alfredo
77 * This is true, but... ;)
78 * CLIP_BUTTON_SIZE only means the button itself, without the black delimiting
79 * line. So because one sees the black line together with the button, it should
80 * be 23. There is no big difference between 23 and 24, but 23 gives also a
81 * better aspect ratio between the button and the arrow mark.
82 * Anyway if you feel stronger for 24 you can change it as it is not very
83 * important.
85 * BTW I think you meant 64-64/1.6=24 or 64/(1.6*1.6) ;)
86 * -Dan
88 #define CLIP_BUTTON_SIZE 23
91 /**** Global variables ****/
93 /* in dockedapp.c */
94 extern void DestroyDockAppSettingsPanel();
96 extern void ShowDockAppSettingsPanel(WAppIcon *aicon);
100 extern Cursor wCursor[WCUR_LAST];
102 extern WPreferences wPreferences;
104 extern XContext wWinContext;
106 #ifdef OFFIX_DND
107 extern Atom _XA_DND_PROTOCOL;
108 #endif
111 #define MOD_MASK wPreferences.modifier_mask
113 extern void appIconMouseDown(WObjDescriptor *desc, XEvent *event);
115 #define ICON_SIZE wPreferences.icon_size
118 /***** Local variables ****/
120 static proplist_t dCommand=NULL;
121 #ifdef OFFIX_DND
122 static proplist_t dDropCommand=NULL;
123 #endif
124 static proplist_t dAutoLaunch, dName, dForced, dBuggyApplication, dYes, dNo;
125 static proplist_t dHost, dDock, dClip;
126 static proplist_t dAutoAttractIcons, dKeepAttracted;
128 static proplist_t dPosition, dApplications, dLowered, dCollapsed, dAutoCollapse;
130 static void dockIconPaint(WAppIcon *btn);
132 static void iconMouseDown(WObjDescriptor *desc, XEvent *event);
134 static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state);
136 static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock);
138 static int getClipButton(int px, int py);
140 static void toggleLowered(WDock *dock);
142 static void toggleCollapsed(WDock *dock);
144 static void toggleAutoCollapse(WDock *dock);
146 static void clipIconExpose(WObjDescriptor *desc, XEvent *event);
148 static void clipLeave(WDock *dock);
150 static void handleClipChangeWorkspace(WScreen *scr, XEvent *event);
152 Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y);
154 static void clipEnterNotify(WObjDescriptor *desc, XEvent *event);
155 static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event);
156 static void clipAutoCollapse(void *cdata);
157 static void launchDockedApplication(WAppIcon *btn);
159 static void showClipBalloon(WDock *dock, int workspace);
161 #ifdef OFFIX_DND
163 #define DndNotDnd -1
164 #define DndUnknown 0
165 #define DndRawData 1
166 #define DndFile 2
167 #define DndFiles 3
168 #define DndText 4
169 #define DndDir 5
170 #define DndLink 6
171 #define DndExe 7
173 #define DndEND 8
175 #endif /* OFFIX_DND */
179 static void
180 make_keys()
182 if (dCommand!=NULL)
183 return;
185 dCommand = PLRetain(PLMakeString("Command"));
186 #ifdef OFFIX_DND
187 dDropCommand = PLRetain(PLMakeString("DropCommand"));
188 #endif
189 dAutoLaunch = PLRetain(PLMakeString("AutoLaunch"));
190 dName = PLRetain(PLMakeString("Name"));
191 dForced = PLRetain(PLMakeString("Forced"));
192 dBuggyApplication = PLRetain(PLMakeString("BuggyApplication"));
193 dYes = PLRetain(PLMakeString("Yes"));
194 dNo = PLRetain(PLMakeString("No"));
195 dHost = PLRetain(PLMakeString("Host"));
197 dPosition = PLMakeString("Position");
198 dApplications = PLMakeString("Applications");
199 dLowered = PLMakeString("Lowered");
200 dCollapsed = PLMakeString("Collapsed");
201 dAutoCollapse = PLMakeString("AutoCollapse");
202 dAutoAttractIcons = PLMakeString("AutoAttractIcons");
203 dKeepAttracted = PLMakeString("KeepAttracted");
205 dDock = PLMakeString("Dock");
206 dClip = PLMakeString("Clip");
211 static void
212 renameCallback(WMenu *menu, WMenuEntry *entry)
214 WDock *dock = entry->clientdata;
215 char buffer[128];
216 int wspace;
217 char *name;
219 assert(entry->clientdata!=NULL);
221 wspace = dock->screen_ptr->current_workspace;
223 name = wstrdup(dock->screen_ptr->workspaces[wspace]->name);
225 sprintf(buffer, _("Type the name for workspace %i:"), wspace+1);
226 if (wInputDialog(dock->screen_ptr, _("Rename Workspace"), buffer,
227 &name)==WDB_OK) {
228 wWorkspaceRename(dock->screen_ptr, wspace, name);
230 if (name) {
231 free(name);
236 static void
237 toggleLoweredCallback(WMenu *menu, WMenuEntry *entry)
239 assert(entry->clientdata!=NULL);
241 toggleLowered(entry->clientdata);
243 entry->flags.indicator_on = !((WDock*)entry->clientdata)->lowered;
245 wMenuPaint(menu);
250 static void
251 killCallback(WMenu *menu, WMenuEntry *entry)
253 WAppIcon *icon;
254 #ifdef REDUCE_APPICONS
255 WAppIconAppList *tapplist;
257 extern Atom _XA_WM_DELETE_WINDOW;
258 #endif
260 assert(entry->clientdata!=NULL);
262 icon = (WAppIcon*)entry->clientdata;
263 #ifdef REDUCE_APPICONS
264 /* Send a delete message to the main window of each application
265 * bound to this docked appicon. - cls
267 tapplist = icon->applist;
268 while (tapplist != NULL) {
269 if (tapplist->wapp->main_window_desc != NULL) {
270 if (tapplist->wapp->main_window_desc->protocols.DELETE_WINDOW) {
271 wClientSendProtocol(tapplist->wapp->main_window_desc,
272 _XA_WM_DELETE_WINDOW, CurrentTime);
273 } else {
274 wClientKill(tapplist->wapp->main_window_desc);
277 tapplist = tapplist->next;
279 #else
280 if (wPreferences.dont_confirm_kill
281 || wMessageDialog(menu->frame->screen_ptr, _("Kill Application"),
282 _("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
283 _("Yes"), _("No"), NULL)==WAPRDefault) {
284 if (icon->icon && icon->icon->owner) {
285 wClientKill(icon->icon->owner);
288 #endif /* !REDUCE_APPICONS */
292 static LinkedList*
293 getSelected(WDock *dock)
295 LinkedList *ret=NULL;
296 WAppIcon *btn;
297 int i;
299 for (i=1; i<dock->max_icons; i++) {
300 btn = dock->icon_array[i];
301 if (btn && btn->icon->selected) {
302 ret = list_cons(btn, ret);
306 return ret;
310 static void
311 paintClipButtons(WAppIcon *clipIcon, Bool lpushed, Bool rpushed)
313 Window win = clipIcon->icon->core->window;
314 WScreen *scr = clipIcon->icon->core->screen_ptr;
315 XPoint p[4];
316 int pt = CLIP_BUTTON_SIZE*ICON_SIZE/64;
317 int tp = ICON_SIZE - pt;
318 int as = pt - 15; /* 15 = 5+5+5 */
321 if (rpushed) {
322 p[0].x = tp+1;
323 p[0].y = 1;
324 p[1].x = ICON_SIZE-2;
325 p[1].y = 1;
326 p[2].x = ICON_SIZE-2;
327 p[2].y = pt-1;
328 } else if (lpushed) {
329 p[0].x = 1;
330 p[0].y = tp;
331 p[1].x = pt;
332 p[1].y = ICON_SIZE-2;
333 p[2].x = 1;
334 p[2].y = ICON_SIZE-2;
336 if (lpushed || rpushed) {
337 XSetForeground(dpy, scr->draw_gc, scr->white_pixel);
338 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
339 XSetForeground(dpy, scr->draw_gc, scr->black_pixel);
342 p[0].x = p[3].x = ICON_SIZE-6-as;
343 p[0].y = p[3].y = 5;
344 p[1].x = ICON_SIZE-6;
345 p[1].y = 5;
346 p[2].x = ICON_SIZE-6;
347 p[2].y = 5+as;
348 if (rpushed) {
349 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
350 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
351 } else {
352 XFillPolygon(dpy, win, scr->clip_title_gc, p,3,Convex,CoordModeOrigin);
353 XDrawLines(dpy, win, scr->clip_title_gc, p,4,CoordModeOrigin);
356 p[0].x = p[3].x = 5;
357 p[0].y = p[3].y = ICON_SIZE-6-as;
358 p[1].x = 5;
359 p[1].y = ICON_SIZE-6;
360 p[2].x = 5+as;
361 p[2].y = ICON_SIZE-6;
362 if (lpushed) {
363 XFillPolygon(dpy, win, scr->draw_gc, p, 3, Convex, CoordModeOrigin);
364 XDrawLines(dpy, win, scr->draw_gc, p, 4, CoordModeOrigin);
365 } else {
366 XFillPolygon(dpy, win, scr->clip_title_gc, p,3,Convex,CoordModeOrigin);
367 XDrawLines(dpy, win, scr->clip_title_gc, p,4,CoordModeOrigin);
372 RImage*
373 wClipMakeTile(WScreen *scr, RImage *normalTile)
375 RImage *tile = RCloneImage(normalTile);
376 RColor black;
377 RColor dark;
378 RColor light;
379 int pt, tp;
381 pt = CLIP_BUTTON_SIZE*wPreferences.icon_size/64;
382 tp = wPreferences.icon_size-1 - pt;
384 black.alpha = 255;
385 black.red = black.green = black.blue = 0;
387 dark.alpha = 0;
388 dark.red = dark.green = dark.blue = 80;
390 light.alpha = 0;
391 light.red = light.green = light.blue = 80;
394 /* top right */
395 ROperateLine(tile, RSubtractOperation, tp, 0, wPreferences.icon_size-2,
396 pt-1, &dark);
397 RDrawLine(tile, tp-1, 0, wPreferences.icon_size-1, pt+1, &black);
398 ROperateLine(tile, RAddOperation, tp, 2, wPreferences.icon_size-3,
399 pt, &light);
402 /* bottom left */
403 ROperateLine(tile, RAddOperation, 2, tp+2, pt-2,
404 wPreferences.icon_size-3, &dark);
405 RDrawLine(tile, 0, tp-1, pt+1, wPreferences.icon_size-1, &black);
406 ROperateLine(tile, RSubtractOperation, 0, tp-2, pt+1,
407 wPreferences.icon_size-2, &light);
409 return tile;
413 static void
414 removeIconsCallback(WMenu *menu, WMenuEntry *entry)
416 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
417 WDock *dock;
418 WAppIcon *aicon;
419 LinkedList *selectedIcons;
420 int keepit;
422 assert(clickedIcon!=NULL);
424 dock = clickedIcon->dock;
426 selectedIcons = getSelected(dock);
428 if (selectedIcons) {
429 if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"),
430 _("All selected icons will be removed!"),
431 _("OK"), _("Cancel"), NULL)!=WAPRDefault) {
432 return;
434 } else {
435 if (clickedIcon->xindex==0 && clickedIcon->yindex==0)
436 return;
437 selectedIcons = list_cons(clickedIcon, NULL);
440 while(selectedIcons) {
441 aicon = selectedIcons->head;
442 keepit = aicon->running && wApplicationOf(aicon->main_window);
443 wDockDetach(dock, aicon);
444 if (keepit) {
445 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
446 #ifndef STRICTNS
447 wLowerFrame(aicon->icon->core);
448 #endif
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) {
479 if (wInputDialog(dock->screen_ptr, _("Keep Icon"),
480 _("Type the command used to launch the application"),
481 &command)==WDB_OK) {
482 if (command && (command[0]==0 ||
483 (command[0]=='-' && command[1]==0))) {
484 free(command);
485 command = NULL;
487 clickedIcon->command = command;
488 } else {
489 if (command)
490 free(command);
491 return;
495 selectedIcons = list_cons(clickedIcon, NULL);
498 while (selectedIcons) {
499 aicon = selectedIcons->head;
500 if (aicon->icon->selected)
501 wIconSelect(aicon->icon);
502 if (aicon && aicon->attracted && aicon->command) {
503 aicon->attracted = 0;
504 if (aicon->icon->shadowed) {
505 aicon->icon->shadowed = 0;
506 aicon->icon->force_paint = 1;
507 wAppIconPaint(aicon);
510 list_remove_head(&selectedIcons);
517 static void
518 toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry)
520 WDock *dock = (WDock*)entry->clientdata;
522 assert(entry->clientdata!=NULL);
524 dock->attract_icons = !dock->attract_icons;
525 /*if (!dock->attract_icons)
526 dock->keep_attracted = 0;*/
528 entry->flags.indicator_on = dock->attract_icons;
530 wMenuPaint(menu);
534 static void
535 toggleKeepCallback(WMenu *menu, WMenuEntry *entry)
537 WDock *dock = (WDock*)entry->clientdata;
538 WAppIcon *btn;
539 int i;
541 assert(entry->clientdata!=NULL);
543 dock->keep_attracted = !dock->keep_attracted;
545 if (dock->keep_attracted) {
546 for (i=0; i< dock->max_icons; i++) {
547 btn = dock->icon_array[i];
548 if (btn && btn->attracted && btn->command) {
549 btn->attracted = 0;
550 if (btn->icon->shadowed) {
551 btn->icon->shadowed = 0;
552 btn->icon->force_paint = 1;
553 wAppIconPaint(btn);
559 entry->flags.indicator_on = dock->keep_attracted;
561 wMenuPaint(menu);
565 static void
566 selectCallback(WMenu *menu, WMenuEntry *entry)
568 WAppIcon *icon = (WAppIcon*)entry->clientdata;
570 assert(icon!=NULL);
572 wIconSelect(icon->icon);
574 wMenuPaint(menu);
578 static void
579 colectIconsCallback(WMenu *menu, WMenuEntry *entry)
581 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
582 WDock *clip;
583 WAppIcon *aicon;
584 int x, y, x_pos, y_pos;
586 assert(entry->clientdata!=NULL);
587 clip = clickedIcon->dock;
589 aicon = clip->screen_ptr->app_icon_list;
591 while (aicon) {
592 if (!aicon->docked && wDockFindFreeSlot(clip, &x, &y)) {
593 x_pos = clip->x_pos + x*ICON_SIZE;
594 y_pos = clip->y_pos + y*ICON_SIZE;
595 if (aicon->x_pos != x_pos || aicon->y_pos != y_pos) {
596 #ifdef ANIMATIONS
597 if (wPreferences.no_animations) {
598 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
599 } else {
600 SlideWindow(aicon->icon->core->window,
601 aicon->x_pos, aicon->y_pos, x_pos, y_pos);
603 #else
604 XMoveWindow(dpy, aicon->icon->core->window, x_pos, y_pos);
605 #endif /* ANIMATIONS */
607 aicon->attracted = 1;
608 if (!clip->keep_attracted && !aicon->icon->shadowed) {
609 aicon->icon->shadowed = 1;
610 aicon->icon->force_paint = 1;
611 /* We don't do an wAppIconPaint() here because it's in
612 * wDockAttachIcon(). -Dan
615 wDockAttachIcon(clip, aicon, x, y);
616 if (clip->collapsed || !clip->mapped)
617 XUnmapWindow(dpy, aicon->icon->core->window);
619 aicon = aicon->next;
624 static void
625 selectIconsCallback(WMenu *menu, WMenuEntry *entry)
627 WAppIcon *clickedIcon = (WAppIcon*)entry->clientdata;
628 WDock *dock;
629 LinkedList *selectedIcons;
630 WAppIcon *btn;
631 int i;
633 assert(clickedIcon!=NULL);
634 dock = clickedIcon->dock;
636 selectedIcons = getSelected(dock);
638 if (!selectedIcons) {
639 for (i=1; i<dock->max_icons; i++) {
640 btn = dock->icon_array[i];
641 if (btn && !btn->icon->selected) {
642 wIconSelect(btn->icon);
645 } else {
646 while(selectedIcons) {
647 btn = selectedIcons->head;
648 wIconSelect(btn->icon);
649 list_remove_head(&selectedIcons);
653 wMenuPaint(menu);
657 static void
658 toggleCollapsedCallback(WMenu *menu, WMenuEntry *entry)
660 assert(entry->clientdata!=NULL);
662 toggleCollapsed(entry->clientdata);
664 entry->flags.indicator_on = ((WDock*)entry->clientdata)->collapsed;
666 wMenuPaint(menu);
670 static void
671 toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry)
673 assert(entry->clientdata!=NULL);
675 toggleAutoCollapse(entry->clientdata);
677 entry->flags.indicator_on = ((WDock*)entry->clientdata)->auto_collapse;
679 wMenuPaint(menu);
683 static void
684 launchCallback(WMenu *menu, WMenuEntry *entry)
686 WAppIcon *btn = (WAppIcon*)entry->clientdata;
688 launchDockedApplication(btn);
692 static void
693 settingsCallback(WMenu *menu, WMenuEntry *entry)
695 WAppIcon *btn = (WAppIcon*)entry->clientdata;
697 if (btn->editing)
698 return;
699 ShowDockAppSettingsPanel(btn);
703 static void
704 hideCallback(WMenu *menu, WMenuEntry *entry)
706 WApplication *wapp;
707 WAppIcon *btn = (WAppIcon*)entry->clientdata;
709 wapp = wApplicationOf(btn->icon->owner->main_window);
711 if (wapp->flags.hidden) {
712 wWorkspaceChange(btn->icon->core->screen_ptr,wapp->last_workspace);
713 wUnhideApplication(wapp, False, False);
714 } else {
715 wHideApplication(wapp);
720 static void
721 unhideHereCallback(WMenu *menu, WMenuEntry *entry)
723 WApplication *wapp;
724 WAppIcon *btn = (WAppIcon*)entry->clientdata;
726 wapp = wApplicationOf(btn->icon->owner->main_window);
728 wUnhideApplication(wapp, False, True);
732 WAppIcon*
733 mainIconCreate(WScreen *scr, int type)
735 WAppIcon *btn;
736 int x_pos;
738 if (type == WM_CLIP) {
739 if (scr->clip_icon)
740 return scr->clip_icon;
741 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP);
742 btn->icon->core->descriptor.handle_expose = clipIconExpose;
743 btn->icon->core->descriptor.handle_enternotify = clipEnterNotify;
744 btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
745 /*x_pos = scr->scr_width - ICON_SIZE*2 - DOCK_EXTRA_SPACE;*/
746 x_pos = 0;
748 else {
749 btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL);
750 x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
753 btn->xindex = 0;
754 btn->yindex = 0;
756 btn->icon->core->descriptor.handle_mousedown = iconMouseDown;
757 btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
758 btn->icon->core->descriptor.parent = btn;
759 /*ChangeStackingLevel(btn->icon->core, NSDockWindowLevel);*/
760 XMapWindow(dpy, btn->icon->core->window);
761 btn->x_pos = x_pos;
762 btn->y_pos = 0;
763 btn->docked = 1;
764 if (type == WM_CLIP)
765 scr->clip_icon = btn;
767 return btn;
771 static void
772 switchWSCommand(WMenu *menu, WMenuEntry *entry)
774 WAppIcon *btn, *icon = (WAppIcon*) entry->clientdata;
775 WScreen *scr = icon->icon->core->screen_ptr;
776 WDock *src, *dest;
777 LinkedList *selectedIcons;
778 int x, y;
780 if (entry->order == scr->current_workspace)
781 return;
782 src = icon->dock;
783 dest = scr->workspaces[entry->order]->clip;
785 selectedIcons = getSelected(src);
787 if (selectedIcons) {
788 while(selectedIcons) {
789 btn = selectedIcons->head;
790 if (wDockFindFreeSlot(dest, &x, &y)) {
791 moveIconBetweenDocks(src, dest, btn, x, y);
792 XUnmapWindow(dpy, btn->icon->core->window);
794 list_remove_head(&selectedIcons);
796 } else if (icon != scr->clip_icon) {
797 if (wDockFindFreeSlot(dest, &x, &y)) {
798 moveIconBetweenDocks(src, dest, icon, x, y);
799 XUnmapWindow(dpy, icon->icon->core->window);
806 static void
807 launchDockedApplication(WAppIcon *btn)
809 WScreen *scr = btn->icon->core->screen_ptr;
811 if (!btn->launching && btn->command!=NULL) {
812 if (!btn->forced_dock) {
813 btn->relaunching = btn->running;
814 btn->running = 1;
816 if (btn->wm_instance || btn->wm_class) {
817 WWindowAttributes attr;
818 memset(&attr, 0, sizeof(WWindowAttributes));
819 wDefaultFillAttributes(scr, btn->wm_instance, btn->wm_class, &attr,
820 True);
822 if (!attr.no_appicon && !btn->buggy_app)
823 btn->launching = 1;
824 else
825 btn->running = 0;
827 btn->drop_launch = 0;
828 scr->last_dock = btn->dock;
829 btn->pid = execCommand(btn, btn->command, NULL);
830 if (btn->pid>0) {
831 if (btn->buggy_app) {
832 /* give feedback that the app was launched */
833 btn->launching = 1;
834 dockIconPaint(btn);
835 btn->launching = 0;
836 WMAddTimerHandler(200, (WMCallback*)dockIconPaint, btn);
837 } else {
838 dockIconPaint(btn);
840 } else {
841 wwarning(_("could not launch application %s\n"), btn->command);
842 btn->launching = 0;
843 if (!btn->relaunching)
844 btn->running = 0;
851 static void
852 updateWorkspaceMenu(WMenu *menu, WAppIcon *icon)
854 WScreen *scr = menu->frame->screen_ptr;
855 char title[MAX_WORKSPACENAME_WIDTH+1];
856 int i;
858 if (!menu || !icon)
859 return;
861 for (i=0; i<scr->workspace_count; i++) {
862 if (i < menu->entry_no) {
863 if (strcmp(menu->entries[i]->text,scr->workspaces[i]->name)!=0) {
864 free(menu->entries[i]->text);
865 strcpy(title, scr->workspaces[i]->name);
866 menu->entries[i]->text = wstrdup(title);
867 menu->flags.realized = 0;
869 menu->entries[i]->clientdata = (void*)icon;
870 } else {
871 strcpy(title, scr->workspaces[i]->name);
873 wMenuAddCallback(menu, title, switchWSCommand, (void*)icon);
875 menu->flags.realized = 0;
877 if (i == scr->current_workspace) {
878 wMenuSetEnabled(menu, i, False);
879 } else {
880 wMenuSetEnabled(menu, i, True);
884 if (!menu->flags.realized)
885 wMenuRealize(menu);
889 static WMenu*
890 makeWorkspaceMenu(WScreen *scr)
892 WMenu *menu;
894 menu = wMenuCreate(scr, NULL, False);
895 if (!menu)
896 wwarning(_("could not create workspace submenu for Clip menu"));
898 wMenuAddCallback(menu, "", switchWSCommand, (void*)scr->clip_icon);
900 menu->flags.realized = 0;
901 wMenuRealize(menu);
903 return menu;
907 static void
908 updateClipOptionsMenu(WMenu *menu, WDock *dock)
910 WMenuEntry *entry;
911 int index = 0;
913 if (!menu || !dock)
914 return;
916 /* floating */
917 entry = menu->entries[index];
918 entry->flags.indicator_on = !dock->lowered;
919 entry->clientdata = dock;
921 /* collapsed */
922 entry = menu->entries[++index];
923 entry->flags.indicator_on = dock->collapsed;
924 entry->clientdata = dock;
926 /* auto-collapse */
927 entry = menu->entries[++index];
928 entry->flags.indicator_on = dock->auto_collapse;
929 entry->clientdata = dock;
931 /* attract icons */
932 entry = menu->entries[++index];
933 entry->flags.indicator_on = dock->attract_icons;
934 entry->clientdata = dock;
936 /* keep attracted icons */
937 entry = menu->entries[++index];
938 entry->flags.indicator_on = dock->keep_attracted;
939 entry->clientdata = dock;
941 menu->flags.realized = 0;
942 wMenuRealize(menu);
946 static WMenu*
947 makeClipOptionsMenu(WScreen *scr)
949 WMenu *menu;
950 WMenuEntry *entry;
952 menu = wMenuCreate(scr, NULL, False);
953 if (!menu) {
954 wwarning(_("could not create options submenu for Clip menu"));
955 return NULL;
958 entry = wMenuAddCallback(menu, _("Floating Clip"),
959 toggleLoweredCallback, NULL);
960 entry->flags.indicator = 1;
961 entry->flags.indicator_on = 1;
962 entry->flags.indicator_type = MI_CHECK;
964 entry = wMenuAddCallback(menu, _("Collapsed"),
965 toggleCollapsedCallback, NULL);
966 entry->flags.indicator = 1;
967 entry->flags.indicator_on = 1;
968 entry->flags.indicator_type = MI_CHECK;
970 entry = wMenuAddCallback(menu, _("AutoCollapse"),
971 toggleAutoCollapseCallback, NULL);
972 entry->flags.indicator = 1;
973 entry->flags.indicator_on = 1;
974 entry->flags.indicator_type = MI_CHECK;
976 entry = wMenuAddCallback(menu, _("AutoAttract Icons"),
977 toggleAutoAttractCallback, NULL);
978 entry->flags.indicator = 1;
979 entry->flags.indicator_on = 1;
980 entry->flags.indicator_type = MI_CHECK;
982 entry = wMenuAddCallback(menu, _("Keep Attracted Icons"),
983 toggleKeepCallback, NULL);
984 entry->flags.indicator = 1;
985 entry->flags.indicator_on = 1;
986 entry->flags.indicator_type = MI_CHECK;
988 menu->flags.realized = 0;
989 wMenuRealize(menu);
991 return menu;
995 static WMenu*
996 dockMenuCreate(WScreen *scr, int type)
998 WMenu *menu;
999 WMenuEntry *entry;
1001 if (type == WM_CLIP && scr->clip_menu)
1002 return scr->clip_menu;
1004 menu = wMenuCreate(scr, NULL, False);
1005 if (type != WM_CLIP) {
1006 entry = wMenuAddCallback(menu, _("Floating Dock"),
1007 toggleLoweredCallback, NULL);
1008 entry->flags.indicator = 1;
1009 entry->flags.indicator_on = 1;
1010 entry->flags.indicator_type = MI_CHECK;
1011 } else {
1012 entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL);
1013 scr->clip_options = makeClipOptionsMenu(scr);
1014 if (scr->clip_options)
1015 wMenuEntrySetCascade(menu, entry, scr->clip_options);
1017 wMenuAddCallback(menu, _("Rename Workspace"), renameCallback, NULL);
1019 wMenuAddCallback(menu, _("(Un)Select Icon"), selectCallback, NULL);
1021 wMenuAddCallback(menu, _("(Un)Select All Icons"), selectIconsCallback,
1022 NULL);
1024 wMenuAddCallback(menu, _("Keep Icon(s)"), keepIconsCallback, NULL);
1026 entry = wMenuAddCallback(menu, _("Move Icon(s) To"), NULL, NULL);
1027 scr->clip_submenu = makeWorkspaceMenu(scr);
1028 if (scr->clip_submenu)
1029 wMenuEntrySetCascade(menu, entry, scr->clip_submenu);
1031 wMenuAddCallback(menu, _("Remove Icon(s)"), removeIconsCallback, NULL);
1033 wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL);
1036 wMenuAddCallback(menu, _("Launch"), launchCallback, NULL);
1038 wMenuAddCallback(menu, _("Unhide Here"), unhideHereCallback, NULL);
1040 wMenuAddCallback(menu, _("(Un)Hide"), hideCallback, NULL);
1042 wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL);
1044 wMenuAddCallback(menu, _("Kill"), killCallback, NULL);
1046 if (type == WM_CLIP)
1047 scr->clip_menu = menu;
1049 return menu;
1053 WDock*
1054 wDockCreate(WScreen *scr, int type)
1056 WDock *dock;
1057 WAppIcon *btn;
1058 int icon_count;
1060 make_keys();
1062 dock = wmalloc(sizeof(WDock));
1063 memset(dock, 0, sizeof(WDock));
1065 if (type == WM_CLIP)
1066 icon_count = CLIP_MAX_ICONS;
1067 else
1068 icon_count = scr->scr_height/wPreferences.icon_size;
1070 dock->icon_array = wmalloc(sizeof(WAppIcon*)*icon_count);
1071 memset(dock->icon_array, 0, sizeof(WAppIcon*)*icon_count);
1073 dock->max_icons = icon_count;
1075 btn = mainIconCreate(scr, type);
1077 btn->dock = dock;
1079 dock->x_pos = btn->x_pos;
1080 dock->y_pos = btn->y_pos;
1081 dock->screen_ptr = scr;
1082 dock->type = type;
1083 dock->icon_count = 1;
1084 dock->on_right_side = 1;
1085 dock->collapsed = 0;
1086 dock->auto_collapse = 0;
1087 dock->auto_collapse_magic = NULL;
1088 dock->attract_icons = 0;
1089 dock->keep_attracted = 0;
1090 dock->lowered = 1;
1091 dock->icon_array[0] = btn;
1092 wRaiseFrame(btn->icon->core);
1093 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
1095 /* create dock menu */
1096 dock->menu = dockMenuCreate(scr, type);
1098 return dock;
1102 void
1103 wDockDestroy(WDock *dock)
1105 int i;
1106 WAppIcon *aicon;
1108 for (i=(dock->type == WM_CLIP) ? 1 : 0; i<dock->max_icons; i++) {
1109 aicon = dock->icon_array[i];
1110 if (aicon) {
1111 int keepit = aicon->running && wApplicationOf(aicon->main_window);
1112 wDockDetach(dock, aicon);
1113 if (keepit) {
1114 PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos);
1115 #ifndef STRICTNS
1116 wLowerFrame(aicon->icon->core);
1117 #endif
1118 XMoveWindow(dpy, aicon->icon->core->window,
1119 aicon->x_pos, aicon->y_pos);
1120 if (!dock->mapped || dock->collapsed)
1121 XMapWindow(dpy, aicon->icon->core->window);
1125 if (wPreferences.auto_arrange_icons)
1126 wArrangeIcons(dock->screen_ptr, True);
1127 free(dock->icon_array);
1128 if (dock->menu && dock->type!=WM_CLIP)
1129 wMenuDestroy(dock->menu, True);
1130 free(dock);
1134 void
1135 wClipIconPaint(WAppIcon *aicon)
1137 WScreen *scr = aicon->icon->core->screen_ptr;
1138 WWorkspace *workspace = scr->workspaces[scr->current_workspace];
1139 GC gc;
1140 Window win = aicon->icon->core->window;
1141 int length, nlength;
1142 char *ws_name, ws_number[10];
1143 int ty, tx;
1145 wIconPaint(aicon->icon);
1147 length = strlen(workspace->name);
1148 ws_name = malloc(length + 1);
1149 sprintf(ws_name, "%s", workspace->name);
1150 sprintf(ws_number, "%i", scr->current_workspace + 1);
1151 nlength = strlen(ws_number);
1153 gc = scr->clip_title_gc;
1155 if (!workspace->clip->collapsed)
1156 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_NORMAL]);
1157 else
1158 XSetForeground(dpy, gc, scr->clip_title_pixel[CLIP_COLLAPSED]);
1160 ty = ICON_SIZE - scr->clip_title_font->height - 3;
1162 tx = CLIP_BUTTON_SIZE*ICON_SIZE/64;
1164 wDrawString(win, scr->clip_title_font, gc, tx,
1165 ty + scr->clip_title_font->y, ws_name, length);
1167 tx = (ICON_SIZE/2 - wTextWidth(scr->clip_title_font->font, ws_number, nlength))/2;
1169 wDrawString(win, scr->clip_title_font, gc, tx,
1170 scr->clip_title_font->y + 2, ws_number, nlength);
1172 free(ws_name);
1174 if (aicon->launching) {
1175 XFillRectangle(dpy, aicon->icon->core->window, scr->stipple_gc,
1176 0, 0, wPreferences.icon_size, wPreferences.icon_size);
1178 paintClipButtons(aicon, aicon->dock->lclip_button_pushed,
1179 aicon->dock->rclip_button_pushed);
1183 static void
1184 clipIconExpose(WObjDescriptor *desc, XEvent *event)
1186 wClipIconPaint(desc->parent);
1190 static void
1191 dockIconPaint(WAppIcon *btn)
1193 if (btn == btn->icon->core->screen_ptr->clip_icon)
1194 wClipIconPaint(btn);
1195 else
1196 wAppIconPaint(btn);
1200 static proplist_t
1201 make_icon_state(WAppIcon *btn)
1203 proplist_t node = NULL;
1204 proplist_t command, autolaunch, name, forced, host, position, buggy;
1205 char buffer[256];
1208 if (btn) {
1209 if (!btn->command)
1210 command = PLMakeString("-");
1211 else
1212 command = PLMakeString(btn->command);
1214 autolaunch = btn->auto_launch ? dYes : dNo;
1216 if (btn->wm_class && btn->wm_instance)
1217 sprintf(buffer, "%s.%s", btn->wm_instance, btn->wm_class);
1218 else if (btn->wm_instance)
1219 sprintf(buffer, "%s", btn->wm_instance);
1220 else if (btn->wm_class)
1221 sprintf(buffer, ".%s", btn->wm_class);
1222 else
1223 sprintf(buffer, ".");
1225 name = PLMakeString(buffer);
1227 forced = btn->forced_dock ? dYes : dNo;
1229 buggy = btn->buggy_app ? dYes: dNo;
1231 if (btn == btn->icon->core->screen_ptr->clip_icon)
1232 sprintf(buffer, "%i,%i", btn->x_pos, btn->y_pos);
1233 else
1234 sprintf(buffer, "%hi,%hi", btn->xindex, btn->yindex);
1235 position = PLMakeString(buffer);
1237 node = PLMakeDictionaryFromEntries(dCommand, command,
1238 dName, name,
1239 dAutoLaunch, autolaunch,
1240 dForced, forced,
1241 dBuggyApplication, buggy,
1242 dPosition, position,
1243 NULL);
1244 PLRelease(command);
1245 PLRelease(name);
1246 PLRelease(position);
1247 #ifdef OFFIX_DND
1248 if (btn->dnd_command) {
1249 command = PLMakeString(btn->dnd_command);
1250 PLInsertDictionaryEntry(node, dDropCommand, command);
1251 PLRelease(command);
1253 #endif /* OFFIX_DND */
1255 if (btn->client_machine && btn->remote_start) {
1256 host = PLMakeString(btn->client_machine);
1257 PLInsertDictionaryEntry(node, dHost, host);
1258 PLRelease(host);
1262 return node;
1266 static proplist_t
1267 dockSaveState(WDock *dock)
1269 int i;
1270 proplist_t icon_info;
1271 proplist_t list=NULL, dock_state=NULL;
1272 proplist_t value;
1273 char buffer[256];
1275 list = PLMakeArrayFromElements(NULL);
1277 for (i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
1278 WAppIcon *btn = dock->icon_array[i];
1280 if (!btn || (btn->attracted && !dock->keep_attracted))
1281 continue;
1283 if ((icon_info = make_icon_state(dock->icon_array[i]))) {
1284 list = PLAppendArrayElement(list, icon_info);
1285 PLRelease(icon_info);
1289 dock_state = PLMakeDictionaryFromEntries(dApplications, list, NULL);
1291 PLRelease(list);
1293 if (dock->type == WM_DOCK) {
1294 sprintf(buffer, "%i,%i", (dock->on_right_side ? -ICON_SIZE : 0),
1295 dock->y_pos);
1296 value = PLMakeString(buffer);
1297 PLInsertDictionaryEntry(dock_state, dPosition, value);
1298 PLRelease(value);
1301 value = (dock->lowered ? dYes : dNo);
1302 PLInsertDictionaryEntry(dock_state, dLowered, value);
1304 if (dock->type == WM_CLIP) {
1305 value = (dock->collapsed ? dYes : dNo);
1306 PLInsertDictionaryEntry(dock_state, dCollapsed, value);
1308 value = (dock->auto_collapse ? dYes : dNo);
1309 PLInsertDictionaryEntry(dock_state, dAutoCollapse, value);
1311 value = (dock->attract_icons ? dYes : dNo);
1312 PLInsertDictionaryEntry(dock_state, dAutoAttractIcons, value);
1314 value = (dock->keep_attracted ? dYes : dNo);
1315 PLInsertDictionaryEntry(dock_state, dKeepAttracted, value);
1318 return dock_state;
1322 void
1323 wDockSaveState(WScreen *scr)
1325 proplist_t dock_state;
1327 dock_state = dockSaveState(scr->dock);
1329 PLInsertDictionaryEntry(scr->session_state, dDock, dock_state);
1331 PLRelease(dock_state);
1335 void
1336 wClipSaveState(WScreen *scr)
1338 proplist_t clip_state;
1340 clip_state = make_icon_state(scr->clip_icon);
1342 PLInsertDictionaryEntry(scr->session_state, dClip, clip_state);
1344 PLRelease(clip_state);
1348 proplist_t
1349 wClipSaveWorkspaceState(WScreen *scr, int workspace)
1351 return dockSaveState(scr->workspaces[workspace]->clip);
1355 static WAppIcon*
1356 restore_icon_state(WScreen *scr, proplist_t info, int type, int index)
1358 WAppIcon *aicon;
1359 char *wclass, *winstance;
1360 proplist_t cmd, value;
1361 char *command;
1364 cmd = PLGetDictionaryEntry(info, dCommand);
1365 if (!cmd || !PLIsString(cmd)) {
1366 return NULL;
1369 /* parse window name */
1370 value = PLGetDictionaryEntry(info, dName);
1371 if (!value)
1372 return NULL;
1374 ParseWindowName(value, &winstance, &wclass, "dock");
1376 if (!winstance && !wclass) {
1377 return NULL;
1380 /* get commands */
1382 command = wstrdup(PLGetString(cmd));
1384 if (!command || strcmp(command, "-")==0) {
1385 if (command)
1386 free(command);
1387 if (wclass)
1388 free(wclass);
1389 if (winstance)
1390 free(winstance);
1392 return NULL;
1395 aicon = wAppIconCreateForDock(scr, command, winstance, wclass,
1396 TILE_NORMAL);
1397 if (wclass)
1398 free(wclass);
1399 if (winstance)
1400 free(winstance);
1402 aicon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1403 if (type == WM_CLIP) {
1404 aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1405 aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1407 aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1408 aicon->icon->core->descriptor.parent = aicon;
1411 #ifdef OFFIX_DND
1412 cmd = PLGetDictionaryEntry(info, dDropCommand);
1413 if (cmd)
1414 aicon->dnd_command = wstrdup(PLGetString(cmd));
1415 #endif
1417 /* check auto launch */
1418 value = PLGetDictionaryEntry(info, dAutoLaunch);
1420 aicon->auto_launch = 0;
1421 if (value) {
1422 if (PLIsString(value)) {
1423 if (strcasecmp(PLGetString(value), "YES")==0)
1424 aicon->auto_launch = 1;
1425 } else {
1426 wwarning(_("bad value in docked icon state info %s"),
1427 PLGetString(dAutoLaunch));
1431 /* check if it wasn't normally docked */
1432 value = PLGetDictionaryEntry(info, dForced);
1434 aicon->forced_dock = 0;
1435 if (value) {
1436 if (PLIsString(value)) {
1437 if (strcasecmp(PLGetString(value), "YES")==0)
1438 aicon->forced_dock = 1;
1439 } else {
1440 wwarning(_("bad value in docked icon state info %s"),
1441 PLGetString(dForced));
1445 /* check if we can rely on the stuff in the app */
1446 value = PLGetDictionaryEntry(info, dBuggyApplication);
1448 aicon->buggy_app = 0;
1449 if (value) {
1450 if (PLIsString(value)) {
1451 if (strcasecmp(PLGetString(value), "YES")==0)
1452 aicon->buggy_app = 1;
1453 } else {
1454 wwarning(_("bad value in docked icon state info %s"),
1455 PLGetString(dBuggyApplication));
1459 /* get position in the dock */
1460 value = PLGetDictionaryEntry(info, dPosition);
1461 if (value && PLIsString(value)) {
1462 if (sscanf(PLGetString(value), "%hi,%hi", &aicon->xindex,
1463 &aicon->yindex)!=2)
1464 wwarning(_("bad value in docked icon state info %s"),
1465 PLGetString(dPosition));
1467 /* check position sanity */
1468 /* incomplete section! */
1469 if (type == WM_DOCK) {
1470 aicon->xindex = 0;
1471 if (aicon->yindex < 0)
1472 wwarning(_("bad value in docked icon position %i,%i"),
1473 aicon->xindex, aicon->yindex);
1475 } else {
1476 aicon->yindex = index;
1477 aicon->xindex = 0;
1480 aicon->running = 0;
1481 aicon->docked = 1;
1483 return aicon;
1487 #define COMPLAIN(key) wwarning(_("bad value in dock state info:%s"), key)
1490 WAppIcon*
1491 wClipRestoreState(WScreen *scr, proplist_t clip_state)
1493 WAppIcon *icon;
1494 proplist_t value;
1497 icon = mainIconCreate(scr, WM_CLIP);
1499 if (!clip_state)
1500 return icon;
1501 else
1502 PLRetain(clip_state);
1504 /* restore position */
1506 value = PLGetDictionaryEntry(clip_state, dPosition);
1508 if (value) {
1509 if (!PLIsString(value))
1510 COMPLAIN("Position");
1511 else {
1512 if (sscanf(PLGetString(value), "%i,%i", &icon->x_pos,
1513 &icon->y_pos)!=2)
1514 COMPLAIN("Position");
1516 /* check position sanity */
1517 if (icon->y_pos < 0)
1518 icon->y_pos = 0;
1519 else if (icon->y_pos > scr->scr_height-ICON_SIZE)
1520 icon->y_pos = scr->scr_height-ICON_SIZE;
1522 if (icon->x_pos < 0)
1523 icon->x_pos = 0;
1524 else if (icon->x_pos > scr->scr_width-ICON_SIZE)
1525 icon->x_pos = scr->scr_width-ICON_SIZE;
1529 #ifdef OFFIX_DND
1530 value = PLGetDictionaryEntry(clip_state, dDropCommand);
1531 if (value && PLIsString(value))
1532 icon->dnd_command = wstrdup(PLGetString(value));
1533 #endif
1535 PLRelease(clip_state);
1537 return icon;
1541 WDock*
1542 wDockRestoreState(WScreen *scr, proplist_t dock_state, int type)
1544 WDock *dock;
1545 proplist_t apps;
1546 proplist_t value;
1547 WAppIcon *aicon, *old_top;
1548 int count, i;
1551 dock = wDockCreate(scr, type);
1553 if (!dock_state)
1554 return dock;
1556 if (dock_state)
1557 PLRetain(dock_state);
1560 /* restore position */
1562 value = PLGetDictionaryEntry(dock_state, dPosition);
1564 if (value) {
1565 if (!PLIsString(value))
1566 COMPLAIN("Position");
1567 else {
1568 if (sscanf(PLGetString(value), "%i,%i", &dock->x_pos,
1569 &dock->y_pos)!=2)
1570 COMPLAIN("Position");
1572 /* check position sanity */
1573 if (dock->y_pos < 0)
1574 dock->y_pos = 0;
1575 else if (dock->y_pos > scr->scr_height-ICON_SIZE)
1576 dock->y_pos = scr->scr_height-ICON_SIZE;
1578 /* This is no more needed. ??? */
1579 if (type == WM_CLIP) {
1580 if (dock->x_pos < 0)
1581 dock->x_pos = 0;
1582 else if (dock->x_pos > scr->scr_width-ICON_SIZE)
1583 dock->x_pos = scr->scr_width-ICON_SIZE;
1585 else {
1586 if (dock->x_pos >= 0) {
1587 dock->x_pos = DOCK_EXTRA_SPACE;
1588 dock->on_right_side = 0;
1589 } else {
1590 dock->x_pos = scr->scr_width - DOCK_EXTRA_SPACE - ICON_SIZE;
1591 dock->on_right_side = 1;
1597 /* restore lowered/raised state */
1599 dock->lowered = 0;
1601 value = PLGetDictionaryEntry(dock_state, dLowered);
1603 if (value) {
1604 if (!PLIsString(value))
1605 COMPLAIN("Lowered");
1606 else {
1607 if (strcasecmp(PLGetString(value), "YES")==0)
1608 dock->lowered = 1;
1613 /* restore collapsed state */
1615 dock->collapsed = 0;
1617 value = PLGetDictionaryEntry(dock_state, dCollapsed);
1619 if (value) {
1620 if (!PLIsString(value))
1621 COMPLAIN("Collapsed");
1622 else {
1623 if (strcasecmp(PLGetString(value), "YES")==0)
1624 dock->collapsed = 1;
1629 /* restore auto-collapsed state */
1631 value = PLGetDictionaryEntry(dock_state, dAutoCollapse);
1633 if (value) {
1634 if (!PLIsString(value))
1635 COMPLAIN("AutoCollapse");
1636 else {
1637 if (strcasecmp(PLGetString(value), "YES")==0) {
1638 dock->auto_collapse = 1;
1639 dock->collapsed = 1;
1645 /* restore attract icons state */
1647 dock->attract_icons = 0;
1649 value = PLGetDictionaryEntry(dock_state, dAutoAttractIcons);
1651 if (value) {
1652 if (!PLIsString(value))
1653 COMPLAIN("AutoAttractIcons");
1654 else {
1655 if (strcasecmp(PLGetString(value), "YES")==0)
1656 dock->attract_icons = 1;
1661 /* restore keep attracted icons state */
1663 dock->keep_attracted = 0;
1665 value = PLGetDictionaryEntry(dock_state, dKeepAttracted);
1667 if (value) {
1668 if (!PLIsString(value))
1669 COMPLAIN("KeepAttracted");
1670 else {
1671 if (strcasecmp(PLGetString(value), "YES")==0)
1672 dock->keep_attracted = 1;
1677 /* application list */
1679 apps = PLGetDictionaryEntry(dock_state, dApplications);
1681 if (!apps) {
1682 goto finish;
1685 count = PLGetNumberOfElements(apps);
1687 if (count==0)
1688 goto finish;
1690 old_top = dock->icon_array[0];
1692 /* dock->icon_count is set to 1 when dock is created.
1693 * Since Clip is already restored, we want to keep it so for clip,
1694 * but for dock we may change the default top tile, so we set it to 0.
1696 if (type == WM_DOCK)
1697 dock->icon_count = 0;
1699 for (i=0; i<count; i++) {
1700 if (dock->icon_count >= dock->max_icons) {
1701 wwarning(_("there are too many icons stored in dock. Ignoring what doesn't fit"));
1702 break;
1705 value = PLGetArrayElement(apps, i);
1706 aicon = restore_icon_state(scr, value, type, dock->icon_count);
1708 dock->icon_array[dock->icon_count] = aicon;
1710 if (aicon) {
1711 aicon->dock = dock;
1712 aicon->x_pos = dock->x_pos + (aicon->xindex*ICON_SIZE);
1713 aicon->y_pos = dock->y_pos + (aicon->yindex*ICON_SIZE);
1715 if (dock->lowered)
1716 ChangeStackingLevel(aicon->icon->core, WMNormalWindowLevel);
1717 else
1718 ChangeStackingLevel(aicon->icon->core, WMDockWindowLevel);
1720 wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos,
1721 0, 0);
1723 if (!dock->collapsed)
1724 XMapWindow(dpy, aicon->icon->core->window);
1725 wRaiseFrame(aicon->icon->core);
1727 dock->icon_count++;
1728 } else if (dock->icon_count==0 && type==WM_DOCK)
1729 dock->icon_count++;
1732 /* if the first icon is not defined, use the default */
1733 if (dock->icon_array[0]==NULL) {
1734 /* update default icon */
1735 old_top->x_pos = dock->x_pos;
1736 old_top->y_pos = dock->y_pos;
1737 if (dock->lowered)
1738 ChangeStackingLevel(old_top->icon->core, WMNormalWindowLevel);
1739 else
1740 ChangeStackingLevel(old_top->icon->core, WMDockWindowLevel);
1741 dock->icon_array[0] = old_top;
1742 XMoveWindow(dpy, old_top->icon->core->window, dock->x_pos, dock->y_pos);
1743 /* we don't need to increment dock->icon_count here because it was
1744 * incremented in the loop above.
1746 } else if (old_top!=dock->icon_array[0]) {
1747 if (old_top == scr->clip_icon)
1748 scr->clip_icon = dock->icon_array[0];
1749 wAppIconDestroy(old_top);
1752 finish:
1753 if (dock_state)
1754 PLRelease(dock_state);
1756 return dock;
1761 void
1762 wDockLaunchWithState(WDock *dock, WAppIcon *btn, WSavedState *state)
1764 if (btn && btn->command && !btn->running && !btn->launching) {
1766 btn->drop_launch = 0;
1768 btn->pid = execCommand(btn, btn->command, state);
1770 if (btn->pid>0) {
1771 if (!btn->forced_dock && !btn->buggy_app) {
1772 btn->launching = 1;
1773 dockIconPaint(btn);
1776 } else {
1777 free(state);
1782 void
1783 wDockDoAutoLaunch(WDock *dock, int workspace)
1785 WAppIcon *btn;
1786 WSavedState *state;
1787 int i;
1789 for (i=0; i < dock->max_icons; i++) {
1790 btn = dock->icon_array[i];
1791 if (!btn || !btn->auto_launch)
1792 continue;
1794 state = wmalloc(sizeof(WSavedState));
1795 memset(state, 0, sizeof(WSavedState));
1796 state->workspace = workspace;
1797 /* TODO: this is klugy and is very difficult to understand
1798 * what's going on. Try to clean up */
1799 wDockLaunchWithState(dock, btn, state);
1803 #ifdef REDUCE_APPICONS
1804 void
1805 wDockSimulateLaunch(WDock *dock, WAppIcon *btn)
1807 if ((btn == NULL) || (dock == NULL))
1808 return;
1810 if (!btn->running) {
1811 if ((btn->icon->owner == NULL) && (btn->applist))
1812 btn->icon->owner = btn->applist->wapp->main_window_desc;
1813 if (!btn->forced_dock)
1814 btn->launching = 1;
1815 dockIconPaint(btn);
1816 wusleep(5000);
1819 #endif
1821 #ifdef OFFIX_DND
1822 static WDock*
1823 findDock(WScreen *scr, XEvent *event, int *icon_pos)
1825 WDock *dock;
1826 int i;
1828 *icon_pos = -1;
1829 if ((dock = scr->dock)!=NULL) {
1830 for (i=0; i<dock->max_icons; i++) {
1831 if (dock->icon_array[i]
1832 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
1833 *icon_pos = i;
1834 break;
1838 if (*icon_pos<0 && (dock = scr->workspaces[scr->current_workspace]->clip)!=NULL) {
1839 for (i=0; i<dock->max_icons; i++) {
1840 if (dock->icon_array[i]
1841 && dock->icon_array[i]->icon->core->window==event->xclient.window) {
1842 *icon_pos = i;
1843 break;
1847 if(*icon_pos>=0)
1848 return dock;
1849 return NULL;
1854 wDockReceiveDNDDrop(WScreen *scr, XEvent *event)
1856 WDock *dock;
1857 WAppIcon *btn;
1858 int icon_pos;
1860 dock = findDock(scr, event, &icon_pos);
1861 if (!dock)
1862 return False;
1865 * Return True if the drop was on an application icon window.
1866 * In this case, let the ClientMessage handler redirect the
1867 * message to the app.
1869 if (dock->icon_array[icon_pos]->icon->icon_win!=None)
1870 return True;
1872 if (dock->icon_array[icon_pos]->dnd_command!=NULL) {
1873 scr->flags.dnd_data_convertion_status = 0;
1875 btn = dock->icon_array[icon_pos];
1877 if (!btn->forced_dock) {
1878 btn->relaunching = btn->running;
1879 btn->running = 1;
1881 if (btn->wm_instance || btn->wm_class) {
1882 WWindowAttributes attr;
1883 memset(&attr, 0, sizeof(WWindowAttributes));
1884 wDefaultFillAttributes(btn->icon->core->screen_ptr,
1885 btn->wm_instance,
1886 btn->wm_class, &attr, True);
1888 if (!attr.no_appicon)
1889 btn->launching = 1;
1890 else
1891 btn->running = 0;
1894 btn->drop_launch = 1;
1895 scr->last_dock = dock;
1896 btn->pid = execCommand(btn, btn->dnd_command, NULL);
1897 if (btn->pid>0) {
1898 dockIconPaint(btn);
1899 } else {
1900 btn->launching = 0;
1901 if (!btn->relaunching) {
1902 btn->running = 0;
1906 return False;
1908 #endif /* OFFIX_DND */
1912 Bool
1913 wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y)
1915 WWindow *wwin;
1916 char **argv;
1917 int argc;
1918 int index;
1920 wwin = icon->icon->owner;
1921 if (icon->command==NULL) {
1922 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
1924 icon->command = FlattenStringList(argv, argc);
1925 XFreeStringList(argv);
1926 } else {
1927 char *command=NULL;
1929 /* icon->forced_dock = 1;*/
1930 if (!icon->attracted || dock->type!=WM_CLIP || dock->keep_attracted) {
1931 if (wInputDialog(dock->screen_ptr, _("Dock Icon"),
1932 _("Type the command used to launch the application"),
1933 &command)==WDB_OK) {
1934 if (command && (command[0]==0 ||
1935 (command[0]=='-' && command[1]==0))) {
1936 free(command);
1937 command = NULL;
1939 icon->command = command;
1940 } else {
1941 if (command)
1942 free(command);
1943 /* If the target is the dock, reject the icon. If
1944 * the target is the clip, make it an attracted icon
1946 if (dock->type==WM_CLIP) {
1947 icon->attracted = 1;
1948 if (!icon->icon->shadowed) {
1949 icon->icon->shadowed = 1;
1950 icon->icon->force_paint = 1;
1952 } else
1953 return False;
1960 for (index=1; index<dock->max_icons; index++)
1961 if (dock->icon_array[index] == NULL)
1962 break;
1963 /* if (index == dock->max_icons)
1964 return; */
1966 assert(index < dock->max_icons);
1968 dock->icon_array[index] = icon;
1969 icon->yindex = y;
1970 icon->xindex = x;
1972 icon->x_pos = dock->x_pos + x*ICON_SIZE;
1973 icon->y_pos = dock->y_pos + y*ICON_SIZE;
1975 dock->icon_count++;
1977 icon->running = 1;
1978 icon->launching = 0;
1979 icon->docked = 1;
1980 icon->dock = dock;
1981 icon->icon->core->descriptor.handle_mousedown = iconMouseDown;
1982 if (dock->type == WM_CLIP) {
1983 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
1984 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
1986 icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON;
1987 icon->icon->core->descriptor.parent = icon;
1989 MoveInStackListUnder(dock->icon_array[index-1]->icon->core,
1990 icon->icon->core);
1991 wAppIconMove(icon, icon->x_pos, icon->y_pos);
1992 wAppIconPaint(icon);
1994 if (wPreferences.auto_arrange_icons)
1995 wArrangeIcons(dock->screen_ptr, True);
1997 return True;
2001 void
2002 reattachIcon(WDock *dock, WAppIcon *icon, int x, int y)
2004 int index;
2006 for(index=1; index<dock->max_icons; index++) {
2007 if(dock->icon_array[index] == icon)
2008 break;
2010 assert(index < dock->max_icons);
2012 icon->yindex = y;
2013 icon->xindex = x;
2015 icon->x_pos = dock->x_pos + x*ICON_SIZE;
2016 icon->y_pos = dock->y_pos + y*ICON_SIZE;
2020 Bool
2021 moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y)
2023 WWindow *wwin;
2024 char **argv;
2025 int argc;
2026 int index;
2028 if (dest == NULL)
2029 return False;
2031 wwin = icon->icon->owner;
2034 * For the moment we can't do this if we move icons in Clip from one
2035 * workspace to other, because if we move two or more icons without
2036 * command, the dialog box will not be able to tell us to which of the
2037 * moved icons it applies. -Dan
2039 if ((dest->type==WM_DOCK /*|| dest->keep_attracted*/) && icon->command==NULL) {
2040 if (XGetCommand(dpy, wwin->client_win, &argv, &argc) && argc>0) {
2042 icon->command = FlattenStringList(argv, argc);
2043 XFreeStringList(argv);
2044 } else {
2045 char *command=NULL;
2047 /* icon->forced_dock = 1;*/
2048 if (wInputDialog(src->screen_ptr, _("Dock Icon"),
2049 _("Type the command used to launch the application"),
2050 &command)==WDB_OK) {
2051 if (command && (command[0]==0 ||
2052 (command[0]=='-' && command[1]==0))) {
2053 free(command);
2054 command = NULL;
2056 icon->command = command;
2057 } else {
2058 if (command)
2059 free(command);
2060 return False;
2065 for(index=1; index<src->max_icons; index++) {
2066 if(src->icon_array[index] == icon)
2067 break;
2069 assert(index < src->max_icons);
2071 src->icon_array[index] = NULL;
2072 src->icon_count--;
2074 for(index=1; index<dest->max_icons; index++) {
2075 if(dest->icon_array[index] == NULL)
2076 break;
2078 /* if (index == dest->max_icons)
2079 return; */
2081 assert(index < dest->max_icons);
2083 dest->icon_array[index] = icon;
2084 icon->dock = dest;
2086 /* deselect the icon */
2087 if (icon->icon->selected)
2088 wIconSelect(icon->icon);
2090 if (dest->type == WM_DOCK) {
2091 icon->icon->core->descriptor.handle_enternotify = NULL;
2092 icon->icon->core->descriptor.handle_leavenotify = NULL;
2093 } else {
2094 icon->icon->core->descriptor.handle_enternotify = clipEnterNotify;
2095 icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify;
2098 /* set it to be kept when moving to dock, or to a clip that keep the
2099 * attracted icons.
2100 * Unless the icon does not have a command set
2102 if (icon->command && (dest->type==WM_DOCK || dest->keep_attracted)) {
2103 icon->attracted = 0;
2104 if (icon->icon->shadowed) {
2105 icon->icon->shadowed = 0;
2106 icon->icon->force_paint = 1;
2110 if (src->auto_collapse)
2111 clipLeave(src);
2113 icon->yindex = y;
2114 icon->xindex = x;
2116 icon->x_pos = dest->x_pos + x*ICON_SIZE;
2117 icon->y_pos = dest->y_pos + y*ICON_SIZE;
2119 dest->icon_count++;
2121 MoveInStackListUnder(dest->icon_array[index-1]->icon->core,
2122 icon->icon->core);
2123 wAppIconPaint(icon);
2125 return True;
2128 void
2129 wDockDetach(WDock *dock, WAppIcon *icon)
2131 int index;
2133 /* make the settings panel be closed */
2134 if (icon->panel) {
2135 DestroyDockAppSettingsPanel(icon->panel);
2138 icon->docked = 0;
2139 icon->dock = NULL;
2140 icon->attracted = 0;
2141 if (icon->icon->shadowed) {
2142 icon->icon->shadowed = 0;
2143 icon->icon->force_paint = 1;
2146 /* deselect the icon */
2147 if (icon->icon->selected)
2148 wIconSelect(icon->icon);
2150 if (icon->command) {
2151 free(icon->command);
2152 icon->command = NULL;
2154 #ifdef OFFIX_DND
2155 if (icon->dnd_command) {
2156 free(icon->dnd_command);
2157 icon->dnd_command = NULL;
2159 #endif
2161 for (index=1; index<dock->max_icons; index++)
2162 if (dock->icon_array[index] == icon)
2163 break;
2164 assert(index < dock->max_icons);
2165 dock->icon_array[index] = NULL;
2166 icon->yindex = -1;
2167 icon->xindex = -1;
2168 dock->icon_count--;
2170 /* if the dock is not attached to an application or
2171 * the the application did not set the approriate hints yet,
2172 * destroy the icon */
2173 #ifdef REDUCE_APPICONS
2174 if ((icon->num_apps == 0) && (!icon->running || !wApplicationOf(icon->main_window)) )
2175 #else
2176 if (!icon->running || !wApplicationOf(icon->main_window))
2177 #endif
2178 wAppIconDestroy(icon);
2179 else {
2180 icon->icon->core->descriptor.handle_mousedown = appIconMouseDown;
2181 icon->icon->core->descriptor.handle_enternotify = NULL;
2182 icon->icon->core->descriptor.handle_leavenotify = NULL;
2183 icon->icon->core->descriptor.parent_type = WCLASS_APPICON;
2184 icon->icon->core->descriptor.parent = icon;
2185 ChangeStackingLevel(icon->icon->core, WMNormalWindowLevel);
2186 wAppIconPaint(icon);
2187 if (wPreferences.auto_arrange_icons) {
2188 wArrangeIcons(dock->screen_ptr, True);
2191 if (dock->auto_collapse)
2192 clipLeave(dock);
2197 * returns the closest Dock slot index for the passed
2198 * coordinates.
2200 * Returns False if icon can't be docked.
2203 wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y,
2204 int *ret_x, int *ret_y, int redocking)
2206 WScreen *scr = dock->screen_ptr;
2207 int dx, dy;
2208 int ex_x, ex_y;
2209 int i, offset = ICON_SIZE/2;
2210 int only_down = (dock->type == WM_DOCK);
2211 WAppIcon *aicon = NULL;
2212 WAppIcon *nicon = NULL;
2214 dx = dock->x_pos;
2215 dy = dock->y_pos;
2217 /* if the dock is full */
2218 if (!redocking &&
2219 (dock->icon_count >= dock->max_icons)) {
2220 return False;
2223 /* exact position */
2224 if (req_y < dy)
2225 ex_y = (req_y - offset - dy)/ICON_SIZE;
2226 else
2227 ex_y = (req_y + offset - dy)/ICON_SIZE;
2229 if (req_x < dx)
2230 ex_x = (req_x - offset - dx)/ICON_SIZE;
2231 else
2232 ex_x = (req_x + offset - dx)/ICON_SIZE;
2234 if ((ex_y < 1 || ex_x != 0) && only_down)
2235 return False;
2237 if (dock->x_pos + ex_x*ICON_SIZE < -ICON_SIZE+2 ||
2238 dock->x_pos + ex_x*ICON_SIZE > scr->scr_width-1 ||
2239 dock->y_pos + ex_y*ICON_SIZE < -ICON_SIZE+2 ||
2240 dock->y_pos + ex_y*ICON_SIZE > scr->scr_height-1)
2241 return False;
2243 for (i=0; i<dock->max_icons; i++) {
2244 nicon = dock->icon_array[i];
2245 if (nicon && nicon->xindex == ex_x && nicon->yindex == ex_y) {
2246 aicon = nicon;
2247 break;
2251 if (!only_down) {
2252 int neighbours = 0;
2253 for (i=0; i<dock->max_icons; i++) {
2254 nicon = dock->icon_array[i];
2255 if (nicon && nicon != icon && /* Icon can't be it's own neighbour */
2256 (abs(nicon->xindex - ex_x) <= CLIP_ATTACH_VICINITY &&
2257 abs(nicon->yindex - ex_y) <= CLIP_ATTACH_VICINITY)) {
2258 neighbours = 1;
2259 break;
2262 if ((!redocking && neighbours && !aicon) ||
2263 (redocking && neighbours && (aicon == icon || !aicon))) {
2264 *ret_x = ex_x;
2265 *ret_y = ex_y;
2266 return True;
2269 else {
2270 if ((!redocking && !aicon) ||
2271 (redocking && (aicon==icon || !aicon))) {
2272 *ret_x = 0;
2273 *ret_y = ex_y;
2274 return True;
2278 return False;
2281 #define MIN(x, y) ((x) > (y) ? (y) : (x))
2282 #define MAX(x, y) ((x) < (y) ? (y) : (x))
2284 #define ON_SCREEN(x, y, sx, ex, sy, ey) \
2285 ((((x)+ICON_SIZE/2) >= (sx)) && (((y)+ICON_SIZE/2) >= (sy)) && \
2286 (((x) + (ICON_SIZE/2)) <= (ex)) && (((y) + (ICON_SIZE/2)) <= ey))
2290 * returns true if it can find a free slot in the dock,
2291 * in which case it changes x_pos and y_pos accordingly.
2292 * Else returns false.
2295 wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos)
2297 WScreen *scr = dock->screen_ptr;
2298 WAppIcon *btn;
2299 #if 1
2300 unsigned char *slot_map;
2301 int mwidth;
2302 int r;
2303 int x, y;
2304 int i, done = False;
2305 int corner;
2306 #else
2307 int n = MAX(dock->max_icons, MAX(scr->scr_width, scr->scr_height)/ICON_SIZE + 1);
2308 unsigned char *north, *south, *east, *west;
2309 int i, j, done=False;
2310 #endif
2311 int sx=0, sy=0, ex=scr->scr_width, ey=scr->scr_height;
2313 /* if the dock is full */
2314 if (dock->icon_count >= dock->max_icons) {
2315 return False;
2318 if (!wPreferences.flags.nodock && scr->dock) {
2319 if (scr->dock->on_right_side)
2320 ex -= ICON_SIZE + DOCK_EXTRA_SPACE;
2321 else
2322 sx += ICON_SIZE + DOCK_EXTRA_SPACE;
2325 if (ex < dock->x_pos)
2326 ex = dock->x_pos;
2327 if (sx > dock->x_pos+ICON_SIZE)
2328 sx = dock->x_pos+ICON_SIZE;
2329 #if 1
2330 #define C_NONE 0
2331 #define C_NW 1
2332 #define C_NE 2
2333 #define C_SW 3
2334 #define C_SE 4
2336 /* check if clip is in a corner */
2337 if (dock->type==WM_CLIP) {
2338 if (dock->x_pos < 1 && dock->y_pos < 1)
2339 corner = C_NE;
2340 else if (dock->x_pos < 1 && dock->y_pos >= (ey-ICON_SIZE))
2341 corner = C_SE;
2342 else if (dock->x_pos >= (ex-ICON_SIZE)&& dock->y_pos >= (ey-ICON_SIZE))
2343 corner = C_SW;
2344 else if (dock->x_pos >= (ex-ICON_SIZE) && dock->y_pos < 1)
2345 corner = C_NW;
2346 else
2347 corner = C_NONE;
2348 } else
2349 corner = C_NONE;
2351 /* This one checks for diagonal positions, uses less memory
2352 * and CPU. -Alfredo */
2354 /* If the clip is in the corner, use only slots that are in the border
2355 * of the screen */
2356 if (corner!=C_NONE) {
2357 char *hmap, *vmap;
2358 int hcount, vcount;
2360 hcount = MIN(dock->max_icons, scr->scr_width/ICON_SIZE);
2361 vcount = MIN(dock->max_icons, scr->scr_height/ICON_SIZE);
2362 hmap = wmalloc(hcount+1);
2363 memset(hmap, 0, hcount+1);
2364 vmap = wmalloc(vcount+1);
2365 memset(vmap, 0, vcount+1);
2367 /* mark used positions */
2368 switch (corner) {
2369 case C_NE:
2370 for (i=0; i<dock->max_icons; i++) {
2371 btn = dock->icon_array[i];
2372 if (!btn)
2373 continue;
2375 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2376 vmap[btn->yindex] = 1;
2377 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2378 hmap[btn->xindex] = 1;
2380 case C_NW:
2381 for (i=0; i<dock->max_icons; i++) {
2382 btn = dock->icon_array[i];
2383 if (!btn)
2384 continue;
2386 if (btn->xindex==0 && btn->yindex > 0 && btn->yindex < vcount)
2387 vmap[btn->yindex] = 1;
2388 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2389 hmap[-btn->xindex] = 1;
2391 case C_SE:
2392 for (i=0; i<dock->max_icons; i++) {
2393 btn = dock->icon_array[i];
2394 if (!btn)
2395 continue;
2397 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2398 vmap[-btn->yindex] = 1;
2399 else if (btn->yindex==0 && btn->xindex>0 && btn->xindex<hcount)
2400 hmap[btn->xindex] = 1;
2402 case C_SW:
2403 default:
2404 for (i=0; i<dock->max_icons; i++) {
2405 btn = dock->icon_array[i];
2406 if (!btn)
2407 continue;
2409 if (btn->xindex==0 && btn->yindex < 0 && btn->yindex > -vcount)
2410 vmap[-btn->yindex] = 1;
2411 else if (btn->yindex==0 && btn->xindex<0 &&btn->xindex>-hcount)
2412 hmap[-btn->xindex] = 1;
2415 x=0; y=0;
2416 done = 0;
2417 /* search a vacant slot */
2418 for (i=1; i<MAX(vcount, hcount); i++) {
2419 if (i < vcount && vmap[i]==0) {
2420 /* found a slot */
2421 x = 0;
2422 y = i;
2423 done = 1;
2424 break;
2425 } else if (i < hcount && hmap[i]==0) {
2426 /* found a slot */
2427 x = i;
2428 y = 0;
2429 done = 1;
2430 break;
2433 free(vmap);
2434 free(hmap);
2435 /* If found a slot, translate and return */
2436 if (done) {
2437 if (corner==C_NW || corner==C_NE) {
2438 *y_pos = y;
2439 } else {
2440 *y_pos = -y;
2442 if (corner==C_NE || corner==C_SE) {
2443 *x_pos = x;
2444 } else {
2445 *x_pos = -x;
2447 return True;
2449 /* else, try to find a slot somewhere else */
2452 /* a map of mwidth x mwidth would be enough if we allowed icons to be
2453 * placed outside of screen */
2454 mwidth = (int)ceil(sqrt(dock->max_icons));
2456 /* In the worst case (the clip is in the corner of the screen),
2457 * the amount of icons that fit in the clip is smaller.
2458 * Double the map to get a safe value.
2460 mwidth += mwidth;
2462 r = (mwidth-1)/2;
2464 slot_map = wmalloc(mwidth*mwidth);
2465 memset(slot_map, 0, mwidth*mwidth);
2467 #define XY2OFS(x,y) (MAX(abs(x),abs(y)) > r) ? 0 : (((y)+r)*(mwidth)+(x)+r)
2469 /* mark used slots in the map. If the slot falls outside the map
2470 * (for example, when all icons are placed in line), ignore them. */
2471 for (i=0; i<dock->max_icons; i++) {
2472 btn = dock->icon_array[i];
2473 if (btn)
2474 slot_map[XY2OFS(btn->xindex, btn->yindex)] = 1;
2476 /* Find closest slot from the center that is free by scanning the
2477 * map from the center to outward in circular passes.
2478 * This will not result in a neat layout, but will be optimal
2479 * in the sense that there will not be holes left.
2481 done = 0;
2482 for (i = 1; i <= r && !done; i++) {
2483 int tx, ty;
2485 /* top and bottom parts of the ring */
2486 for (x = -i; x <= i && !done; x++) {
2487 tx = dock->x_pos + x*ICON_SIZE;
2488 y = -i;
2489 ty = dock->y_pos + y*ICON_SIZE;
2490 if (slot_map[XY2OFS(x,y)]==0
2491 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2492 *x_pos = x;
2493 *y_pos = y;
2494 done = 1;
2495 break;
2497 y = i;
2498 ty = dock->y_pos + y*ICON_SIZE;
2499 if (slot_map[XY2OFS(x,y)]==0
2500 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2501 *x_pos = x;
2502 *y_pos = y;
2503 done = 1;
2504 break;
2507 /* left and right parts of the ring */
2508 for (y = -i+1; y <= i-1; y++) {
2509 ty = dock->y_pos + y*ICON_SIZE;
2510 x = -i;
2511 tx = dock->x_pos + x*ICON_SIZE;
2512 if (slot_map[XY2OFS(x,y)]==0
2513 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2514 *x_pos = x;
2515 *y_pos = y;
2516 done = 1;
2517 break;
2519 x = i;
2520 tx = dock->x_pos + x*ICON_SIZE;
2521 if (slot_map[XY2OFS(x,y)]==0
2522 && ON_SCREEN(tx, ty, sx, ex, sy, ey)) {
2523 *x_pos = x;
2524 *y_pos = y;
2525 done = 1;
2526 break;
2530 free(slot_map);
2531 #undef XY2OFS
2532 return done;
2533 #else
2534 north = (unsigned char*) wmalloc(sizeof(unsigned char) * n);
2535 south = (unsigned char*) wmalloc(sizeof(unsigned char) * n);
2536 east = (unsigned char*) wmalloc(sizeof(unsigned char) * n);
2537 west = (unsigned char*) wmalloc(sizeof(unsigned char) * n);
2539 /* We allow 3 passes to find a free position. This can be increased,
2540 * but usually is more than enough for a normal (<25 icons/clip)
2541 * environment. 25 free slots are found in the first pass for a
2542 * 1024x768 screen size.
2545 for (i=0; i<3 && !done; i++) {
2546 memset(north, 0, sizeof(unsigned char) * n);
2547 memset(south, 0, sizeof(unsigned char) * n);
2548 memset(east, 0, sizeof(unsigned char) * n);
2549 memset(west, 0, sizeof(unsigned char) * n);
2550 for (j=0; j<dock->max_icons; j++) {
2551 btn = dock->icon_array[j];
2552 if (!btn)
2553 continue;
2555 if (btn->yindex == i && btn->xindex >= 0)
2556 east[btn->xindex] = 1;
2557 else if (btn->yindex == -i && btn->xindex < 0)
2558 west[-(btn->xindex)] = 1;
2559 else if (btn->xindex == -i && btn->yindex >= 0)
2560 south[btn->yindex] = 1;
2561 else if (btn->xindex == i && btn->yindex < 0)
2562 north[-(btn->yindex)] = 1;
2564 for (j=1; j<n; j++) {
2565 int x, y;
2567 if (east[j]==0) {
2568 x = dock->x_pos + j*ICON_SIZE;
2569 y = dock->y_pos + i*ICON_SIZE;
2570 if (ON_SCREEN(x, y, sx, ex, sy, ey)) {
2571 *x_pos = j;
2572 *y_pos = i;
2573 done = True;
2574 break;
2577 if (west[j]==0) {
2578 x = dock->x_pos + (-j)*ICON_SIZE;
2579 y = dock->y_pos + (-i)*ICON_SIZE;
2580 if (ON_SCREEN(x, y, sx, ex, sy, ey)) {
2581 *x_pos = -j;
2582 *y_pos = -i;
2583 done = True;
2584 break;
2587 if (south[j]==0) {
2588 x = dock->x_pos + (-i)*ICON_SIZE;
2589 y = dock->y_pos + (j)*ICON_SIZE;
2590 if (ON_SCREEN(x, y, sx, ex, sy, ey)) {
2591 *x_pos = -i;
2592 *y_pos = j;
2593 done = True;
2594 break;
2597 if (north[j]==0) {
2598 x = dock->x_pos + (i)*ICON_SIZE;
2599 y = dock->y_pos + (-j)*ICON_SIZE;
2600 if (ON_SCREEN(x, y, sx, ex, sy, ey)) {
2601 *x_pos = i;
2602 *y_pos = -j;
2603 done = True;
2604 break;
2610 free(north);
2611 free(south);
2612 free(east);
2613 free(west);
2615 return done; /* If done is True this means that we found a free slot */
2616 #endif
2620 static void
2621 moveDock(WDock *dock, int new_x, int new_y)
2623 WAppIcon *btn;
2624 int i;
2626 dock->x_pos = new_x;
2627 dock->y_pos = new_y;
2628 for (i=0; i<dock->max_icons; i++) {
2629 btn = dock->icon_array[i];
2630 if (btn) {
2631 btn->x_pos = new_x + btn->xindex*ICON_SIZE;
2632 btn->y_pos = new_y + btn->yindex*ICON_SIZE;
2633 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2639 static void
2640 swapDock(WDock *dock)
2642 WScreen *scr = dock->screen_ptr;
2643 WAppIcon *btn;
2644 int x, i;
2647 if (dock->on_right_side) {
2648 x = dock->x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE;
2649 } else {
2650 x = dock->x_pos = DOCK_EXTRA_SPACE;
2653 for (i=0; i<dock->max_icons; i++) {
2654 btn = dock->icon_array[i];
2655 if (btn) {
2656 btn->x_pos = x;
2657 XMoveWindow(dpy, btn->icon->core->window, btn->x_pos, btn->y_pos);
2663 static pid_t
2664 execCommand(WAppIcon *btn, char *command, WSavedState *state)
2666 WScreen *scr = btn->icon->core->screen_ptr;
2667 pid_t pid;
2668 char **argv;
2669 int argc;
2670 char *cmdline;
2672 cmdline = ExpandOptions(scr, command);
2674 if (scr->flags.dnd_data_convertion_status || !cmdline) {
2675 if (cmdline)
2676 free(cmdline);
2677 if (state)
2678 free(state);
2679 return 0;
2682 ParseCommand(cmdline, &argv, &argc);
2684 if (argv==NULL) {
2685 if (cmdline)
2686 free(cmdline);
2687 if (state)
2688 free(state);
2689 return 0;
2692 if ((pid=fork())==0) {
2693 char **args;
2694 int i;
2696 SetupEnvironment(scr);
2698 close(ConnectionNumber(dpy));
2700 #ifdef HAVE_SETPGID
2701 setpgid(0, 0);
2702 #endif
2704 args = malloc(sizeof(char*)*(argc+1));
2705 if (!args)
2706 exit(111);
2707 for (i=0; i<argc; i++) {
2708 args[i] = argv[i];
2710 args[argc] = NULL;
2711 execvp(argv[0], args);
2712 exit(111);
2714 while (argc > 0)
2715 free(argv[--argc]);
2716 free(argv);
2718 if (pid > 0) {
2719 if (!state) {
2720 state = wmalloc(sizeof(WSavedState));
2721 memset(state, 0, sizeof(WSavedState));
2722 state->hidden = -1;
2723 state->miniaturized = -1;
2724 state->shaded = -1;
2725 if (btn->dock == scr->dock)
2726 state->workspace = -1;
2727 else
2728 state->workspace = scr->current_workspace;
2730 wAddWindowSavedState(btn->wm_instance, btn->wm_class,
2731 cmdline, pid, state);
2732 wAddDeathHandler(pid, (WDeathHandler*)trackDeadProcess,
2733 btn->dock);
2734 } else if (state) {
2735 free(state);
2737 free(cmdline);
2738 return pid;
2742 void
2743 wDockHideIcons(WDock *dock)
2745 int i;
2746 WAppIcon *btn;
2748 if (dock==NULL)
2749 return;
2751 btn = dock->icon_array[0];
2753 for (i=1; i<dock->max_icons; i++) {
2754 if (dock->icon_array[i])
2755 XUnmapWindow(dpy, dock->icon_array[i]->icon->core->window);
2757 dock->mapped = 0;
2759 dockIconPaint(btn);
2763 void
2764 wDockShowIcons(WDock *dock)
2766 int i, newlevel;
2767 WAppIcon *btn;
2769 if (dock==NULL)
2770 return;
2772 btn = dock->icon_array[0];
2773 moveDock(dock, btn->x_pos, btn->y_pos);
2775 newlevel = dock->lowered ? WMNormalWindowLevel : WMDockWindowLevel;
2776 ChangeStackingLevel(btn->icon->core, newlevel);
2778 for (i=1; i<dock->max_icons; i++) {
2779 if (dock->icon_array[i]) {
2780 MoveInStackListAbove(dock->icon_array[i]->icon->core,
2781 btn->icon->core);
2782 break;
2786 if (!dock->collapsed) {
2787 for (i=1; i<dock->max_icons; i++) {
2788 if (dock->icon_array[i]) {
2789 XMapWindow(dpy, dock->icon_array[i]->icon->core->window);
2793 dock->mapped = 1;
2795 dockIconPaint(btn);
2799 void
2800 wDockLower(WDock *dock)
2802 int i;
2804 for (i=0; i<dock->max_icons; i++) {
2805 if (dock->icon_array[i])
2806 wLowerFrame(dock->icon_array[i]->icon->core);
2811 void
2812 wDockRaise(WDock *dock)
2814 int i;
2816 for (i=dock->max_icons-1; i>=0; i--) {
2817 if (dock->icon_array[i])
2818 wRaiseFrame(dock->icon_array[i]->icon->core);
2823 void
2824 wDockRaiseLower(WDock *dock)
2826 if (!dock->icon_array[0]->icon->core->stacking->above
2827 ||(dock->icon_array[0]->icon->core->stacking->window_level
2828 !=dock->icon_array[0]->icon->core->stacking->above->stacking->window_level))
2829 wDockLower(dock);
2830 else
2831 wDockRaise(dock);
2835 void
2836 wDockFinishLaunch(WDock *dock, WAppIcon *icon)
2838 icon->launching = 0;
2839 icon->relaunching = 0;
2840 dockIconPaint(icon);
2844 WAppIcon*
2845 wDockFindIconFor(WDock *dock, Window window)
2847 WAppIcon *icon;
2848 int i;
2850 for (i=0; i<dock->max_icons; i++) {
2851 icon = dock->icon_array[i];
2852 if (icon && icon->main_window == window)
2853 return icon;
2855 return NULL;
2859 void
2860 wDockTrackWindowLaunch(WDock *dock, Window window)
2862 WAppIcon *icon;
2863 #ifdef REDUCE_APPICONS
2864 WAppIconAppList *tapplist;
2865 #endif
2866 char *wm_class, *wm_instance;
2867 int i;
2870 if (!PropGetWMClass(window, &wm_class, &wm_instance) ||
2871 (!wm_class && !wm_instance))
2872 return;
2874 for (i=0; i<dock->max_icons; i++) {
2875 icon = dock->icon_array[i];
2876 if (!icon)
2877 continue;
2879 /* kluge. If this does not exist, some windows attach themselves
2880 * to more than one icon. Find out why */
2881 if (icon->main_window == window) {
2882 break;
2884 if ((icon->wm_instance || icon->wm_class)
2885 && (icon->launching
2886 || (dock->screen_ptr->flags.startup && !icon->running))) {
2888 if (icon->wm_instance && wm_instance &&
2889 strcmp(icon->wm_instance, wm_instance)!=0) {
2890 continue;
2892 if (icon->wm_class && wm_class &&
2893 strcmp(icon->wm_class, wm_class)!=0) {
2894 continue;
2897 if (!icon->relaunching) {
2898 WApplication *wapp;
2900 /* Possibly an application that was docked with dockit,
2901 * but the user did not update WMState to indicate that
2902 * it was docked by force */
2903 wapp = wApplicationOf(window);
2904 if (!wapp) {
2905 icon->forced_dock = 1;
2906 icon->running = 0;
2908 if (!icon->forced_dock)
2909 icon->main_window = window;
2910 #ifdef REDUCE_APPICONS
2911 tapplist = wmalloc(sizeof(WAppIconAppList));
2912 memset(tapplist, 0, sizeof(WAppIconAppList));
2913 tapplist->next = icon->applist;
2914 if (icon->applist)
2915 icon->applist->prev = tapplist;
2916 icon->applist = tapplist;
2917 tapplist->wapp = wApplicationOf(window);
2918 icon->num_apps++;
2919 #endif
2921 wDockFinishLaunch(dock, icon);
2922 break;
2926 if (wm_class)
2927 XFree(wm_class);
2928 if (wm_instance)
2929 XFree(wm_instance);
2934 void
2935 wClipUpdateForWorkspaceChange(WScreen *scr, int workspace)
2937 if (!wPreferences.flags.noclip) {
2938 scr->clip_icon->dock = scr->workspaces[workspace]->clip;
2939 if (scr->current_workspace != workspace) {
2940 WDock *old_clip = scr->workspaces[scr->current_workspace]->clip;
2942 wDockHideIcons(old_clip);
2943 if (old_clip->auto_collapse && !old_clip->collapsed)
2944 old_clip->collapsed = 1;
2945 wDockShowIcons(scr->workspaces[workspace]->clip);
2947 if (scr->flags.clip_balloon_mapped)
2948 showClipBalloon(scr->clip_icon->dock, workspace);
2954 static void
2955 trackDeadProcess(pid_t pid, unsigned char status, WDock *dock)
2957 WAppIcon *icon;
2958 int i;
2960 for (i=0; i<dock->max_icons; i++) {
2961 icon = dock->icon_array[i];
2962 if (!icon)
2963 continue;
2965 if (icon->launching && icon->pid == pid) {
2966 if (!icon->relaunching) {
2967 icon->running = 0;
2968 icon->main_window = None;
2970 wDockFinishLaunch(dock, icon);
2971 icon->pid = 0;
2972 if (status==111) {
2973 char msg[PATH_MAX];
2974 #ifdef OFFIX_DND
2975 sprintf(msg, _("Could not execute command \"%s\""),
2976 icon->drop_launch && icon->dnd_command
2977 ? icon->dnd_command : icon->command);
2978 #else
2979 sprintf(msg, _("Could not execute command \"%s\""),
2980 icon->command);
2981 #endif
2982 wMessageDialog(dock->screen_ptr, _("Error"), msg,
2983 _("OK"), NULL, NULL);
2985 break;
2991 static void
2992 toggleLowered(WDock *dock)
2994 WAppIcon *tmp;
2995 int newlevel, i;
2997 /* lower/raise Dock */
2998 if (!dock->lowered) {
2999 newlevel = WMNormalWindowLevel;
3000 dock->lowered = 1;
3001 } else {
3002 newlevel = WMDockWindowLevel;
3003 dock->lowered = 0;
3006 for (i=0; i<dock->max_icons; i++) {
3007 tmp = dock->icon_array[i];
3008 if (!tmp)
3009 continue;
3011 ChangeStackingLevel(tmp->icon->core, newlevel);
3012 if (dock->lowered)
3013 wLowerFrame(tmp->icon->core);
3018 static void
3019 toggleCollapsed(WDock *dock)
3021 if (dock->collapsed) {
3022 dock->collapsed = 0;
3023 wDockShowIcons(dock);
3025 else {
3026 dock->collapsed = 1;
3027 wDockHideIcons(dock);
3032 static void
3033 toggleAutoCollapse(WDock *dock)
3035 dock->auto_collapse = !dock->auto_collapse;
3036 if (dock->auto_collapse_magic) {
3037 WMDeleteTimerHandler(dock->auto_collapse_magic);
3038 dock->auto_collapse_magic = NULL;
3043 static void
3044 openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event)
3046 WScreen *scr = dock->screen_ptr;
3047 WObjDescriptor *desc;
3048 WMenuEntry *entry;
3049 int index = 0;
3050 int x_pos;
3051 int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner;
3053 if (dock->type == WM_DOCK) {
3054 /* floating */
3055 entry = dock->menu->entries[index];
3056 entry->flags.indicator_on = !dock->lowered;
3057 entry->clientdata = dock;
3058 } else {
3059 /* clip options */
3060 if (scr->clip_options)
3061 updateClipOptionsMenu(scr->clip_options, dock);
3063 /* Rename Workspace */
3064 entry = dock->menu->entries[++index];
3065 entry->clientdata = dock;
3067 /* select icon */
3068 entry = dock->menu->entries[++index];
3069 entry->clientdata = aicon;
3070 wMenuSetEnabled(dock->menu, index, aicon!=scr->clip_icon);
3072 /* (un)select all icons */
3073 entry = dock->menu->entries[++index];
3074 entry->clientdata = aicon;
3075 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3077 /* keep icon(s) */
3078 entry = dock->menu->entries[++index];
3079 entry->clientdata = aicon;
3080 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3082 /* this is the workspace submenu part */
3083 if (scr->clip_submenu)
3084 updateWorkspaceMenu(scr->clip_submenu, aicon);
3085 index++;
3087 /* remove icon(s) */
3088 entry = dock->menu->entries[++index];
3089 entry->clientdata = aicon;
3090 wMenuSetEnabled(dock->menu, index, dock->icon_count > 1);
3092 /* attract icon(s) */
3093 entry = dock->menu->entries[++index];
3094 entry->clientdata = aicon;
3097 /* launch */
3098 entry = dock->menu->entries[++index];
3099 entry->clientdata = aicon;
3100 wMenuSetEnabled(dock->menu, index, aicon->command!=NULL);
3102 /* unhide here */
3103 entry = dock->menu->entries[++index];
3104 entry->clientdata = aicon;
3105 wMenuSetEnabled(dock->menu, index, appIsRunning);
3107 /* hide */
3108 entry = dock->menu->entries[++index];
3109 entry->clientdata = aicon;
3110 wMenuSetEnabled(dock->menu, index, appIsRunning);
3112 /* settings */
3113 entry = dock->menu->entries[++index];
3114 entry->clientdata = aicon;
3115 wMenuSetEnabled(dock->menu, index, !aicon->editing);
3117 /* kill */
3118 entry = dock->menu->entries[++index];
3119 entry->clientdata = aicon;
3120 wMenuSetEnabled(dock->menu, index, appIsRunning);
3122 if (!dock->menu->flags.realized)
3123 wMenuRealize(dock->menu);
3125 if (dock->type == WM_CLIP) {
3126 x_pos = event->xbutton.x_root+2;
3128 else {
3129 x_pos = dock->on_right_side ?
3130 scr->scr_width - dock->menu->frame->core->width - 2 : 0;
3133 wMenuMapAt(dock->menu, x_pos, event->xbutton.y_root+2, False);
3135 /* allow drag select */
3136 event->xany.send_event = True;
3137 desc = &dock->menu->menu->descriptor;
3138 (*desc->handle_mousedown)(desc, event);
3142 static void
3143 openClipWorkspaceMenu(WScreen *scr, int x, int y)
3145 if (!scr->clip_ws_menu) {
3146 scr->clip_ws_menu = wWorkspaceMenuMake(scr, False);
3148 wWorkspaceMenuUpdate(scr, scr->clip_ws_menu);
3149 wMenuMapAt(scr->clip_ws_menu, x, y, False);
3153 /******************************************************************/
3154 static void
3155 iconDblClick(WObjDescriptor *desc, XEvent *event)
3157 WAppIcon *btn = desc->parent;
3158 WDock *dock = btn->dock;
3159 WApplication *wapp = NULL;
3160 int unhideHere = 0;
3162 #ifdef REDUCE_APPICONS
3163 if ((btn->icon->owner && !(event->xbutton.state & ControlMask)) ||
3164 ((btn->icon->owner == NULL) && (btn->applist != NULL))) {
3165 if (btn->icon->owner == NULL)
3166 btn->icon->owner = btn->applist->wapp->main_window_desc;
3167 #ifdef I_HATE_THIS
3169 #endif
3170 #else
3171 if (btn->icon->owner && !(event->xbutton.state & ControlMask)) {
3172 #endif
3173 wapp = wApplicationOf(btn->icon->owner->main_window);
3175 assert(wapp!=NULL);
3177 unhideHere = (event->xbutton.state & ShiftMask);
3179 /* go to the last workspace that the user worked on the app */
3180 if (!unhideHere) {
3181 wWorkspaceChange(dock->screen_ptr, wapp->last_workspace);
3184 wUnhideApplication(wapp, event->xbutton.button==Button2,
3185 unhideHere);
3187 if (event->xbutton.state & MOD_MASK) {
3188 wHideOtherApplications(btn->icon->owner);
3190 } else {
3191 if (event->xbutton.button==Button1) {
3193 if (event->xbutton.state & MOD_MASK) {
3194 /* raise/lower dock */
3195 toggleLowered(dock);
3196 } else if (btn == dock->screen_ptr->clip_icon) {
3197 if (getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE)
3198 toggleCollapsed(dock);
3199 else
3200 handleClipChangeWorkspace(dock->screen_ptr, event);
3201 } else if (btn->command) {
3202 if (!btn->launching &&
3203 (!btn->running || (event->xbutton.state & ControlMask))) {
3204 launchDockedApplication(btn);
3212 static void
3213 handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3215 WScreen *scr = dock->screen_ptr;
3216 int ofs_x=event->xbutton.x, ofs_y=event->xbutton.y;
3217 int x, y;
3218 XEvent ev;
3219 int grabbed = 0, swapped = 0, done;
3220 Pixmap ghost = None;
3221 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3223 #ifdef DEBUG
3224 puts("moving dock");
3225 #endif
3226 if (XGrabPointer(dpy, aicon->icon->core->window, True, ButtonMotionMask
3227 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3228 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3229 wwarning("pointer grab failed for dock move");
3231 y = 0;
3232 for (x=0; x<dock->max_icons; x++) {
3233 if (dock->icon_array[x]!=NULL &&
3234 dock->icon_array[x]->yindex > y)
3235 y = dock->icon_array[x]->yindex;
3237 y++;
3238 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE*y);
3240 done = 0;
3241 while (!done) {
3242 WMMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3243 |ButtonMotionMask|ExposureMask, &ev);
3244 switch (ev.type) {
3245 case Expose:
3246 WMHandleEvent(&ev);
3247 break;
3249 case MotionNotify:
3250 if (!grabbed) {
3251 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3252 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3253 XChangeActivePointerGrab(dpy, ButtonMotionMask
3254 |ButtonReleaseMask|ButtonPressMask,
3255 wCursor[WCUR_MOVE], CurrentTime);
3256 grabbed=1;
3258 break;
3260 if (dock->type == WM_CLIP) {
3261 if (ev.xmotion.x_root - ofs_x < 0) {
3262 x = 0;
3263 } else if (ev.xmotion.x_root - ofs_x + ICON_SIZE >
3264 scr->scr_width) {
3265 x = scr->scr_width - ICON_SIZE;
3266 } else {
3267 x = ev.xmotion.x_root - ofs_x;
3269 if (ev.xmotion.y_root - ofs_y < 0) {
3270 y = 0;
3271 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3272 scr->scr_height) {
3273 y = scr->scr_height - ICON_SIZE;
3274 } else {
3275 y = ev.xmotion.y_root - ofs_y;
3277 moveDock(dock, x, y);
3279 else {
3280 /* move vertically if pointer is inside the dock*/
3281 if ((dock->on_right_side &&
3282 ev.xmotion.x_root >= dock->x_pos - ICON_SIZE)
3283 || (!dock->on_right_side &&
3284 ev.xmotion.x_root <= dock->x_pos + ICON_SIZE*2)) {
3286 if (ev.xmotion.y_root - ofs_y < 0) {
3287 y = 0;
3288 } else if (ev.xmotion.y_root - ofs_y + ICON_SIZE >
3289 scr->scr_height) {
3290 y = scr->scr_height - ICON_SIZE;
3291 } else {
3292 y = ev.xmotion.y_root - ofs_y;
3294 moveDock(dock, dock->x_pos, y);
3296 /* move horizontally to change sides */
3297 x = ev.xmotion.x_root - ofs_x;
3298 if (!dock->on_right_side) {
3300 /* is on left */
3302 if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE*2) {
3303 XMoveWindow(dpy, scr->dock_shadow, scr->scr_width-ICON_SIZE
3304 -DOCK_EXTRA_SPACE, dock->y_pos);
3305 if (superfluous) {
3306 if (ghost==None) {
3307 ghost = MakeGhostDock(dock, dock->x_pos,
3308 scr->scr_width-ICON_SIZE
3309 -DOCK_EXTRA_SPACE,
3310 dock->y_pos);
3311 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3312 ghost);
3313 XClearWindow(dpy, scr->dock_shadow);
3316 XMapRaised(dpy, scr->dock_shadow);
3317 swapped = 1;
3318 } else {
3319 if (superfluous && ghost!=None) {
3320 XFreePixmap(dpy, ghost);
3321 ghost = None;
3323 XUnmapWindow(dpy, scr->dock_shadow);
3324 swapped = 0;
3326 } else {
3327 /* is on right */
3328 if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) {
3329 XMoveWindow(dpy, scr->dock_shadow,
3330 DOCK_EXTRA_SPACE, dock->y_pos);
3331 if (superfluous) {
3332 if (ghost==None) {
3333 ghost = MakeGhostDock(dock, dock->x_pos,
3334 DOCK_EXTRA_SPACE, dock->y_pos);
3335 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow,
3336 ghost);
3337 XClearWindow(dpy, scr->dock_shadow);
3340 XMapRaised(dpy, scr->dock_shadow);
3341 swapped = -1;
3342 } else {
3343 XUnmapWindow(dpy, scr->dock_shadow);
3344 swapped = 0;
3345 if (superfluous && ghost!=None) {
3346 XFreePixmap(dpy, ghost);
3347 ghost = None;
3352 break;
3354 case ButtonPress:
3355 break;
3357 case ButtonRelease:
3358 if (ev.xbutton.button != event->xbutton.button)
3359 break;
3360 XUngrabPointer(dpy, CurrentTime);
3361 XUnmapWindow(dpy, scr->dock_shadow);
3362 XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE);
3363 if (dock->type == WM_DOCK) {
3364 if (swapped!=0) {
3365 if (swapped>0)
3366 dock->on_right_side = 1;
3367 else
3368 dock->on_right_side = 0;
3369 swapDock(dock);
3370 wArrangeIcons(scr, False);
3373 done = 1;
3374 break;
3377 if (superfluous) {
3378 if (ghost!=None)
3379 XFreePixmap(dpy, ghost);
3380 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3382 #ifdef DEBUG
3383 puts("End dock move");
3384 #endif
3389 static void
3390 handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event)
3392 WScreen *scr = dock->screen_ptr;
3393 Window wins[2];
3394 WIcon *icon = aicon->icon;
3395 WDock *dock2 = NULL, *last_dock = dock;
3396 int ondock, grabbed = 0, change_dock = 0, collapsed = 0;
3397 XEvent ev;
3398 int x = aicon->x_pos, y = aicon->y_pos;
3399 int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y;
3400 int shad_x = x, shad_y = y;
3401 int ix = aicon->xindex, iy = aicon->yindex;
3402 int tmp;
3403 Pixmap ghost = None;
3404 Bool docked;
3405 int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */
3407 if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask
3408 |ButtonReleaseMask|ButtonPressMask, GrabModeAsync,
3409 GrabModeAsync, None, None, CurrentTime) !=GrabSuccess) {
3410 #ifdef DEBUG0
3411 wwarning("pointer grab failed for icon move");
3412 #endif
3415 wRaiseFrame(icon->core);
3417 if (dock == scr->dock && !wPreferences.flags.noclip)
3418 dock2 = scr->workspaces[scr->current_workspace]->clip;
3419 else if (dock != scr->dock && !wPreferences.flags.nodock)
3420 dock2 = scr->dock;
3422 wins[0] = icon->core->window;
3423 wins[1] = scr->dock_shadow;
3424 XRestackWindows(dpy, wins, 2);
3425 XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos,
3426 ICON_SIZE, ICON_SIZE);
3427 if (superfluous) {
3428 if (icon->pixmap!=None)
3429 ghost = MakeGhostIcon(scr, icon->pixmap);
3430 else
3431 ghost = MakeGhostIcon(scr, icon->core->window);
3433 XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost);
3434 XClearWindow(dpy, scr->dock_shadow);
3436 XMapWindow(dpy, scr->dock_shadow);
3438 ondock = 1;
3441 while(1) {
3442 XMaskEvent(dpy, PointerMotionMask|ButtonReleaseMask|ButtonPressMask
3443 |ButtonMotionMask|ExposureMask, &ev);
3444 switch (ev.type) {
3445 case Expose:
3446 WMHandleEvent(&ev);
3447 break;
3449 case MotionNotify:
3450 if (!grabbed) {
3451 if (abs(ofs_x-ev.xmotion.x)>=MOVE_THRESHOLD
3452 || abs(ofs_y-ev.xmotion.y)>=MOVE_THRESHOLD) {
3453 XChangeActivePointerGrab(dpy, ButtonMotionMask
3454 |ButtonReleaseMask|ButtonPressMask,
3455 wCursor[WCUR_MOVE], CurrentTime);
3456 grabbed=1;
3457 } else {
3458 break;
3462 x = ev.xmotion.x_root - ofs_x;
3463 y = ev.xmotion.y_root - ofs_y;
3464 tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True);
3465 if (tmp && dock2) {
3466 change_dock = 0;
3467 if (last_dock != dock && collapsed) {
3468 last_dock->collapsed = 1;
3469 wDockHideIcons(last_dock);
3470 collapsed = 0;
3472 if (!collapsed && (collapsed = dock->collapsed)) {
3473 dock->collapsed = 0;
3474 wDockShowIcons(dock);
3476 last_dock = dock;
3478 else if (dock2) {
3479 tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False);
3480 if (tmp) {
3481 change_dock = 1;
3482 if (last_dock != dock2 && collapsed) {
3483 last_dock->collapsed = 1;
3484 wDockHideIcons(last_dock);
3485 collapsed = 0;
3487 if (!collapsed && (collapsed = dock2->collapsed)) {
3488 dock2->collapsed = 0;
3489 wDockShowIcons(dock2);
3491 last_dock = dock2;
3494 if (aicon->launching
3495 || (aicon->running && !(ev.xmotion.state & MOD_MASK))
3496 || (!aicon->running && tmp)) {
3497 shad_x = last_dock->x_pos + ix*wPreferences.icon_size;
3498 shad_y = last_dock->y_pos + iy*wPreferences.icon_size;
3500 XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y);
3502 if (!ondock) {
3503 XMapWindow(dpy, scr->dock_shadow);
3504 #if 0
3505 if (!collapsed && (collapsed = last_dock->collapsed)) {
3506 last_dock->collapsed = 0;
3507 wDockShowIcons(last_dock);
3509 #endif
3511 ondock = 1;
3512 } else {
3513 if (ondock) {
3514 XUnmapWindow(dpy, scr->dock_shadow);
3515 #if 0
3516 if (last_dock && collapsed &&
3517 aicon->running && (ev.xmotion.state & MOD_MASK)) {
3518 last_dock->collapsed = 1;
3519 wDockHideIcons(last_dock);
3520 collapsed = 0;
3522 #endif
3524 ondock = 0;
3526 XMoveWindow(dpy, icon->core->window, x, y);
3527 break;
3529 case ButtonPress:
3530 break;
3532 case ButtonRelease:
3533 if (ev.xbutton.button != event->xbutton.button)
3534 break;
3535 XUngrabPointer(dpy, CurrentTime);
3536 if (ondock) {
3537 SlideWindow(icon->core->window, x, y, shad_x, shad_y);
3538 XUnmapWindow(dpy, scr->dock_shadow);
3539 if (!change_dock)
3540 reattachIcon(dock, aicon, ix, iy);
3541 else {
3542 docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy);
3543 if (!docked) {
3544 /* Slide it back if dock rejected it */
3545 SlideWindow(icon->core->window, x, y, aicon->x_pos,
3546 aicon->y_pos);
3547 reattachIcon(dock, aicon, aicon->xindex,aicon->yindex);
3549 if (last_dock->type==WM_CLIP && last_dock->auto_collapse) {
3550 collapsed = 0;
3553 } else {
3554 aicon->x_pos = x;
3555 aicon->y_pos = y;
3556 if (superfluous) {
3557 if (!aicon->running && !wPreferences.no_animations) {
3558 /* We need to deselect it, even if is deselected in
3559 * wDockDetach(), because else DoKaboom() will fail.
3561 if (aicon->icon->selected)
3562 wIconSelect(aicon->icon);
3563 DoKaboom(scr,aicon->icon->core->window, x, y);
3566 wDockDetach(dock, aicon);
3568 if (collapsed) {
3569 last_dock->collapsed = 1;
3570 wDockHideIcons(last_dock);
3571 collapsed = 0;
3573 if (superfluous) {
3574 if (ghost!=None)
3575 XFreePixmap(dpy, ghost);
3576 XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel);
3578 #ifdef DEBUG
3579 puts("End icon move");
3580 #endif
3581 return;
3587 static int
3588 getClipButton(int px, int py)
3590 int pt = (CLIP_BUTTON_SIZE+2)*ICON_SIZE/64;
3592 if (px < 0 || py < 0 || px >= ICON_SIZE || py >= ICON_SIZE)
3593 return CLIP_IDLE;
3595 if (py <= pt-((int)ICON_SIZE-1-px))
3596 return CLIP_FORWARD;
3597 else if (px <= pt-((int)ICON_SIZE-1-py))
3598 return CLIP_REWIND;
3600 return CLIP_IDLE;
3604 static void
3605 handleClipChangeWorkspace(WScreen *scr, XEvent *event)
3607 XEvent ev;
3608 int done, direction, new_ws;
3609 int new_dir;
3610 WDock *clip = scr->clip_icon->dock;
3612 /* if this is reached during startup (before the 1st wWorkspaceForceChange)
3613 * we will get undesired effects, like having all windows unmapped and
3614 * don't having them back if the clip buttons are clicked.
3616 if (scr->flags.startup2 || scr->flags.startup)
3617 return;
3619 direction = getClipButton(event->xbutton.x, event->xbutton.y);
3621 clip->lclip_button_pushed = direction==CLIP_REWIND;
3622 clip->rclip_button_pushed = direction==CLIP_FORWARD;
3624 wClipIconPaint(scr->clip_icon);
3625 done = 0;
3626 while(!done) {
3627 WMMaskEvent(dpy, ExposureMask|ButtonMotionMask|ButtonReleaseMask
3628 |ButtonPressMask, &ev);
3629 switch (ev.type) {
3630 case Expose:
3631 WMHandleEvent(&ev);
3632 break;
3634 case MotionNotify:
3635 new_dir = getClipButton(ev.xmotion.x, ev.xmotion.y);
3636 if (new_dir != direction) {
3637 direction = new_dir;
3638 clip->lclip_button_pushed = direction==CLIP_REWIND;
3639 clip->rclip_button_pushed = direction==CLIP_FORWARD;
3640 wClipIconPaint(scr->clip_icon);
3642 break;
3644 case ButtonPress:
3645 break;
3647 case ButtonRelease:
3648 if (ev.xbutton.button == event->xbutton.button)
3649 done = 1;
3653 clip->lclip_button_pushed = 0;
3654 clip->rclip_button_pushed = 0;
3656 new_ws = wPreferences.ws_advance || (event->xbutton.state & ControlMask);
3658 if (direction == CLIP_FORWARD) {
3659 if (scr->current_workspace < scr->workspace_count-1)
3660 wWorkspaceChange(scr, scr->current_workspace+1);
3661 else if (new_ws && scr->current_workspace < MAX_WORKSPACES-1)
3662 wWorkspaceChange(scr, scr->current_workspace+1);
3663 else if (wPreferences.ws_cycle)
3664 wWorkspaceChange(scr, 0);
3666 else if (direction == CLIP_REWIND) {
3667 if (scr->current_workspace > 0)
3668 wWorkspaceChange(scr, scr->current_workspace-1);
3669 else if (scr->current_workspace==0 && wPreferences.ws_cycle)
3670 wWorkspaceChange(scr, scr->workspace_count-1);
3673 wClipIconPaint(scr->clip_icon);
3677 static void
3678 iconMouseDown(WObjDescriptor *desc, XEvent *event)
3680 WAppIcon *aicon = desc->parent;
3681 WDock *dock = aicon->dock;
3682 WScreen *scr = aicon->icon->core->screen_ptr;
3684 scr->last_dock = dock;
3686 if (dock->menu->flags.mapped)
3687 wMenuUnmap(dock->menu);
3689 if (IsDoubleClick(scr, event)) {
3690 /* double-click was not in the main clip icon */
3691 if (dock->type != WM_CLIP || aicon->xindex!=0 || aicon->yindex!=0
3692 || getClipButton(event->xbutton.x, event->xbutton.y)==CLIP_IDLE) {
3693 iconDblClick(desc, event);
3694 return;
3698 if (dock->type == WM_CLIP && scr->flags.clip_balloon_mapped) {
3699 XUnmapWindow(dpy, scr->clip_balloon);
3700 scr->flags.clip_balloon_mapped = 0;
3703 #ifdef DEBUG
3704 puts("handling dock");
3705 #endif
3706 if (event->xbutton.button == Button1) {
3707 if (event->xbutton.state & MOD_MASK)
3708 wDockLower(dock);
3709 else
3710 wDockRaise(dock);
3712 if ((event->xbutton.state & ShiftMask) && aicon!=scr->clip_icon &&
3713 dock->type!=WM_DOCK) {
3714 wIconSelect(aicon->icon);
3715 return;
3718 if (aicon->yindex==0 && aicon->xindex==0) {
3719 if (getClipButton(event->xbutton.x, event->xbutton.y)!=CLIP_IDLE
3720 && dock->type==WM_CLIP)
3721 handleClipChangeWorkspace(scr, event);
3722 else
3723 handleDockMove(dock, aicon, event);
3724 } else
3725 handleIconMove(dock, aicon, event);
3727 } else if (event->xbutton.button==Button2 && dock->type==WM_CLIP &&
3728 aicon->xindex==0 && aicon->yindex==0) {
3729 openClipWorkspaceMenu(scr, event->xbutton.x_root+2,
3730 event->xbutton.y_root+2);
3731 if (scr->clip_ws_menu) {
3732 WMenu *menu;
3733 menu = scr->clip_ws_menu;
3734 desc = &menu->menu->descriptor;
3736 event->xany.send_event = True;
3737 (*desc->handle_mousedown)(desc, event);
3739 } else if (event->xbutton.button == Button3) {
3740 openDockMenu(dock, aicon, event);
3745 static void
3746 showClipBalloon(WDock *dock, int workspace)
3748 int w, h;
3749 int x, y;
3750 WScreen *scr = dock->screen_ptr;
3751 char *text;
3752 Window stack[2];
3754 scr->flags.clip_balloon_mapped = 1;
3755 XMapWindow(dpy, scr->clip_balloon);
3757 text = scr->workspaces[workspace]->name;
3759 w = wTextWidth(scr->clip_title_font->font, text, strlen(text)) + 4;
3761 h = scr->clip_title_font->height;
3762 XResizeWindow(dpy, scr->clip_balloon, w, h);
3764 x = dock->x_pos + CLIP_BUTTON_SIZE*ICON_SIZE/64;
3765 y = dock->y_pos + ICON_SIZE-scr->clip_title_font->height - 3;
3767 if (x+w > scr->scr_width) {
3768 x = scr->scr_width - w;
3769 if (dock->y_pos + ICON_SIZE + h > scr->scr_height)
3770 y = dock->y_pos - h - 1;
3771 else
3772 y = dock->y_pos + ICON_SIZE;
3773 XRaiseWindow(dpy, scr->clip_balloon);
3774 } else {
3775 stack[0] = scr->clip_icon->icon->core->window;
3776 stack[1] = scr->clip_balloon;
3777 XRestackWindows(dpy, stack, 2);
3779 XMoveWindow(dpy, scr->clip_balloon, x, y);
3780 XSetForeground(dpy, scr->clip_title_gc,
3781 scr->clip_title_pixel[CLIP_NORMAL]);
3782 XClearWindow(dpy, scr->clip_balloon);
3783 wDrawString(scr->clip_balloon, scr->clip_title_font, scr->clip_title_gc,
3784 0, scr->clip_title_font->y, text, strlen(text));
3788 static void
3789 clipEnterNotify(WObjDescriptor *desc, XEvent *event)
3791 WAppIcon *btn = (WAppIcon*)desc->parent;
3792 WDock *dock;
3794 assert(event->type==EnterNotify);
3796 if(desc->parent_type!=WCLASS_DOCK_ICON)
3797 return;
3799 dock = btn->dock;
3800 if (!dock || dock->type!=WM_CLIP)
3801 return;
3803 if (btn->xindex == 0 && btn->yindex == 0)
3804 showClipBalloon(dock, dock->screen_ptr->current_workspace);
3805 else {
3806 if (dock->screen_ptr->flags.clip_balloon_mapped) {
3807 XUnmapWindow(dpy, dock->screen_ptr->clip_balloon);
3808 dock->screen_ptr->flags.clip_balloon_mapped = 0;
3811 if (!dock->auto_collapse)
3812 return;
3814 if (dock->auto_collapse_magic) {
3815 WMDeleteTimerHandler(dock->auto_collapse_magic);
3816 dock->auto_collapse_magic = NULL;
3819 if (dock->collapsed)
3820 toggleCollapsed(dock);
3824 static void
3825 clipLeave(WDock *dock)
3827 if (!dock || dock->type!=WM_CLIP || !dock->auto_collapse)
3828 return;
3830 if (dock->auto_collapse_magic) {
3831 WMDeleteTimerHandler(dock->auto_collapse_magic);
3832 dock->auto_collapse_magic = NULL;
3834 if (!dock->collapsed) {
3835 dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY,
3836 clipAutoCollapse,
3837 (void *)dock);
3842 static void
3843 clipLeaveNotify(WObjDescriptor *desc, XEvent *event)
3845 WAppIcon *btn = (WAppIcon*)desc->parent;
3847 assert(event->type==LeaveNotify);
3849 if(desc->parent_type!=WCLASS_DOCK_ICON)
3850 return;
3852 clipLeave(btn->dock);
3856 static void
3857 clipAutoCollapse(void *cdata)
3859 WDock *dock = (WDock *)cdata;
3861 if (dock->type!=WM_CLIP || !dock->auto_collapse)
3862 return;
3864 if (!dock->collapsed && dock->auto_collapse_magic!=NULL) {
3865 toggleCollapsed(dock);
3867 dock->auto_collapse_magic = NULL;