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"
50 #define MC_MINIATURIZE 1
53 #define MC_MOVERESIZE 4
55 #define MC_DUMMY_MOVETO 6
56 #define MC_PROPERTIES 7
64 #define WO_KEEP_ON_TOP 0
65 #define WO_KEEP_AT_BOTTOM 1
66 #define WO_OMNIPRESENT 2
69 /**** Global data ***/
70 extern Time LastTimestamp
;
71 extern Atom _XA_WM_DELETE_WINDOW
;
72 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
;
74 extern WShortKey wKeyBindings
[WKBD_LAST
];
76 extern WPreferences wPreferences
;
78 static void updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
);
81 execWindowOptionCommand(WMenu
*menu
, WMenuEntry
*entry
)
83 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
85 switch (entry
->order
) {
87 if(wwin
->frame
->core
->stacking
->window_level
!=WMFloatingLevel
)
88 ChangeStackingLevel(wwin
->frame
->core
, WMFloatingLevel
);
90 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
93 case WO_KEEP_AT_BOTTOM
:
94 if(wwin
->frame
->core
->stacking
->window_level
!=WMSunkenLevel
)
95 ChangeStackingLevel(wwin
->frame
->core
, WMSunkenLevel
);
97 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
101 wwin
->flags
.omnipresent
^=1;
102 UpdateSwitchMenu(wwin
->screen_ptr
, wwin
, ACTION_CHANGE_WORKSPACE
);
104 wGNOMEUpdateClientStateHint(wwin
, False
);
112 execMenuCommand(WMenu
*menu
, WMenuEntry
*entry
)
114 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
117 CloseWindowMenu(menu
->frame
->screen_ptr
);
119 switch (entry
->order
) {
121 /* send delete message */
122 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
127 if (wPreferences
.dont_confirm_kill
128 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
129 _("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
130 _("Yes"), _("No"), NULL
)==WAPRDefault
) {
131 if (!wwin
->flags
.destroyed
)
138 if (wwin
->flags
.miniaturized
) {
139 wDeiconifyWindow(wwin
);
141 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
142 wClientSendProtocol(wwin
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
,
145 wIconifyWindow(wwin
);
151 if (wwin
->flags
.maximized
)
152 wUnmaximizeWindow(wwin
);
154 wMaximizeWindow(wwin
, MAX_VERTICAL
|MAX_HORIZONTAL
);
158 if (wwin
->flags
.shaded
)
159 wUnshadeWindow(wwin
);
165 if (!wwin
->flags
.miniaturized
)
166 wSelectWindow(wwin
, !wwin
->flags
.selected
);
168 wIconSelect(wwin
->icon
);
172 wKeyboardMoveResizeWindow(wwin
);
176 wShowInspectorForWindow(wwin
);
180 wapp
= wApplicationOf(wwin
->main_window
);
181 wHideApplication(wapp
);
189 switchWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
191 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
193 wSelectWindow(wwin
, False
);
194 wWindowChangeWorkspace(wwin
, entry
->order
);
199 makeShortcutCommand(WMenu
*menu
, WMenuEntry
*entry
)
201 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
202 WScreen
*scr
= wwin
->screen_ptr
;
203 int index
= entry
->order
-WO_ENTRIES
;
205 if (scr
->shortcutWindows
[index
]) {
206 WMFreeBag(scr
->shortcutWindows
[index
]);
207 scr
->shortcutWindows
[index
] = NULL
;
210 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
211 WMBag
*selwin
= scr
->selected_windows
;
214 scr
->shortcutWindows
[index
] = WMCreateBag(4);
216 for (i
= 0; i
< WMGetBagItemCount(selwin
); i
++) {
217 WWindow
*tmp
= WMGetFromBag(selwin
, i
);
219 WMPutInBag(scr
->shortcutWindows
[index
], tmp
);
222 scr
->shortcutWindows
[index
] = WMCreateBag(4);
224 WMPutInBag(scr
->shortcutWindows
[index
], wwin
);
227 wSelectWindow(wwin
, !wwin
->flags
.selected
);
230 wSelectWindow(wwin
, !wwin
->flags
.selected
);
236 updateWorkspaceMenu(WMenu
*menu
)
238 WScreen
*scr
= menu
->frame
->screen_ptr
;
239 char title
[MAX_WORKSPACENAME_WIDTH
+1];
245 for (i
=0; i
<scr
->workspace_count
; i
++) {
246 if (i
< menu
->entry_no
) {
247 if (strcmp(menu
->entries
[i
]->text
,scr
->workspaces
[i
]->name
)!=0) {
248 wfree(menu
->entries
[i
]->text
);
249 strcpy(title
, scr
->workspaces
[i
]->name
);
250 menu
->entries
[i
]->text
= wstrdup(title
);
251 menu
->flags
.realized
= 0;
254 strcpy(title
, scr
->workspaces
[i
]->name
);
256 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
258 menu
->flags
.realized
= 0;
262 if (!menu
->flags
.realized
)
268 updateMakeShortcutMenu(WMenu
*menu
, WWindow
*wwin
)
270 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
278 buffer
= wmalloc(strlen(_("Set Shortcut"))+16);
280 for (i
=WO_ENTRIES
; i
<smenu
->entry_no
; i
++) {
282 int shortcutNo
= i
-WO_ENTRIES
;
283 WMenuEntry
*entry
= smenu
->entries
[i
];
284 WMBag
*shortSelWindows
= wwin
->screen_ptr
->shortcutWindows
[shortcutNo
];
286 sprintf(buffer
, "%s %i", _("Set Shortcut"), shortcutNo
+1);
288 if (!shortSelWindows
) {
289 entry
->flags
.indicator_on
= 0;
291 entry
->flags
.indicator_on
= 1;
292 if (WMCountInBag(shortSelWindows
, wwin
))
293 entry
->flags
.indicator_type
= MI_DIAMOND
;
295 entry
->flags
.indicator_type
= MI_CHECK
;
298 if (strcmp(buffer
, entry
->text
)!=0) {
300 entry
->text
= wstrdup(buffer
);
301 smenu
->flags
.realized
= 0;
304 kcode
= wKeyBindings
[WKBD_WINDOW1
+shortcutNo
].keycode
;
307 if ((tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0)))
308 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
)!=0)) {
311 entry
->rtext
= wstrdup(tmp
);
312 smenu
->flags
.realized
= 0;
314 wMenuSetEnabled(smenu
, i
, True
);
316 wMenuSetEnabled(smenu
, i
, False
);
320 smenu
->flags
.realized
= 0;
323 entry
->clientdata
= wwin
;
326 if (!smenu
->flags
.realized
)
332 updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
)
334 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
336 /* keep on top check */
337 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
338 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
339 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
)?1:0;
340 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
342 /* keep at bottom check */
343 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
344 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
345 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
)?1:0;
346 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
348 /* omnipresent check */
349 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
350 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
352 smenu
->flags
.realized
= 0;
358 makeWorkspaceMenu(WScreen
*scr
)
362 menu
= wMenuCreate(scr
, NULL
, False
);
364 wwarning(_("could not create submenu for window menu"));
368 updateWorkspaceMenu(menu
);
375 makeMakeShortcutMenu(WScreen
*scr
, WMenu
*menu
)
382 menu = wMenuCreate(scr, NULL, False);
384 wwarning(_("could not create submenu for window menu"));
389 for (i
=0; i
<MAX_WINDOW_SHORTCUTS
; i
++) {
391 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
393 entry
->flags
.indicator
= 1;
402 makeOptionsMenu(WScreen
*scr
)
407 menu
= wMenuCreate(scr
, NULL
, False
);
409 wwarning(_("could not create submenu for window menu"));
413 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
,
415 entry
->flags
.indicator
= 1;
416 entry
->flags
.indicator_type
= MI_CHECK
;
418 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
,
420 entry
->flags
.indicator
= 1;
421 entry
->flags
.indicator_type
= MI_CHECK
;
423 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
,
425 entry
->flags
.indicator
= 1;
426 entry
->flags
.indicator_type
= MI_CHECK
;
433 createWindowMenu(WScreen
*scr
)
440 menu
= wMenuCreate(scr
, NULL
, False
);
442 * Warning: If you make some change that affects the order of the
443 * entries, you must update the command #defines in the top of
446 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
447 if (wKeyBindings
[WKBD_MAXIMIZE
].keycode
!=0) {
448 kcode
= wKeyBindings
[WKBD_MAXIMIZE
].keycode
;
450 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
451 entry
->rtext
= wstrdup(tmp
);
454 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
456 if (wKeyBindings
[WKBD_MINIATURIZE
].keycode
!=0) {
457 kcode
= wKeyBindings
[WKBD_MINIATURIZE
].keycode
;
459 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
460 entry
->rtext
= wstrdup(tmp
);
463 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
464 if (wKeyBindings
[WKBD_SHADE
].keycode
!=0) {
465 kcode
= wKeyBindings
[WKBD_SHADE
].keycode
;
467 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
468 entry
->rtext
= wstrdup(tmp
);
471 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
472 if (wKeyBindings
[WKBD_HIDE
].keycode
!=0) {
473 kcode
= wKeyBindings
[WKBD_HIDE
].keycode
;
475 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
476 entry
->rtext
= wstrdup(tmp
);
479 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
480 if (wKeyBindings
[WKBD_MOVERESIZE
].keycode
!=0) {
481 kcode
= wKeyBindings
[WKBD_MOVERESIZE
].keycode
;
483 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
484 entry
->rtext
= wstrdup(tmp
);
487 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
488 if (wKeyBindings
[WKBD_SELECT
].keycode
!=0) {
489 kcode
= wKeyBindings
[WKBD_SELECT
].keycode
;
491 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
492 entry
->rtext
= wstrdup(tmp
);
495 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
496 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
497 if (scr
->workspace_submenu
)
498 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
500 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
502 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
503 wMenuEntrySetCascade(menu
, entry
,
504 makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
507 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
508 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
511 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
512 if (wKeyBindings
[WKBD_CLOSE
].keycode
!=0) {
513 kcode
= wKeyBindings
[WKBD_CLOSE
].keycode
;
514 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
515 entry
->rtext
= wstrdup(tmp
);
518 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
525 CloseWindowMenu(WScreen
*scr
)
527 if (scr
->window_menu
) {
528 if (scr
->window_menu
->flags
.mapped
)
529 wMenuUnmap(scr
->window_menu
);
531 if (scr
->window_menu
->entries
[0]->clientdata
) {
532 WWindow
*wwin
= (WWindow
*)scr
->window_menu
->entries
[0]->clientdata
;
534 wwin
->flags
.menu_open_for_me
= 0;
536 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
543 updateMenuForWindow(WMenu
*menu
, WWindow
*wwin
)
545 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
546 WScreen
*scr
= wwin
->screen_ptr
;
549 updateOptionsMenu(menu
, wwin
);
551 updateMakeShortcutMenu(menu
, wwin
);
553 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!=NULL
554 && !WFLAGP(wapp
->main_window_desc
, no_appicon
));
556 wMenuSetEnabled(menu
, MC_CLOSE
,
557 (wwin
->protocols
.DELETE_WINDOW
558 && !WFLAGP(wwin
, no_closable
)));
560 if (wwin
->flags
.miniaturized
) {
561 static char *text
= NULL
;
562 if (!text
) text
= _("Deminiaturize");
564 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
566 static char *text
= NULL
;
567 if (!text
) text
= _("Miniaturize");
569 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
572 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
574 if (wwin
->flags
.maximized
) {
575 static char *text
= NULL
;
576 if (!text
) text
= _("Unmaximize");
578 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
580 static char *text
= NULL
;
581 if (!text
) text
= _("Maximize");
583 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
585 wMenuSetEnabled(menu
, MC_MAXIMIZE
, !WFLAGP(wwin
, no_resizable
));
588 wMenuSetEnabled(menu
, MC_MOVERESIZE
, !WFLAGP(wwin
, no_resizable
)
589 && !wwin
->flags
.miniaturized
);
591 if (wwin
->flags
.shaded
) {
592 static char *text
= NULL
;
593 if (!text
) text
= _("Unshade");
595 menu
->entries
[MC_SHADE
]->text
= text
;
597 static char *text
= NULL
;
598 if (!text
) text
= _("Shade");
600 menu
->entries
[MC_SHADE
]->text
= text
;
603 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
604 && !wwin
->flags
.miniaturized
);
606 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
608 if (!wwin
->flags
.inspector_open
) {
609 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
611 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
614 /* set the client data of the entries to the window */
615 for (i
= 0; i
< menu
->entry_no
; i
++) {
616 menu
->entries
[i
]->clientdata
= wwin
;
619 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
620 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
621 if (i
== scr
->current_workspace
) {
622 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
624 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
628 menu
->flags
.realized
= 0;
634 OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
637 WScreen
*scr
= wwin
->screen_ptr
;
639 wwin
->flags
.menu_open_for_me
= 1;
641 if (!scr
->window_menu
) {
642 scr
->window_menu
= createWindowMenu(scr
);
644 /* hack to save some memory allocation/deallocation */
645 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
646 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
647 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
649 updateWorkspaceMenu(scr
->workspace_submenu
);
652 menu
= scr
->window_menu
;
653 if (menu
->flags
.mapped
) {
655 if (menu
->entries
[0]->clientdata
==wwin
) {
660 updateMenuForWindow(menu
, wwin
);
662 x
-= menu
->frame
->core
->width
/2;
663 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+wwin
->frame
->core
->width
)
664 x
= wwin
->frame_x
+wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
665 if (x
< wwin
->frame_x
)
668 if (!wwin
->flags
.internal_window
)
669 wMenuMapAt(menu
, x
, y
, keyboard
);
674 OpenMiniwindowMenu(WWindow
*wwin
, int x
, int y
)
677 WScreen
*scr
= wwin
->screen_ptr
;
679 wwin
->flags
.menu_open_for_me
= 1;
681 if (!scr
->window_menu
) {
682 scr
->window_menu
= createWindowMenu(scr
);
684 /* hack to save some memory allocation/deallocation */
685 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
686 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
687 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
689 updateWorkspaceMenu(scr
->workspace_submenu
);
692 menu
= scr
->window_menu
;
693 if (menu
->flags
.mapped
) {
695 if (menu
->entries
[0]->clientdata
==wwin
) {
700 updateMenuForWindow(menu
, wwin
);
702 x
-= menu
->frame
->core
->width
/2;
704 wMenuMapAt(menu
, x
, y
, False
);