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"
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
);
80 static void execWindowOptionCommand(WMenu
* menu
, WMenuEntry
* entry
)
82 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
84 switch (entry
->order
) {
86 if (wwin
->frame
->core
->stacking
->window_level
!= WMFloatingLevel
)
87 ChangeStackingLevel(wwin
->frame
->core
, WMFloatingLevel
);
89 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
92 case WO_KEEP_AT_BOTTOM
:
93 if (wwin
->frame
->core
->stacking
->window_level
!= WMSunkenLevel
)
94 ChangeStackingLevel(wwin
->frame
->core
, WMSunkenLevel
);
96 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
100 wWindowSetOmnipresent(wwin
, !wwin
->flags
.omnipresent
);
105 static void execMenuCommand(WMenu
* menu
, WMenuEntry
* entry
)
107 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
110 CloseWindowMenu(menu
->frame
->screen_ptr
);
112 switch (entry
->order
) {
114 /* send delete message */
115 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
120 if (wPreferences
.dont_confirm_kill
121 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
123 ("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
124 _("Yes"), _("No"), NULL
) == WAPRDefault
) {
125 if (!wwin
->flags
.destroyed
)
132 if (wwin
->flags
.miniaturized
) {
133 wDeiconifyWindow(wwin
);
135 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
136 wClientSendProtocol(wwin
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
, LastTimestamp
);
138 wIconifyWindow(wwin
);
144 if (wwin
->flags
.maximized
)
145 wUnmaximizeWindow(wwin
);
147 wMaximizeWindow(wwin
, MAX_VERTICAL
| MAX_HORIZONTAL
);
151 if (wwin
->flags
.shaded
)
152 wUnshadeWindow(wwin
);
158 if (!wwin
->flags
.miniaturized
)
159 wSelectWindow(wwin
, !wwin
->flags
.selected
);
161 wIconSelect(wwin
->icon
);
165 wKeyboardMoveResizeWindow(wwin
);
169 wShowInspectorForWindow(wwin
);
173 (void) RelaunchWindow(wwin
);
177 wapp
= wApplicationOf(wwin
->main_window
);
178 wHideApplication(wapp
);
183 static void switchWSCommand(WMenu
* menu
, WMenuEntry
* entry
)
185 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
187 wSelectWindow(wwin
, False
);
188 wWindowChangeWorkspace(wwin
, entry
->order
);
191 static void makeShortcutCommand(WMenu
* menu
, WMenuEntry
* entry
)
193 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
194 WScreen
*scr
= wwin
->screen_ptr
;
195 int index
= entry
->order
- WO_ENTRIES
;
197 if (scr
->shortcutWindows
[index
]) {
198 WMFreeArray(scr
->shortcutWindows
[index
]);
199 scr
->shortcutWindows
[index
] = NULL
;
202 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
203 scr
->shortcutWindows
[index
] = WMDuplicateArray(scr
->selected_windows
);
204 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
205 WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
207 scr
->shortcutWindows
[index
] = WMCreateArray(4);
208 WMAddToArray(scr
->shortcutWindows
[index
], wwin
);
211 wSelectWindow(wwin
, !wwin
->flags
.selected
);
214 wSelectWindow(wwin
, !wwin
->flags
.selected
);
218 static void updateWorkspaceMenu(WMenu
* menu
)
220 WScreen
*scr
= menu
->frame
->screen_ptr
;
221 char title
[MAX_WORKSPACENAME_WIDTH
+ 1];
224 for (i
= 0; i
< scr
->workspace_count
; i
++) {
225 if (i
< menu
->entry_no
) {
226 if (strcmp(menu
->entries
[i
]->text
, scr
->workspaces
[i
]->name
) != 0) {
227 wfree(menu
->entries
[i
]->text
);
228 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
229 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
230 menu
->entries
[i
]->text
= wstrdup(title
);
231 menu
->flags
.realized
= 0;
234 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
235 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
237 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
239 menu
->flags
.realized
= 0;
243 if (!menu
->flags
.realized
)
247 static char *getShortcutString(WShortKey key
)
250 char *k
= XKeysymToString(XkbKeycodeToKeysym(dpy
, key
.keycode
, 0, 0));
253 char **m
= wPreferences
.modifier_labels
;
254 if (key
.modifier
& ControlMask
) tmp
= wstrappend(tmp
, m
[1] ? m
[1] : "Ctrl+");
255 if (key
.modifier
& ShiftMask
) tmp
= wstrappend(tmp
, m
[0] ? m
[0] : "Shift+");
256 if (key
.modifier
& Mod1Mask
) tmp
= wstrappend(tmp
, m
[2] ? m
[2] : "Mod1+");
257 if (key
.modifier
& Mod2Mask
) tmp
= wstrappend(tmp
, m
[3] ? m
[3] : "Mod2+");
258 if (key
.modifier
& Mod3Mask
) tmp
= wstrappend(tmp
, m
[4] ? m
[4] : "Mod3+");
259 if (key
.modifier
& Mod4Mask
) tmp
= wstrappend(tmp
, m
[5] ? m
[5] : "Mod4+");
260 if (key
.modifier
& Mod5Mask
) tmp
= wstrappend(tmp
, m
[6] ? m
[6] : "Mod5+");
261 tmp
= wstrappend(tmp
, k
);
266 static void updateMakeShortcutMenu(WMenu
* menu
, WWindow
* wwin
)
268 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
277 buflen
= strlen(_("Set Shortcut")) + 16;
278 buffer
= wmalloc(buflen
);
280 for (i
= WO_ENTRIES
; i
< smenu
->entry_no
; i
++) {
282 int shortcutNo
= i
- WO_ENTRIES
;
283 WMenuEntry
*entry
= smenu
->entries
[i
];
284 WMArray
*shortSelWindows
= wwin
->screen_ptr
->shortcutWindows
[shortcutNo
];
286 snprintf(buffer
, buflen
, "%s %i", _("Set Shortcut"), shortcutNo
+ 1);
288 if (!shortSelWindows
) {
289 entry
->flags
.indicator_on
= 0;
291 entry
->flags
.indicator_on
= 1;
292 if (WMCountInArray(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
= getShortcutString(wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
]))
308 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
) != 0)) {
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
)
330 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
)
332 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
334 /* keep on top check */
335 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
336 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
337 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
) ? 1 : 0;
338 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
340 /* keep at bottom check */
341 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
342 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
343 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
) ? 1 : 0;
344 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
346 /* omnipresent check */
347 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
348 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
350 smenu
->flags
.realized
= 0;
354 static WMenu
*makeWorkspaceMenu(WScreen
* scr
)
358 menu
= wMenuCreate(scr
, NULL
, False
);
360 wwarning(_("could not create submenu for window menu"));
364 updateWorkspaceMenu(menu
);
369 static WMenu
*makeMakeShortcutMenu(WScreen
* scr
, WMenu
* menu
)
376 menu = wMenuCreate(scr, NULL, False);
378 wwarning(_("could not create submenu for window menu"));
383 for (i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
385 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
387 entry
->flags
.indicator
= 1;
393 static WMenu
*makeOptionsMenu(WScreen
* scr
)
398 menu
= wMenuCreate(scr
, NULL
, False
);
400 wwarning(_("could not create submenu for window menu"));
404 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
, NULL
);
405 entry
->flags
.indicator
= 1;
406 entry
->flags
.indicator_type
= MI_CHECK
;
408 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
, NULL
);
409 entry
->flags
.indicator
= 1;
410 entry
->flags
.indicator_type
= MI_CHECK
;
412 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
, NULL
);
413 entry
->flags
.indicator
= 1;
414 entry
->flags
.indicator_type
= MI_CHECK
;
419 static WMenu
*createWindowMenu(WScreen
* scr
)
424 menu
= wMenuCreate(scr
, NULL
, False
);
426 * Warning: If you make some change that affects the order of the
427 * entries, you must update the command #defines in the top of
430 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
431 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_MAXIMIZE
]);
433 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
434 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_MINIATURIZE
]);
436 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
437 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_SHADE
]);
439 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
440 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_HIDE
]);
442 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
443 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_MOVERESIZE
]);
445 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
446 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_SELECT
]);
448 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
449 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
450 if (scr
->workspace_submenu
)
451 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
453 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
455 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
456 wMenuEntrySetCascade(menu
, entry
, makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
459 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
460 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
463 entry
= wMenuAddCallback(menu
, _("Launch"), execMenuCommand
, NULL
);
464 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_RELAUNCH
]);
466 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
467 entry
->rtext
= getShortcutString(wKeyBindings
[WKBD_CLOSE
]);
469 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
474 void CloseWindowMenu(WScreen
* scr
)
476 if (scr
->window_menu
) {
477 if (scr
->window_menu
->flags
.mapped
)
478 wMenuUnmap(scr
->window_menu
);
480 if (scr
->window_menu
->entries
[0]->clientdata
) {
481 WWindow
*wwin
= (WWindow
*) scr
->window_menu
->entries
[0]->clientdata
;
483 wwin
->flags
.menu_open_for_me
= 0;
485 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
489 static void updateMenuForWindow(WMenu
* menu
, WWindow
* wwin
)
491 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
492 WScreen
*scr
= wwin
->screen_ptr
;
495 updateOptionsMenu(menu
, wwin
);
497 updateMakeShortcutMenu(menu
, wwin
);
499 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!= NULL
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
));
501 wMenuSetEnabled(menu
, MC_CLOSE
, (wwin
->protocols
.DELETE_WINDOW
&& !WFLAGP(wwin
, no_closable
)));
503 if (wwin
->flags
.miniaturized
) {
504 static char *text
= NULL
;
506 text
= _("Deminiaturize");
508 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
510 static char *text
= NULL
;
512 text
= _("Miniaturize");
514 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
517 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
519 if (wwin
->flags
.maximized
) {
520 static char *text
= NULL
;
522 text
= _("Unmaximize");
524 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
526 static char *text
= NULL
;
528 text
= _("Maximize");
530 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
532 wMenuSetEnabled(menu
, MC_MAXIMIZE
, IS_RESIZABLE(wwin
));
534 wMenuSetEnabled(menu
, MC_MOVERESIZE
, IS_RESIZABLE(wwin
)
535 && !wwin
->flags
.miniaturized
);
537 if (wwin
->flags
.shaded
) {
538 static char *text
= NULL
;
542 menu
->entries
[MC_SHADE
]->text
= text
;
544 static char *text
= NULL
;
548 menu
->entries
[MC_SHADE
]->text
= text
;
551 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
552 && !wwin
->flags
.miniaturized
);
554 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
556 if (!wwin
->flags
.inspector_open
) {
557 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
559 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
562 /* set the client data of the entries to the window */
563 for (i
= 0; i
< menu
->entry_no
; i
++) {
564 menu
->entries
[i
]->clientdata
= wwin
;
567 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
568 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
569 if (i
== scr
->current_workspace
) {
570 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
572 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
576 menu
->flags
.realized
= 0;
580 static WMenu
*open_window_menu_core(WWindow
*wwin
, int x
, int y
)
582 WScreen
*scr
= wwin
->screen_ptr
;
585 wwin
->flags
.menu_open_for_me
= 1;
587 if (!scr
->window_menu
) {
588 scr
->window_menu
= createWindowMenu(scr
);
590 /* hack to save some memory allocation/deallocation */
591 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
592 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
593 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
595 updateWorkspaceMenu(scr
->workspace_submenu
);
598 menu
= scr
->window_menu
;
599 if (menu
->flags
.mapped
) {
601 if (menu
->entries
[0]->clientdata
== wwin
)
605 updateMenuForWindow(menu
, wwin
);
610 static void prepare_menu_position(WMenu
*menu
, int x
, int y
)
614 rect
= wGetRectForHead(menu
->frame
->screen_ptr
,
615 wGetHeadForPointerLocation(menu
->frame
->screen_ptr
));
616 if (x
< rect
.pos
.x
- menu
->frame
->core
->width
/ 2)
617 x
= rect
.pos
.x
- menu
->frame
->core
->width
/ 2;
622 void OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
626 menu
= open_window_menu_core(wwin
, x
, y
);
630 /* Specific menu position */
631 x
-= menu
->frame
->core
->width
/ 2;
632 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+ wwin
->frame
->core
->width
)
633 x
= wwin
->frame_x
+ wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
634 if (x
< wwin
->frame_x
)
637 /* Common menu position */
638 prepare_menu_position(menu
, x
, y
);
640 if (!wwin
->flags
.internal_window
)
641 wMenuMapAt(menu
, x
, y
, keyboard
);
644 void OpenWindowMenu2(WWindow
*wwin
, int x
, int y
, int keyboard
)
648 WScreen
*scr
= wwin
->screen_ptr
;
650 menu
= open_window_menu_core(wwin
, x
, y
);
654 /* Specific menu position */
655 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
656 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
657 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
660 x
-= menu
->frame
->core
->width
/ 2;
662 /* Common menu position */
663 prepare_menu_position(menu
, x
, y
);
665 if (!wwin
->flags
.internal_window
)
666 wMenuMapAt(menu
, x
, y
, keyboard
);
669 void OpenMiniwindowMenu(WWindow
* wwin
, int x
, int y
)
673 menu
= open_window_menu_core(wwin
, x
, y
);
677 x
-= menu
->frame
->core
->width
/ 2;
679 wMenuMapAt(menu
, x
, y
, False
);
682 void DestroyWindowMenu(WScreen
*scr
)
684 if (scr
->window_menu
) {
685 scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
= NULL
;
686 scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
= NULL
;
687 scr
->window_menu
->entries
[MC_SHADE
]->text
= NULL
;
688 wMenuDestroy(scr
->window_menu
, True
);
689 scr
->window_menu
= NULL
;