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
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
63 #define WO_KEEP_ON_TOP 0
64 #define WO_KEEP_AT_BOTTOM 1
65 #define WO_OMNIPRESENT 2
68 /**** Global data ***/
69 extern Time LastTimestamp
;
70 extern Atom _XA_WM_DELETE_WINDOW
;
71 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
;
73 extern WShortKey wKeyBindings
[WKBD_LAST
];
75 extern WPreferences wPreferences
;
77 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
);
79 static void 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 wWindowSetOmnipresent(wwin
, !wwin
->flags
.omnipresent
);
104 static void execMenuCommand(WMenu
* menu
, WMenuEntry
* entry
)
106 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
109 CloseWindowMenu(menu
->frame
->screen_ptr
);
111 switch (entry
->order
) {
113 /* send delete message */
114 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
119 if (wPreferences
.dont_confirm_kill
120 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
122 ("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
123 _("Yes"), _("No"), NULL
) == WAPRDefault
) {
124 if (!wwin
->flags
.destroyed
)
131 if (wwin
->flags
.miniaturized
) {
132 wDeiconifyWindow(wwin
);
134 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
135 wClientSendProtocol(wwin
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
, LastTimestamp
);
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 wapp
= wApplicationOf(wwin
->main_window
);
173 wHideApplication(wapp
);
178 static void switchWSCommand(WMenu
* menu
, WMenuEntry
* entry
)
180 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
182 wSelectWindow(wwin
, False
);
183 wWindowChangeWorkspace(wwin
, entry
->order
);
186 static void makeShortcutCommand(WMenu
* menu
, WMenuEntry
* entry
)
188 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
189 WScreen
*scr
= wwin
->screen_ptr
;
190 int index
= entry
->order
- WO_ENTRIES
;
192 if (scr
->shortcutWindows
[index
]) {
193 WMFreeArray(scr
->shortcutWindows
[index
]);
194 scr
->shortcutWindows
[index
] = NULL
;
197 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
198 scr
->shortcutWindows
[index
] = WMDuplicateArray(scr
->selected_windows
);
199 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
200 WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
202 scr
->shortcutWindows
[index
] = WMCreateArray(4);
203 WMAddToArray(scr
->shortcutWindows
[index
], wwin
);
206 wSelectWindow(wwin
, !wwin
->flags
.selected
);
209 wSelectWindow(wwin
, !wwin
->flags
.selected
);
213 static void updateWorkspaceMenu(WMenu
* menu
)
215 WScreen
*scr
= menu
->frame
->screen_ptr
;
216 char title
[MAX_WORKSPACENAME_WIDTH
+ 1];
219 for (i
= 0; i
< scr
->workspace_count
; i
++) {
220 if (i
< menu
->entry_no
) {
221 if (strcmp(menu
->entries
[i
]->text
, scr
->workspaces
[i
]->name
) != 0) {
222 wfree(menu
->entries
[i
]->text
);
223 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
224 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
225 menu
->entries
[i
]->text
= wstrdup(title
);
226 menu
->flags
.realized
= 0;
229 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
230 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
232 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
234 menu
->flags
.realized
= 0;
238 if (!menu
->flags
.realized
)
242 static void updateMakeShortcutMenu(WMenu
* menu
, WWindow
* wwin
)
244 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
253 buflen
= strlen(_("Set Shortcut")) + 16;
254 buffer
= wmalloc(buflen
);
256 for (i
= WO_ENTRIES
; i
< smenu
->entry_no
; i
++) {
258 int shortcutNo
= i
- WO_ENTRIES
;
259 WMenuEntry
*entry
= smenu
->entries
[i
];
260 WMArray
*shortSelWindows
= wwin
->screen_ptr
->shortcutWindows
[shortcutNo
];
262 snprintf(buffer
, buflen
, "%s %i", _("Set Shortcut"), shortcutNo
+ 1);
264 if (!shortSelWindows
) {
265 entry
->flags
.indicator_on
= 0;
267 entry
->flags
.indicator_on
= 1;
268 if (WMCountInArray(shortSelWindows
, wwin
))
269 entry
->flags
.indicator_type
= MI_DIAMOND
;
271 entry
->flags
.indicator_type
= MI_CHECK
;
274 if (strcmp(buffer
, entry
->text
) != 0) {
276 entry
->text
= wstrdup(buffer
);
277 smenu
->flags
.realized
= 0;
280 kcode
= wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
].keycode
;
283 if ((tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0)))
284 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
) != 0)) {
287 entry
->rtext
= wstrdup(tmp
);
288 smenu
->flags
.realized
= 0;
290 wMenuSetEnabled(smenu
, i
, True
);
292 wMenuSetEnabled(smenu
, i
, False
);
296 smenu
->flags
.realized
= 0;
299 entry
->clientdata
= wwin
;
302 if (!smenu
->flags
.realized
)
306 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
)
308 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
310 /* keep on top check */
311 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
312 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
313 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
) ? 1 : 0;
314 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
316 /* keep at bottom check */
317 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
318 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
319 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
) ? 1 : 0;
320 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
322 /* omnipresent check */
323 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
324 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
326 smenu
->flags
.realized
= 0;
330 static WMenu
*makeWorkspaceMenu(WScreen
* scr
)
334 menu
= wMenuCreate(scr
, NULL
, False
);
336 wwarning(_("could not create submenu for window menu"));
340 updateWorkspaceMenu(menu
);
345 static WMenu
*makeMakeShortcutMenu(WScreen
* scr
, WMenu
* menu
)
352 menu = wMenuCreate(scr, NULL, False);
354 wwarning(_("could not create submenu for window menu"));
359 for (i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
361 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
363 entry
->flags
.indicator
= 1;
369 static WMenu
*makeOptionsMenu(WScreen
* scr
)
374 menu
= wMenuCreate(scr
, NULL
, False
);
376 wwarning(_("could not create submenu for window menu"));
380 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
, NULL
);
381 entry
->flags
.indicator
= 1;
382 entry
->flags
.indicator_type
= MI_CHECK
;
384 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
, NULL
);
385 entry
->flags
.indicator
= 1;
386 entry
->flags
.indicator_type
= MI_CHECK
;
388 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
, NULL
);
389 entry
->flags
.indicator
= 1;
390 entry
->flags
.indicator_type
= MI_CHECK
;
395 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 if (wKeyBindings
[WKBD_MAXIMIZE
].keycode
!= 0) {
410 kcode
= wKeyBindings
[WKBD_MAXIMIZE
].keycode
;
412 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
413 entry
->rtext
= wstrdup(tmp
);
416 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
417 if (wKeyBindings
[WKBD_MINIATURIZE
].keycode
!= 0) {
418 kcode
= wKeyBindings
[WKBD_MINIATURIZE
].keycode
;
420 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
421 entry
->rtext
= wstrdup(tmp
);
424 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
425 if (wKeyBindings
[WKBD_SHADE
].keycode
!= 0) {
426 kcode
= wKeyBindings
[WKBD_SHADE
].keycode
;
428 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
429 entry
->rtext
= wstrdup(tmp
);
432 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
433 if (wKeyBindings
[WKBD_HIDE
].keycode
!= 0) {
434 kcode
= wKeyBindings
[WKBD_HIDE
].keycode
;
436 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
437 entry
->rtext
= wstrdup(tmp
);
440 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
441 if (wKeyBindings
[WKBD_MOVERESIZE
].keycode
!= 0) {
442 kcode
= wKeyBindings
[WKBD_MOVERESIZE
].keycode
;
444 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
445 entry
->rtext
= wstrdup(tmp
);
448 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
449 if (wKeyBindings
[WKBD_SELECT
].keycode
!= 0) {
450 kcode
= wKeyBindings
[WKBD_SELECT
].keycode
;
452 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
453 entry
->rtext
= wstrdup(tmp
);
456 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
457 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
458 if (scr
->workspace_submenu
)
459 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
461 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
463 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
464 wMenuEntrySetCascade(menu
, entry
, makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
467 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
468 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
471 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
472 if (wKeyBindings
[WKBD_CLOSE
].keycode
!= 0) {
473 kcode
= wKeyBindings
[WKBD_CLOSE
].keycode
;
474 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
475 entry
->rtext
= wstrdup(tmp
);
478 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
483 void CloseWindowMenu(WScreen
* scr
)
485 if (scr
->window_menu
) {
486 if (scr
->window_menu
->flags
.mapped
)
487 wMenuUnmap(scr
->window_menu
);
489 if (scr
->window_menu
->entries
[0]->clientdata
) {
490 WWindow
*wwin
= (WWindow
*) scr
->window_menu
->entries
[0]->clientdata
;
492 wwin
->flags
.menu_open_for_me
= 0;
494 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
498 static void updateMenuForWindow(WMenu
* menu
, WWindow
* wwin
)
500 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
501 WScreen
*scr
= wwin
->screen_ptr
;
504 updateOptionsMenu(menu
, wwin
);
506 updateMakeShortcutMenu(menu
, wwin
);
508 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!= NULL
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
));
510 wMenuSetEnabled(menu
, MC_CLOSE
, (wwin
->protocols
.DELETE_WINDOW
&& !WFLAGP(wwin
, no_closable
)));
512 if (wwin
->flags
.miniaturized
) {
513 static char *text
= NULL
;
515 text
= _("Deminiaturize");
517 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
519 static char *text
= NULL
;
521 text
= _("Miniaturize");
523 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
526 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
528 if (wwin
->flags
.maximized
) {
529 static char *text
= NULL
;
531 text
= _("Unmaximize");
533 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
535 static char *text
= NULL
;
537 text
= _("Maximize");
539 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
541 wMenuSetEnabled(menu
, MC_MAXIMIZE
, IS_RESIZABLE(wwin
));
543 wMenuSetEnabled(menu
, MC_MOVERESIZE
, IS_RESIZABLE(wwin
)
544 && !wwin
->flags
.miniaturized
);
546 if (wwin
->flags
.shaded
) {
547 static char *text
= NULL
;
551 menu
->entries
[MC_SHADE
]->text
= text
;
553 static char *text
= NULL
;
557 menu
->entries
[MC_SHADE
]->text
= text
;
560 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
561 && !wwin
->flags
.miniaturized
);
563 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
565 if (!wwin
->flags
.inspector_open
) {
566 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
568 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
571 /* set the client data of the entries to the window */
572 for (i
= 0; i
< menu
->entry_no
; i
++) {
573 menu
->entries
[i
]->clientdata
= wwin
;
576 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
577 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
578 if (i
== scr
->current_workspace
) {
579 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
581 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
585 menu
->flags
.realized
= 0;
589 void OpenWindowMenu(WWindow
* wwin
, int x
, int y
, int keyboard
)
592 WScreen
*scr
= wwin
->screen_ptr
;
595 wwin
->flags
.menu_open_for_me
= 1;
597 if (!scr
->window_menu
) {
598 scr
->window_menu
= createWindowMenu(scr
);
600 /* hack to save some memory allocation/deallocation */
601 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
602 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
603 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
605 updateWorkspaceMenu(scr
->workspace_submenu
);
608 menu
= scr
->window_menu
;
609 if (menu
->flags
.mapped
) {
611 if (menu
->entries
[0]->clientdata
== wwin
) {
616 updateMenuForWindow(menu
, wwin
);
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 rect
= wGetRectForHead(menu
->frame
->screen_ptr
,
625 wGetHeadForPointerLocation(menu
->frame
->screen_ptr
));
626 if (x
< rect
.pos
.x
- menu
->frame
->core
->width
/ 2)
627 x
= rect
.pos
.x
- menu
->frame
->core
->width
/ 2;
631 if (!wwin
->flags
.internal_window
)
632 wMenuMapAt(menu
, x
, y
, keyboard
);
635 void OpenWindowMenu2(WWindow
*wwin
, int x
, int y
, int keyboard
)
639 WScreen
*scr
= wwin
->screen_ptr
;
642 wwin
->flags
.menu_open_for_me
= 1;
644 if (!scr
->window_menu
) {
645 scr
->window_menu
= createWindowMenu(scr
);
647 /* hack to save some memory allocation/deallocation */
648 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
649 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
650 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
652 updateWorkspaceMenu(scr
->workspace_submenu
);
655 menu
= scr
->window_menu
;
656 if (menu
->flags
.mapped
) {
658 if (menu
->entries
[0]->clientdata
== wwin
) {
663 updateMenuForWindow(menu
, wwin
);
665 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
666 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
667 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
670 x
-= menu
->frame
->core
->width
/ 2;
672 rect
= wGetRectForHead(menu
->frame
->screen_ptr
,
673 wGetHeadForPointerLocation(menu
->frame
->screen_ptr
));
674 if (x
< rect
.pos
.x
- menu
->frame
->core
->width
/ 2)
675 x
= rect
.pos
.x
- menu
->frame
->core
->width
/ 2;
679 if (!wwin
->flags
.internal_window
)
680 wMenuMapAt(menu
, x
, y
, keyboard
);
683 void OpenMiniwindowMenu(WWindow
* wwin
, int x
, int y
)
686 WScreen
*scr
= wwin
->screen_ptr
;
688 wwin
->flags
.menu_open_for_me
= 1;
690 if (!scr
->window_menu
) {
691 scr
->window_menu
= createWindowMenu(scr
);
693 /* hack to save some memory allocation/deallocation */
694 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
695 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
696 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
698 updateWorkspaceMenu(scr
->workspace_submenu
);
701 menu
= scr
->window_menu
;
702 if (menu
->flags
.mapped
) {
704 if (menu
->entries
[0]->clientdata
== wwin
) {
709 updateMenuForWindow(menu
, wwin
);
711 x
-= menu
->frame
->core
->width
/ 2;
713 wMenuMapAt(menu
, x
, y
, False
);