1 /* winmenu.c - command menu for windows
3 * Window Maker window manager
5 * Copyright (c) 1997-2003 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 along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <X11/Xutil.h>
31 #include <X11/XKBlib.h>
33 #include "WindowMaker.h"
39 #include "application.h"
43 #include "workspace.h"
44 #include "winspector.h"
52 #define MC_MINIATURIZE 1
55 #define MC_MOVERESIZE 4
57 #define MC_DUMMY_MOVETO 6
58 #define MC_PROPERTIES 7
66 #define WO_KEEP_ON_TOP 0
67 #define WO_KEEP_AT_BOTTOM 1
68 #define WO_OMNIPRESENT 2
71 /**** Global data ***/
72 extern Time LastTimestamp
;
73 extern Atom _XA_WM_DELETE_WINDOW
;
74 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
;
76 extern WShortKey wKeyBindings
[WKBD_LAST
];
79 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
);
81 static void 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 wWindowSetOmnipresent(wwin
, !wwin
->flags
.omnipresent
);
106 static void execMenuCommand(WMenu
* menu
, WMenuEntry
* entry
)
108 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
111 CloseWindowMenu(menu
->frame
->screen_ptr
);
113 switch (entry
->order
) {
115 /* send delete message */
116 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
121 if (wPreferences
.dont_confirm_kill
122 || 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
, LastTimestamp
);
139 wIconifyWindow(wwin
);
145 if (wwin
->flags
.maximized
)
146 wUnmaximizeWindow(wwin
);
148 wMaximizeWindow(wwin
, MAX_VERTICAL
| MAX_HORIZONTAL
);
152 if (wwin
->flags
.shaded
)
153 wUnshadeWindow(wwin
);
159 if (!wwin
->flags
.miniaturized
)
160 wSelectWindow(wwin
, !wwin
->flags
.selected
);
162 wIconSelect(wwin
->icon
);
166 wKeyboardMoveResizeWindow(wwin
);
170 wShowInspectorForWindow(wwin
);
174 (void) RelaunchWindow(wwin
);
178 wapp
= wApplicationOf(wwin
->main_window
);
179 wHideApplication(wapp
);
184 static void switchWSCommand(WMenu
* menu
, WMenuEntry
* entry
)
186 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
188 wSelectWindow(wwin
, False
);
189 wWindowChangeWorkspace(wwin
, entry
->order
);
192 static void makeShortcutCommand(WMenu
* menu
, WMenuEntry
* entry
)
194 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
195 WScreen
*scr
= wwin
->screen_ptr
;
196 int index
= entry
->order
- WO_ENTRIES
;
198 if (scr
->shortcutWindows
[index
]) {
199 WMFreeArray(scr
->shortcutWindows
[index
]);
200 scr
->shortcutWindows
[index
] = NULL
;
203 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
204 scr
->shortcutWindows
[index
] = WMDuplicateArray(scr
->selected_windows
);
205 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
206 WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
208 scr
->shortcutWindows
[index
] = WMCreateArray(4);
209 WMAddToArray(scr
->shortcutWindows
[index
], wwin
);
212 wSelectWindow(wwin
, !wwin
->flags
.selected
);
215 wSelectWindow(wwin
, !wwin
->flags
.selected
);
219 static void updateWorkspaceMenu(WMenu
* menu
)
221 WScreen
*scr
= menu
->frame
->screen_ptr
;
222 char title
[MAX_WORKSPACENAME_WIDTH
+ 1];
226 for (i
= 0; i
< scr
->workspace_count
; i
++) {
227 if (i
< menu
->entry_no
) {
229 entry
= menu
->entries
[i
];
230 if (strcmp(entry
->text
, scr
->workspaces
[i
]->name
) != 0) {
232 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
233 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
234 menu
->entries
[i
]->text
= wstrdup(title
);
235 menu
->entries
[i
]->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVE_WORKSPACE1
+ i
]);
236 menu
->flags
.realized
= 0;
239 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
240 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
242 entry
= wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
243 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVE_WORKSPACE1
+ i
]);
245 menu
->flags
.realized
= 0;
248 /* workspace shortcut labels */
249 if (i
/ 10 == scr
->current_workspace
/ 10)
250 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVE_WORKSPACE1
+ (i
% 10)]);
255 if (!menu
->flags
.realized
)
259 static void updateMakeShortcutMenu(WMenu
* menu
, WWindow
* wwin
)
261 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
270 buflen
= strlen(_("Set Shortcut")) + 16;
271 buffer
= wmalloc(buflen
);
273 for (i
= WO_ENTRIES
; i
< smenu
->entry_no
; i
++) {
275 int shortcutNo
= i
- WO_ENTRIES
;
276 WMenuEntry
*entry
= smenu
->entries
[i
];
277 WMArray
*shortSelWindows
= wwin
->screen_ptr
->shortcutWindows
[shortcutNo
];
279 snprintf(buffer
, buflen
, "%s %i", _("Set Shortcut"), shortcutNo
+ 1);
281 if (!shortSelWindows
) {
282 entry
->flags
.indicator_on
= 0;
284 entry
->flags
.indicator_on
= 1;
285 if (WMCountInArray(shortSelWindows
, wwin
))
286 entry
->flags
.indicator_type
= MI_DIAMOND
;
288 entry
->flags
.indicator_type
= MI_CHECK
;
291 if (strcmp(buffer
, entry
->text
) != 0) {
293 entry
->text
= wstrdup(buffer
);
294 smenu
->flags
.realized
= 0;
297 kcode
= wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
].keycode
;
300 if ((tmp
= GetShortcutKey(wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
]))
301 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
) != 0)) {
305 smenu
->flags
.realized
= 0;
307 wMenuSetEnabled(smenu
, i
, True
);
309 wMenuSetEnabled(smenu
, i
, False
);
313 smenu
->flags
.realized
= 0;
316 entry
->clientdata
= wwin
;
319 if (!smenu
->flags
.realized
)
323 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
)
325 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
327 /* keep on top check */
328 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
329 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
330 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
) ? 1 : 0;
331 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
333 /* keep at bottom check */
334 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
335 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
336 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
) ? 1 : 0;
337 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
339 /* omnipresent check */
340 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
341 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
343 smenu
->flags
.realized
= 0;
347 static WMenu
*makeWorkspaceMenu(WScreen
* scr
)
351 menu
= wMenuCreate(scr
, NULL
, False
);
353 wwarning(_("could not create submenu for window menu"));
357 updateWorkspaceMenu(menu
);
362 static WMenu
*makeMakeShortcutMenu(WScreen
* scr
, WMenu
* menu
)
369 menu = wMenuCreate(scr, NULL, False);
371 wwarning(_("could not create submenu for window menu"));
376 for (i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
378 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
380 entry
->flags
.indicator
= 1;
386 static WMenu
*makeOptionsMenu(WScreen
* scr
)
391 menu
= wMenuCreate(scr
, NULL
, False
);
393 wwarning(_("could not create submenu for window menu"));
397 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
, NULL
);
398 entry
->flags
.indicator
= 1;
399 entry
->flags
.indicator_type
= MI_CHECK
;
401 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
, NULL
);
402 entry
->flags
.indicator
= 1;
403 entry
->flags
.indicator_type
= MI_CHECK
;
405 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
, NULL
);
406 entry
->flags
.indicator
= 1;
407 entry
->flags
.indicator_type
= MI_CHECK
;
412 static WMenu
*createWindowMenu(WScreen
* scr
)
417 menu
= wMenuCreate(scr
, NULL
, False
);
419 * Warning: If you make some change that affects the order of the
420 * entries, you must update the command #defines in the top of
423 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
424 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MAXIMIZE
]);
426 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
427 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MINIATURIZE
]);
429 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
430 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_SHADE
]);
432 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
433 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_HIDE
]);
435 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
436 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVERESIZE
]);
438 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
439 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_SELECT
]);
441 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
442 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
443 if (scr
->workspace_submenu
)
444 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
446 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
448 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
449 wMenuEntrySetCascade(menu
, entry
, makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
452 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
453 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
456 entry
= wMenuAddCallback(menu
, _("Launch"), execMenuCommand
, NULL
);
457 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_RELAUNCH
]);
459 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
460 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_CLOSE
]);
462 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
467 void CloseWindowMenu(WScreen
* scr
)
469 if (scr
->window_menu
) {
470 if (scr
->window_menu
->flags
.mapped
)
471 wMenuUnmap(scr
->window_menu
);
473 if (scr
->window_menu
->entries
[0]->clientdata
) {
474 WWindow
*wwin
= (WWindow
*) scr
->window_menu
->entries
[0]->clientdata
;
476 wwin
->flags
.menu_open_for_me
= 0;
478 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
482 static void updateMenuForWindow(WMenu
* menu
, WWindow
* wwin
)
484 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
485 WScreen
*scr
= wwin
->screen_ptr
;
488 updateOptionsMenu(menu
, wwin
);
490 updateMakeShortcutMenu(menu
, wwin
);
492 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!= NULL
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
));
494 wMenuSetEnabled(menu
, MC_CLOSE
, (wwin
->protocols
.DELETE_WINDOW
&& !WFLAGP(wwin
, no_closable
)));
496 if (wwin
->flags
.miniaturized
) {
497 static char *text
= NULL
;
499 text
= _("Deminiaturize");
501 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
503 static char *text
= NULL
;
505 text
= _("Miniaturize");
507 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
510 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
512 if (wwin
->flags
.maximized
) {
513 static char *text
= NULL
;
515 text
= _("Unmaximize");
517 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
519 static char *text
= NULL
;
521 text
= _("Maximize");
523 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
525 wMenuSetEnabled(menu
, MC_MAXIMIZE
, IS_RESIZABLE(wwin
));
527 wMenuSetEnabled(menu
, MC_MOVERESIZE
, IS_RESIZABLE(wwin
)
528 && !wwin
->flags
.miniaturized
);
530 if (wwin
->flags
.shaded
) {
531 static char *text
= NULL
;
535 menu
->entries
[MC_SHADE
]->text
= text
;
537 static char *text
= NULL
;
541 menu
->entries
[MC_SHADE
]->text
= text
;
544 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
545 && !wwin
->flags
.miniaturized
);
547 if (wwin
->flags
.selected
) {
548 static char *text
= NULL
;
550 text
= _("Deselect");
552 menu
->entries
[MC_SELECT
]->text
= text
;
554 static char *text
= NULL
;
558 menu
->entries
[MC_SELECT
]->text
= text
;
561 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
563 if (!wwin
->flags
.inspector_open
) {
564 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
566 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
569 /* set the client data of the entries to the window */
570 for (i
= 0; i
< menu
->entry_no
; i
++) {
571 menu
->entries
[i
]->clientdata
= wwin
;
574 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
575 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
576 if (i
== scr
->current_workspace
) {
577 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
579 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
583 menu
->flags
.realized
= 0;
587 static WMenu
*open_window_menu_core(WWindow
*wwin
, int x
, int y
)
589 WScreen
*scr
= wwin
->screen_ptr
;
592 wwin
->flags
.menu_open_for_me
= 1;
594 if (!scr
->window_menu
) {
595 scr
->window_menu
= createWindowMenu(scr
);
597 /* hack to save some memory allocation/deallocation */
598 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
599 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
600 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
601 wfree(scr
->window_menu
->entries
[MC_SELECT
]->text
);
603 updateWorkspaceMenu(scr
->workspace_submenu
);
606 menu
= scr
->window_menu
;
607 if (menu
->flags
.mapped
) {
609 if (menu
->entries
[0]->clientdata
== wwin
)
613 updateMenuForWindow(menu
, wwin
);
618 static void prepare_menu_position(WMenu
*menu
, int x
, int y
)
622 rect
= wGetRectForHead(menu
->frame
->screen_ptr
,
623 wGetHeadForPointerLocation(menu
->frame
->screen_ptr
));
624 if (x
< rect
.pos
.x
- menu
->frame
->core
->width
/ 2)
625 x
= rect
.pos
.x
- menu
->frame
->core
->width
/ 2;
630 void OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
634 menu
= open_window_menu_core(wwin
, x
, y
);
638 /* Specific menu position */
639 x
-= menu
->frame
->core
->width
/ 2;
640 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+ wwin
->frame
->core
->width
)
641 x
= wwin
->frame_x
+ wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
642 if (x
< wwin
->frame_x
)
645 /* Common menu position */
646 prepare_menu_position(menu
, x
, y
);
648 if (!wwin
->flags
.internal_window
)
649 wMenuMapAt(menu
, x
, y
, keyboard
);
652 void OpenWindowMenu2(WWindow
*wwin
, int x
, int y
, int keyboard
)
656 WScreen
*scr
= wwin
->screen_ptr
;
658 menu
= open_window_menu_core(wwin
, x
, y
);
662 /* Specific menu position */
663 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
664 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
665 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
668 x
-= menu
->frame
->core
->width
/ 2;
670 /* Common menu position */
671 prepare_menu_position(menu
, x
, y
);
673 if (!wwin
->flags
.internal_window
)
674 wMenuMapAt(menu
, x
, y
, keyboard
);
677 void OpenMiniwindowMenu(WWindow
* wwin
, int x
, int y
)
681 menu
= open_window_menu_core(wwin
, x
, y
);
685 x
-= menu
->frame
->core
->width
/ 2;
687 wMenuMapAt(menu
, x
, y
, False
);
690 void DestroyWindowMenu(WScreen
*scr
)
692 if (scr
->window_menu
) {
693 scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
= NULL
;
694 scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
= NULL
;
695 scr
->window_menu
->entries
[MC_SHADE
]->text
= NULL
;
696 scr
->window_menu
->entries
[MC_SELECT
]->text
= NULL
;
697 wMenuDestroy(scr
->window_menu
, True
);
698 scr
->window_menu
= NULL
;