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 WShortKey wKeyBindings
[WKBD_LAST
];
75 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
);
77 static void execWindowOptionCommand(WMenu
* menu
, WMenuEntry
* entry
)
79 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
81 switch (entry
->order
) {
83 if (wwin
->frame
->core
->stacking
->window_level
!= WMFloatingLevel
)
84 ChangeStackingLevel(wwin
->frame
->core
, WMFloatingLevel
);
86 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
89 case WO_KEEP_AT_BOTTOM
:
90 if (wwin
->frame
->core
->stacking
->window_level
!= WMSunkenLevel
)
91 ChangeStackingLevel(wwin
->frame
->core
, WMSunkenLevel
);
93 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
97 wWindowSetOmnipresent(wwin
, !wwin
->flags
.omnipresent
);
102 static void execMenuCommand(WMenu
* menu
, WMenuEntry
* entry
)
104 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
107 CloseWindowMenu(menu
->frame
->screen_ptr
);
109 switch (entry
->order
) {
111 /* send delete message */
112 wClientSendProtocol(wwin
, w_global
.atom
.wm
.delete_window
,
113 w_global
.timestamp
.last_event
);
118 if (wPreferences
.dont_confirm_kill
119 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
121 ("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
122 _("Yes"), _("No"), NULL
) == WAPRDefault
) {
123 if (!wwin
->flags
.destroyed
)
130 if (wwin
->flags
.miniaturized
) {
131 wDeiconifyWindow(wwin
);
133 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
134 wClientSendProtocol(wwin
, w_global
.atom
.gnustep
.wm_miniaturize_window
,
135 w_global
.timestamp
.last_event
);
137 wIconifyWindow(wwin
);
143 if (wwin
->flags
.maximized
)
144 wUnmaximizeWindow(wwin
);
146 wMaximizeWindow(wwin
, MAX_VERTICAL
| MAX_HORIZONTAL
);
150 if (wwin
->flags
.shaded
)
151 wUnshadeWindow(wwin
);
157 if (!wwin
->flags
.miniaturized
)
158 wSelectWindow(wwin
, !wwin
->flags
.selected
);
160 wIconSelect(wwin
->icon
);
164 wKeyboardMoveResizeWindow(wwin
);
168 wShowInspectorForWindow(wwin
);
172 (void) RelaunchWindow(wwin
);
176 wapp
= wApplicationOf(wwin
->main_window
);
177 wHideApplication(wapp
);
182 static void switchWSCommand(WMenu
* menu
, WMenuEntry
* entry
)
184 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
186 wSelectWindow(wwin
, False
);
187 wWindowChangeWorkspace(wwin
, entry
->order
);
190 static void makeShortcutCommand(WMenu
*menu
, WMenuEntry
*entry
)
192 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
193 WScreen
*scr
= wwin
->screen_ptr
;
194 int index
= entry
->order
- WO_ENTRIES
;
196 if (w_global
.shortcut
.windows
[index
]) {
197 WMFreeArray(w_global
.shortcut
.windows
[index
]);
198 w_global
.shortcut
.windows
[index
] = NULL
;
201 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
202 w_global
.shortcut
.windows
[index
] = WMDuplicateArray(scr
->selected_windows
);
204 w_global
.shortcut
.windows
[index
] = WMCreateArray(4);
205 WMAddToArray(w_global
.shortcut
.windows
[index
], wwin
);
208 wSelectWindow(wwin
, !wwin
->flags
.selected
);
211 wSelectWindow(wwin
, !wwin
->flags
.selected
);
215 static void updateWorkspaceMenu(WMenu
* menu
)
217 char title
[MAX_WORKSPACENAME_WIDTH
+ 1];
221 for (i
= 0; i
< w_global
.workspace
.count
; i
++) {
222 if (i
< menu
->entry_no
) {
224 entry
= menu
->entries
[i
];
225 if (strcmp(entry
->text
, w_global
.workspace
.array
[i
]->name
) != 0) {
227 strncpy(title
, w_global
.workspace
.array
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
228 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
229 menu
->entries
[i
]->text
= wstrdup(title
);
230 menu
->entries
[i
]->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVE_WORKSPACE1
+ i
]);
231 menu
->flags
.realized
= 0;
234 strncpy(title
, w_global
.workspace
.array
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
235 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
237 entry
= wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
238 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVE_WORKSPACE1
+ i
]);
240 menu
->flags
.realized
= 0;
243 /* workspace shortcut labels */
244 if (i
/ 10 == w_global
.workspace
.current
/ 10)
245 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVE_WORKSPACE1
+ (i
% 10)]);
250 if (!menu
->flags
.realized
)
254 static void updateMakeShortcutMenu(WMenu
*menu
, WWindow
*wwin
)
256 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
265 buflen
= strlen(_("Set Shortcut")) + 16;
266 buffer
= wmalloc(buflen
);
268 for (i
= WO_ENTRIES
; i
< smenu
->entry_no
; i
++) {
270 int shortcutNo
= i
- WO_ENTRIES
;
271 WMenuEntry
*entry
= smenu
->entries
[i
];
272 WMArray
*shortSelWindows
= w_global
.shortcut
.windows
[shortcutNo
];
274 snprintf(buffer
, buflen
, "%s %i", _("Set Shortcut"), shortcutNo
+ 1);
276 if (!shortSelWindows
) {
277 entry
->flags
.indicator_on
= 0;
279 entry
->flags
.indicator_on
= 1;
280 if (WMCountInArray(shortSelWindows
, wwin
))
281 entry
->flags
.indicator_type
= MI_DIAMOND
;
283 entry
->flags
.indicator_type
= MI_CHECK
;
286 if (strcmp(buffer
, entry
->text
) != 0) {
288 entry
->text
= wstrdup(buffer
);
289 smenu
->flags
.realized
= 0;
292 kcode
= wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
].keycode
;
295 if ((tmp
= GetShortcutKey(wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
]))
296 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
) != 0)) {
300 smenu
->flags
.realized
= 0;
302 wMenuSetEnabled(smenu
, i
, True
);
304 wMenuSetEnabled(smenu
, i
, False
);
308 smenu
->flags
.realized
= 0;
311 entry
->clientdata
= wwin
;
314 if (!smenu
->flags
.realized
)
318 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
)
320 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
322 /* keep on top check */
323 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
324 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
325 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
) ? 1 : 0;
326 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
328 /* keep at bottom check */
329 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
330 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
331 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
) ? 1 : 0;
332 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
334 /* omnipresent check */
335 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
336 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
338 smenu
->flags
.realized
= 0;
342 static WMenu
*makeWorkspaceMenu(WScreen
* scr
)
346 menu
= wMenuCreate(scr
, NULL
, False
);
348 wwarning(_("could not create submenu for window menu"));
352 updateWorkspaceMenu(menu
);
357 static WMenu
*makeMakeShortcutMenu(WMenu
*menu
)
361 for (i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
363 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
365 entry
->flags
.indicator
= 1;
371 static WMenu
*makeOptionsMenu(WScreen
* scr
)
376 menu
= wMenuCreate(scr
, NULL
, False
);
378 wwarning(_("could not create submenu for window menu"));
382 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
, NULL
);
383 entry
->flags
.indicator
= 1;
384 entry
->flags
.indicator_type
= MI_CHECK
;
386 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
, NULL
);
387 entry
->flags
.indicator
= 1;
388 entry
->flags
.indicator_type
= MI_CHECK
;
390 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
, NULL
);
391 entry
->flags
.indicator
= 1;
392 entry
->flags
.indicator_type
= MI_CHECK
;
397 static WMenu
*createWindowMenu(WScreen
* scr
)
402 menu
= wMenuCreate(scr
, NULL
, False
);
404 * Warning: If you make some change that affects the order of the
405 * entries, you must update the command #defines in the top of
408 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
409 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MAXIMIZE
]);
411 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
412 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MINIATURIZE
]);
414 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
415 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_SHADE
]);
417 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
418 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_HIDE
]);
420 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
421 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_MOVERESIZE
]);
423 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
424 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_SELECT
]);
426 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
427 w_global
.workspace
.submenu
= makeWorkspaceMenu(scr
);
428 if (w_global
.workspace
.submenu
)
429 wMenuEntrySetCascade(menu
, entry
, w_global
.workspace
.submenu
);
431 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
433 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
434 wMenuEntrySetCascade(menu
, entry
, makeMakeShortcutMenu(makeOptionsMenu(scr
)));
436 entry
= wMenuAddCallback(menu
, _("Launch"), execMenuCommand
, NULL
);
437 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_RELAUNCH
]);
439 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
440 entry
->rtext
= GetShortcutKey(wKeyBindings
[WKBD_CLOSE
]);
442 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
447 void CloseWindowMenu(WScreen
* scr
)
449 if (scr
->window_menu
) {
450 if (scr
->window_menu
->flags
.mapped
)
451 wMenuUnmap(scr
->window_menu
);
453 if (scr
->window_menu
->entries
[0]->clientdata
) {
454 WWindow
*wwin
= (WWindow
*) scr
->window_menu
->entries
[0]->clientdata
;
456 wwin
->flags
.menu_open_for_me
= 0;
458 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
462 static void updateMenuForWindow(WMenu
* menu
, WWindow
* wwin
)
464 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
467 updateOptionsMenu(menu
, wwin
);
469 updateMakeShortcutMenu(menu
, wwin
);
471 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!= NULL
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
));
473 wMenuSetEnabled(menu
, MC_CLOSE
, (wwin
->protocols
.DELETE_WINDOW
&& !WFLAGP(wwin
, no_closable
)));
475 if (wwin
->flags
.miniaturized
) {
476 static char *text
= NULL
;
478 text
= _("Deminiaturize");
480 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
482 static char *text
= NULL
;
484 text
= _("Miniaturize");
486 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
489 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
491 if (wwin
->flags
.maximized
) {
492 static char *text
= NULL
;
494 text
= _("Unmaximize");
496 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
498 static char *text
= NULL
;
500 text
= _("Maximize");
502 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
504 wMenuSetEnabled(menu
, MC_MAXIMIZE
, IS_RESIZABLE(wwin
));
506 wMenuSetEnabled(menu
, MC_MOVERESIZE
, IS_RESIZABLE(wwin
)
507 && !wwin
->flags
.miniaturized
);
509 if (wwin
->flags
.shaded
) {
510 static char *text
= NULL
;
514 menu
->entries
[MC_SHADE
]->text
= text
;
516 static char *text
= NULL
;
520 menu
->entries
[MC_SHADE
]->text
= text
;
523 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
524 && !wwin
->flags
.miniaturized
);
526 if (wwin
->flags
.selected
) {
527 static char *text
= NULL
;
529 text
= _("Deselect");
531 menu
->entries
[MC_SELECT
]->text
= text
;
533 static char *text
= NULL
;
537 menu
->entries
[MC_SELECT
]->text
= text
;
540 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
542 if (!wwin
->flags
.inspector_open
) {
543 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
545 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
548 /* set the client data of the entries to the window */
549 for (i
= 0; i
< menu
->entry_no
; i
++) {
550 menu
->entries
[i
]->clientdata
= wwin
;
553 for (i
= 0; i
< w_global
.workspace
.submenu
->entry_no
; i
++) {
554 w_global
.workspace
.submenu
->entries
[i
]->clientdata
= wwin
;
556 if (i
== w_global
.workspace
.current
)
557 wMenuSetEnabled(w_global
.workspace
.submenu
, i
, False
);
559 wMenuSetEnabled(w_global
.workspace
.submenu
, i
, True
);
562 menu
->flags
.realized
= 0;
566 static WMenu
*open_window_menu_core(WWindow
*wwin
, int x
, int y
)
568 WScreen
*scr
= wwin
->screen_ptr
;
571 wwin
->flags
.menu_open_for_me
= 1;
573 if (!scr
->window_menu
) {
574 scr
->window_menu
= createWindowMenu(scr
);
576 /* hack to save some memory allocation/deallocation */
577 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
578 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
579 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
580 wfree(scr
->window_menu
->entries
[MC_SELECT
]->text
);
582 updateWorkspaceMenu(w_global
.workspace
.submenu
);
585 menu
= scr
->window_menu
;
586 if (menu
->flags
.mapped
) {
588 if (menu
->entries
[0]->clientdata
== wwin
)
592 updateMenuForWindow(menu
, wwin
);
597 static void prepare_menu_position(WMenu
*menu
, int x
, int y
)
601 rect
= wGetRectForHead(menu
->frame
->screen_ptr
,
602 wGetHeadForPointerLocation(menu
->frame
->screen_ptr
));
603 if (x
< rect
.pos
.x
- menu
->frame
->core
->width
/ 2)
604 x
= rect
.pos
.x
- menu
->frame
->core
->width
/ 2;
609 void OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
613 menu
= open_window_menu_core(wwin
, x
, y
);
617 /* Specific menu position */
618 x
-= menu
->frame
->core
->width
/ 2;
619 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+ wwin
->frame
->core
->width
)
620 x
= wwin
->frame_x
+ wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
621 if (x
< wwin
->frame_x
)
624 /* Common menu position */
625 prepare_menu_position(menu
, x
, y
);
627 if (!wwin
->flags
.internal_window
)
628 wMenuMapAt(menu
, x
, y
, keyboard
);
631 void OpenWindowMenu2(WWindow
*wwin
, int x
, int y
, int keyboard
)
636 menu
= open_window_menu_core(wwin
, x
, y
);
640 /* Specific menu position */
641 for (i
= 0; i
< w_global
.workspace
.submenu
->entry_no
; i
++) {
642 w_global
.workspace
.submenu
->entries
[i
]->clientdata
= wwin
;
643 wMenuSetEnabled(w_global
.workspace
.submenu
, i
, True
);
646 x
-= menu
->frame
->core
->width
/ 2;
648 /* Common menu position */
649 prepare_menu_position(menu
, x
, y
);
651 if (!wwin
->flags
.internal_window
)
652 wMenuMapAt(menu
, x
, y
, keyboard
);
655 void OpenMiniwindowMenu(WWindow
* wwin
, int x
, int y
)
659 menu
= open_window_menu_core(wwin
, x
, y
);
663 x
-= menu
->frame
->core
->width
/ 2;
665 wMenuMapAt(menu
, x
, y
, False
);
668 void DestroyWindowMenu(WScreen
*scr
)
670 if (scr
->window_menu
) {
671 scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
= NULL
;
672 scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
= NULL
;
673 scr
->window_menu
->entries
[MC_SHADE
]->text
= NULL
;
674 scr
->window_menu
->entries
[MC_SELECT
]->text
= NULL
;
675 wMenuDestroy(scr
->window_menu
, True
);
676 scr
->window_menu
= NULL
;