1 /* winmenu.c - command menu for windows
3 * Window Maker window manager
5 * Copyright (c) 1997, 1998 Alfredo K. Kojima
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,
31 #include <X11/Xutil.h>
33 #include "WindowMaker.h"
39 #include "application.h"
42 #include "workspace.h"
43 #include "winspector.h"
48 #define MC_MINIATURIZE 1
51 #define MC_MOVERESIZE 4
53 #define MC_DUMMY_MOVETO 6
54 #define MC_PROPERTIES 7
62 #define WO_KEEP_ON_TOP 0
63 #define WO_KEEP_AT_BOTTOM 1
64 #define WO_OMNIPRESENT 2
67 /**** Global data ***/
68 extern Time LastTimestamp
;
69 extern Atom _XA_WM_DELETE_WINDOW
;
70 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
;
72 extern WShortKey wKeyBindings
[WKBD_LAST
];
74 extern WPreferences wPreferences
;
76 static void updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
);
79 execWindowOptionCommand(WMenu
*menu
, WMenuEntry
*entry
)
81 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
83 switch (entry
->order
) {
85 if(wwin
->frame
->core
->stacking
->window_level
!=WMFloatingLevel
)
86 ChangeStackingLevel(wwin
->frame
->core
, WMFloatingLevel
);
88 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
91 case WO_KEEP_AT_BOTTOM
:
92 if(wwin
->frame
->core
->stacking
->window_level
!=WMSunkenLevel
)
93 ChangeStackingLevel(wwin
->frame
->core
, WMSunkenLevel
);
95 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
99 wwin
->flags
.omnipresent
^=1;
100 UpdateSwitchMenu(wwin
->screen_ptr
, wwin
, ACTION_CHANGE_WORKSPACE
);
107 execMenuCommand(WMenu
*menu
, WMenuEntry
*entry
)
109 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
112 CloseWindowMenu(menu
->frame
->screen_ptr
);
114 switch (entry
->order
) {
116 /* send delete message */
117 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
122 if (wPreferences
.dont_confirm_kill
123 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
124 _("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
125 _("Yes"), _("No"), NULL
)==WAPRDefault
) {
126 if (!wwin
->flags
.destroyed
)
133 if (wwin
->flags
.miniaturized
) {
134 wDeiconifyWindow(wwin
);
136 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
137 wClientSendProtocol(wwin
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
,
140 wIconifyWindow(wwin
);
146 if (wwin
->flags
.maximized
)
147 wUnmaximizeWindow(wwin
);
149 wMaximizeWindow(wwin
, MAX_VERTICAL
|MAX_HORIZONTAL
);
153 if (wwin
->flags
.shaded
)
154 wUnshadeWindow(wwin
);
160 wSelectWindow(wwin
, !wwin
->flags
.selected
);
164 wKeyboardMoveResizeWindow(wwin
);
168 if (wwin
->wm_class
|| wwin
->wm_instance
)
169 wShowInspectorForWindow(wwin
);
173 wapp
= wApplicationOf(wwin
->main_window
);
174 wHideApplication(wapp
);
182 switchWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
184 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
186 wSelectWindow(wwin
, False
);
187 wWindowChangeWorkspace(wwin
, entry
->order
);
192 makeShortcutCommand(WMenu
*menu
, WMenuEntry
*entry
)
194 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
196 wwin
->screen_ptr
->shortcutWindow
[entry
->order
-WO_ENTRIES
] = wwin
;
198 wSelectWindow(wwin
, !wwin
->flags
.selected
);
201 wSelectWindow(wwin
, !wwin
->flags
.selected
);
207 updateWorkspaceMenu(WMenu
*menu
)
209 WScreen
*scr
= menu
->frame
->screen_ptr
;
210 char title
[MAX_WORKSPACENAME_WIDTH
+1];
216 for (i
=0; i
<scr
->workspace_count
; i
++) {
217 if (i
< menu
->entry_no
) {
218 if (strcmp(menu
->entries
[i
]->text
,scr
->workspaces
[i
]->name
)!=0) {
219 free(menu
->entries
[i
]->text
);
220 strcpy(title
, scr
->workspaces
[i
]->name
);
221 menu
->entries
[i
]->text
= wstrdup(title
);
222 menu
->flags
.realized
= 0;
225 strcpy(title
, scr
->workspaces
[i
]->name
);
227 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
229 menu
->flags
.realized
= 0;
233 if (!menu
->flags
.realized
)
239 updateMakeShortcutMenu(WMenu
*menu
, WWindow
*wwin
)
241 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
249 buffer
= wmalloc(strlen(_("Set Shortcut"))+16);
251 for (i
=WO_ENTRIES
; i
<smenu
->entry_no
; i
++) {
253 int shortcutNo
= i
-WO_ENTRIES
;
254 WWindow
*twin
= wwin
->screen_ptr
->shortcutWindow
[shortcutNo
];
255 WMenuEntry
*entry
= smenu
->entries
[i
];
257 sprintf(buffer
, "%s %i", _("Set Shortcut"), shortcutNo
+1);
260 entry
->flags
.indicator_on
= 0;
262 entry
->flags
.indicator_on
= 1;
264 entry
->flags
.indicator_type
= MI_CHECK
;
266 entry
->flags
.indicator_type
= MI_DIAMOND
;
269 if (strcmp(buffer
, entry
->text
)!=0) {
271 entry
->text
= wstrdup(buffer
);
272 smenu
->flags
.realized
= 0;
275 kcode
= wKeyBindings
[WKBD_WINDOW1
+shortcutNo
].keycode
;
278 if ((tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0)))
279 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
)!=0)) {
282 entry
->rtext
= wstrdup(tmp
);
283 smenu
->flags
.realized
= 0;
285 wMenuSetEnabled(smenu
, i
, True
);
287 wMenuSetEnabled(smenu
, i
, False
);
291 smenu
->flags
.realized
= 0;
294 entry
->clientdata
= wwin
;
297 if (!smenu
->flags
.realized
)
303 updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
)
305 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
307 /* keep on top check */
308 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
309 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
310 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
)?1:0;
312 /* keep at bottom check */
313 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
314 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
315 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
)?1:0;
317 /* omnipresent check */
318 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
319 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
321 smenu
->flags
.realized
= 0;
327 makeWorkspaceMenu(WScreen
*scr
)
331 menu
= wMenuCreate(scr
, NULL
, False
);
333 wwarning(_("could not create submenu for window menu"));
337 updateWorkspaceMenu(menu
);
344 makeMakeShortcutMenu(WScreen
*scr
, WMenu
*menu
)
351 menu = wMenuCreate(scr, NULL, False);
353 wwarning(_("could not create submenu for window menu"));
358 for (i
=0; i
<MAX_WINDOW_SHORTCUTS
; i
++) {
361 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
363 entry
->flags
.indicator
= 1;
372 makeOptionsMenu(WScreen
*scr
)
377 menu
= wMenuCreate(scr
, NULL
, False
);
379 wwarning(_("could not create submenu for window menu"));
383 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
,
385 entry
->flags
.indicator
= 1;
386 entry
->flags
.indicator_type
= MI_CHECK
;
388 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
,
390 entry
->flags
.indicator
= 1;
391 entry
->flags
.indicator_type
= MI_CHECK
;
393 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
,
395 entry
->flags
.indicator
= 1;
396 entry
->flags
.indicator_type
= MI_CHECK
;
403 createWindowMenu(WScreen
*scr
)
410 menu
= wMenuCreate(scr
, NULL
, False
);
412 * Warning: If you make some change that affects the order of the
413 * entries, you must update the command #defines in the top of
416 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
417 if (wKeyBindings
[WKBD_MAXIMIZE
].keycode
!=0) {
418 kcode
= wKeyBindings
[WKBD_MAXIMIZE
].keycode
;
420 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
421 entry
->rtext
= wstrdup(tmp
);
424 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
426 if (wKeyBindings
[WKBD_MINIATURIZE
].keycode
!=0) {
427 kcode
= wKeyBindings
[WKBD_MINIATURIZE
].keycode
;
429 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
430 entry
->rtext
= wstrdup(tmp
);
433 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
434 if (wKeyBindings
[WKBD_SHADE
].keycode
!=0) {
435 kcode
= wKeyBindings
[WKBD_SHADE
].keycode
;
437 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
438 entry
->rtext
= wstrdup(tmp
);
441 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
442 if (wKeyBindings
[WKBD_HIDE
].keycode
!=0) {
443 kcode
= wKeyBindings
[WKBD_HIDE
].keycode
;
445 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
446 entry
->rtext
= wstrdup(tmp
);
449 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
450 if (wKeyBindings
[WKBD_MOVERESIZE
].keycode
!=0) {
451 kcode
= wKeyBindings
[WKBD_MOVERESIZE
].keycode
;
453 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
454 entry
->rtext
= wstrdup(tmp
);
457 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
458 if (wKeyBindings
[WKBD_SELECT
].keycode
!=0) {
459 kcode
= wKeyBindings
[WKBD_SELECT
].keycode
;
461 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
462 entry
->rtext
= wstrdup(tmp
);
465 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
466 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
467 if (scr
->workspace_submenu
)
468 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
470 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
472 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
473 wMenuEntrySetCascade(menu
, entry
,
474 makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
477 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
478 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
481 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
482 if (wKeyBindings
[WKBD_CLOSE
].keycode
!=0) {
483 kcode
= wKeyBindings
[WKBD_CLOSE
].keycode
;
484 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
485 entry
->rtext
= wstrdup(tmp
);
488 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
495 CloseWindowMenu(WScreen
*scr
)
497 if (scr
->window_menu
) {
498 if (scr
->window_menu
->flags
.mapped
)
499 wMenuUnmap(scr
->window_menu
);
501 if (scr
->window_menu
->entries
[0]->clientdata
) {
502 WWindow
*wwin
= (WWindow
*)scr
->window_menu
->entries
[0]->clientdata
;
504 wwin
->flags
.menu_open_for_me
= 0;
506 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
513 updateMenuForWindow(WMenu
*menu
, WWindow
*wwin
)
515 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
516 WScreen
*scr
= wwin
->screen_ptr
;
519 updateOptionsMenu(menu
, wwin
);
521 updateMakeShortcutMenu(menu
, wwin
);
523 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!=NULL
524 && !WFLAGP(wapp
->main_window_desc
, no_appicon
));
526 wMenuSetEnabled(menu
, MC_CLOSE
,
527 (wwin
->protocols
.DELETE_WINDOW
528 && !WFLAGP(wwin
, no_closable
)));
530 if (wwin
->flags
.miniaturized
) {
531 static char *text
= NULL
;
532 if (!text
) text
= _("Deminiaturize");
534 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
536 static char *text
= NULL
;
537 if (!text
) text
= _("Miniaturize");
539 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
542 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
544 if (wwin
->flags
.maximized
) {
545 static char *text
= NULL
;
546 if (!text
) text
= _("Unmaximize");
548 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
550 static char *text
= NULL
;
551 if (!text
) text
= _("Maximize");
553 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
556 wMenuSetEnabled(menu
, MC_MOVERESIZE
, !WFLAGP(wwin
, no_resizable
));
558 if (wwin
->flags
.shaded
) {
559 static char *text
= NULL
;
560 if (!text
) text
= _("Unshade");
562 menu
->entries
[MC_SHADE
]->text
= text
;
564 static char *text
= NULL
;
565 if (!text
) text
= _("Shade");
567 menu
->entries
[MC_SHADE
]->text
= text
;
570 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
571 && !wwin
->flags
.miniaturized
);
573 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
575 if ((wwin
->wm_class
|| wwin
->wm_instance
) && !wwin
->flags
.inspector_open
) {
576 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
578 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
581 /* set the client data of the entries to the window */
582 for (i
= 0; i
< menu
->entry_no
; i
++) {
583 menu
->entries
[i
]->clientdata
= wwin
;
586 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
587 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
588 if (i
== scr
->current_workspace
) {
589 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
591 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
595 menu
->flags
.realized
= 0;
601 OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
604 WScreen
*scr
= wwin
->screen_ptr
;
606 wwin
->flags
.menu_open_for_me
= 1;
608 if (!scr
->window_menu
) {
609 scr
->window_menu
= createWindowMenu(scr
);
611 /* hack to save some memory allocation/deallocation */
612 free(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
613 free(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
614 free(scr
->window_menu
->entries
[MC_SHADE
]->text
);
616 updateWorkspaceMenu(scr
->workspace_submenu
);
619 menu
= scr
->window_menu
;
620 if (menu
->flags
.mapped
) {
622 if (menu
->entries
[0]->clientdata
==wwin
) {
627 updateMenuForWindow(menu
, wwin
);
629 x
-= menu
->frame
->core
->width
/2;
630 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+wwin
->frame
->core
->width
)
631 x
= wwin
->frame_x
+wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
632 if (x
< wwin
->frame_x
)
635 if (!wwin
->flags
.internal_window
)
636 wMenuMapAt(menu
, x
, y
, keyboard
);
642 OpenMiniwindowMenu(WWindow
*wwin
, int x
, int y
)
645 WScreen
*scr
= wwin
->screen_ptr
;
647 wwin
->flags
.menu_open_for_me
= 1;
649 if (!scr
->window_menu
) {
650 scr
->window_menu
= createWindowMenu(scr
);
652 /* hack to save some memory allocation/deallocation */
653 free(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
654 free(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
655 free(scr
->window_menu
->entries
[MC_SHADE
]->text
);
657 updateWorkspaceMenu(scr
->workspace_submenu
);
660 menu
= scr
->window_menu
;
661 if (menu
->flags
.mapped
) {
663 if (menu
->entries
[0]->clientdata
==wwin
) {
668 updateMenuForWindow(menu
, wwin
);
670 x
-= menu
->frame
->core
->width
/2;
672 wMenuMapAt(menu
, x
, y
, False
);