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"
49 #define MC_MINIATURIZE 1
52 #define MC_MOVERESIZE 4
54 #define MC_DUMMY_MOVETO 6
55 #define MC_PROPERTIES 7
62 #define WO_KEEP_ON_TOP 0
63 #define WO_KEEP_AT_BOTTOM 1
64 #define WO_OMNIPRESENT 2
67 /**** Global data ***/
68 extern Time LastTimestamp
;
69 extern Atom _XA_WM_DELETE_WINDOW
;
70 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
;
72 extern WShortKey wKeyBindings
[WKBD_LAST
];
74 extern WPreferences wPreferences
;
76 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
);
78 static void execWindowOptionCommand(WMenu
* menu
, WMenuEntry
* entry
)
80 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
82 switch (entry
->order
) {
84 if (wwin
->frame
->core
->stacking
->window_level
!= WMFloatingLevel
)
85 ChangeStackingLevel(wwin
->frame
->core
, WMFloatingLevel
);
87 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
90 case WO_KEEP_AT_BOTTOM
:
91 if (wwin
->frame
->core
->stacking
->window_level
!= WMSunkenLevel
)
92 ChangeStackingLevel(wwin
->frame
->core
, WMSunkenLevel
);
94 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
98 wWindowSetOmnipresent(wwin
, !wwin
->flags
.omnipresent
);
103 static void execMenuCommand(WMenu
* menu
, WMenuEntry
* entry
)
105 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
108 CloseWindowMenu(menu
->frame
->screen_ptr
);
110 switch (entry
->order
) {
112 /* send delete message */
113 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
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
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
, LastTimestamp
);
136 wIconifyWindow(wwin
);
142 if (wwin
->flags
.maximized
)
143 wUnmaximizeWindow(wwin
);
145 wMaximizeWindow(wwin
, MAX_VERTICAL
| MAX_HORIZONTAL
);
149 if (wwin
->flags
.shaded
)
150 wUnshadeWindow(wwin
);
156 if (!wwin
->flags
.miniaturized
)
157 wSelectWindow(wwin
, !wwin
->flags
.selected
);
159 wIconSelect(wwin
->icon
);
163 wKeyboardMoveResizeWindow(wwin
);
167 wShowInspectorForWindow(wwin
);
171 wapp
= wApplicationOf(wwin
->main_window
);
172 wHideApplication(wapp
);
177 static void switchWSCommand(WMenu
* menu
, WMenuEntry
* entry
)
179 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
181 wSelectWindow(wwin
, False
);
182 wWindowChangeWorkspace(wwin
, entry
->order
);
185 static void makeShortcutCommand(WMenu
* menu
, WMenuEntry
* entry
)
187 WWindow
*wwin
= (WWindow
*) entry
->clientdata
;
188 WScreen
*scr
= wwin
->screen_ptr
;
189 int index
= entry
->order
- WO_ENTRIES
;
191 if (scr
->shortcutWindows
[index
]) {
192 WMFreeArray(scr
->shortcutWindows
[index
]);
193 scr
->shortcutWindows
[index
] = NULL
;
196 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
197 scr
->shortcutWindows
[index
] = WMDuplicateArray(scr
->selected_windows
);
198 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
199 WMInsertInArray(scr->shortcutWindows[index], 0, wwin); */
201 scr
->shortcutWindows
[index
] = WMCreateArray(4);
202 WMAddToArray(scr
->shortcutWindows
[index
], wwin
);
205 wSelectWindow(wwin
, !wwin
->flags
.selected
);
208 wSelectWindow(wwin
, !wwin
->flags
.selected
);
212 static void updateWorkspaceMenu(WMenu
* menu
)
214 WScreen
*scr
= menu
->frame
->screen_ptr
;
215 char title
[MAX_WORKSPACENAME_WIDTH
+ 1];
218 for (i
= 0; i
< scr
->workspace_count
; i
++) {
219 if (i
< menu
->entry_no
) {
220 if (strcmp(menu
->entries
[i
]->text
, scr
->workspaces
[i
]->name
) != 0) {
221 wfree(menu
->entries
[i
]->text
);
222 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
223 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
224 menu
->entries
[i
]->text
= wstrdup(title
);
225 menu
->flags
.realized
= 0;
228 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
229 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
231 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
233 menu
->flags
.realized
= 0;
237 if (!menu
->flags
.realized
)
241 static void updateMakeShortcutMenu(WMenu
* menu
, WWindow
* wwin
)
243 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
252 buflen
= strlen(_("Set Shortcut")) + 16;
253 buffer
= wmalloc(buflen
);
255 for (i
= WO_ENTRIES
; i
< smenu
->entry_no
; i
++) {
257 int shortcutNo
= i
- WO_ENTRIES
;
258 WMenuEntry
*entry
= smenu
->entries
[i
];
259 WMArray
*shortSelWindows
= wwin
->screen_ptr
->shortcutWindows
[shortcutNo
];
261 snprintf(buffer
, buflen
, "%s %i", _("Set Shortcut"), shortcutNo
+ 1);
263 if (!shortSelWindows
) {
264 entry
->flags
.indicator_on
= 0;
266 entry
->flags
.indicator_on
= 1;
267 if (WMCountInArray(shortSelWindows
, wwin
))
268 entry
->flags
.indicator_type
= MI_DIAMOND
;
270 entry
->flags
.indicator_type
= MI_CHECK
;
273 if (strcmp(buffer
, entry
->text
) != 0) {
275 entry
->text
= wstrdup(buffer
);
276 smenu
->flags
.realized
= 0;
279 kcode
= wKeyBindings
[WKBD_WINDOW1
+ shortcutNo
].keycode
;
282 if ((tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0)))
283 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
) != 0)) {
286 entry
->rtext
= wstrdup(tmp
);
287 smenu
->flags
.realized
= 0;
289 wMenuSetEnabled(smenu
, i
, True
);
291 wMenuSetEnabled(smenu
, i
, False
);
295 smenu
->flags
.realized
= 0;
298 entry
->clientdata
= wwin
;
301 if (!smenu
->flags
.realized
)
305 static void updateOptionsMenu(WMenu
* menu
, WWindow
* wwin
)
307 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
309 /* keep on top check */
310 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
311 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
312 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
) ? 1 : 0;
313 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
315 /* keep at bottom check */
316 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
317 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
318 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
) ? 1 : 0;
319 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
321 /* omnipresent check */
322 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
323 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
325 smenu
->flags
.realized
= 0;
329 static WMenu
*makeWorkspaceMenu(WScreen
* scr
)
333 menu
= wMenuCreate(scr
, NULL
, False
);
335 wwarning(_("could not create submenu for window menu"));
339 updateWorkspaceMenu(menu
);
344 static WMenu
*makeMakeShortcutMenu(WScreen
* scr
, WMenu
* menu
)
351 menu = wMenuCreate(scr, NULL, False);
353 wwarning(_("could not create submenu for window menu"));
358 for (i
= 0; i
< MAX_WINDOW_SHORTCUTS
; i
++) {
360 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
362 entry
->flags
.indicator
= 1;
368 static WMenu
*makeOptionsMenu(WScreen
* scr
)
373 menu
= wMenuCreate(scr
, NULL
, False
);
375 wwarning(_("could not create submenu for window menu"));
379 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
, NULL
);
380 entry
->flags
.indicator
= 1;
381 entry
->flags
.indicator_type
= MI_CHECK
;
383 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
, NULL
);
384 entry
->flags
.indicator
= 1;
385 entry
->flags
.indicator_type
= MI_CHECK
;
387 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
, NULL
);
388 entry
->flags
.indicator
= 1;
389 entry
->flags
.indicator_type
= MI_CHECK
;
394 static WMenu
*createWindowMenu(WScreen
* scr
)
401 menu
= wMenuCreate(scr
, NULL
, False
);
403 * Warning: If you make some change that affects the order of the
404 * entries, you must update the command #defines in the top of
407 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
408 if (wKeyBindings
[WKBD_MAXIMIZE
].keycode
!= 0) {
409 kcode
= wKeyBindings
[WKBD_MAXIMIZE
].keycode
;
411 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
412 entry
->rtext
= wstrdup(tmp
);
415 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
416 if (wKeyBindings
[WKBD_MINIATURIZE
].keycode
!= 0) {
417 kcode
= wKeyBindings
[WKBD_MINIATURIZE
].keycode
;
419 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
420 entry
->rtext
= wstrdup(tmp
);
423 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
424 if (wKeyBindings
[WKBD_SHADE
].keycode
!= 0) {
425 kcode
= wKeyBindings
[WKBD_SHADE
].keycode
;
427 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
428 entry
->rtext
= wstrdup(tmp
);
431 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
432 if (wKeyBindings
[WKBD_HIDE
].keycode
!= 0) {
433 kcode
= wKeyBindings
[WKBD_HIDE
].keycode
;
435 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
436 entry
->rtext
= wstrdup(tmp
);
439 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
440 if (wKeyBindings
[WKBD_MOVERESIZE
].keycode
!= 0) {
441 kcode
= wKeyBindings
[WKBD_MOVERESIZE
].keycode
;
443 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
444 entry
->rtext
= wstrdup(tmp
);
447 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
448 if (wKeyBindings
[WKBD_SELECT
].keycode
!= 0) {
449 kcode
= wKeyBindings
[WKBD_SELECT
].keycode
;
451 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
452 entry
->rtext
= wstrdup(tmp
);
455 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
456 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
457 if (scr
->workspace_submenu
)
458 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
460 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
462 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
463 wMenuEntrySetCascade(menu
, entry
, makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
466 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
467 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
470 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
471 if (wKeyBindings
[WKBD_CLOSE
].keycode
!= 0) {
472 kcode
= wKeyBindings
[WKBD_CLOSE
].keycode
;
473 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
474 entry
->rtext
= wstrdup(tmp
);
477 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
482 void CloseWindowMenu(WScreen
* scr
)
484 if (scr
->window_menu
) {
485 if (scr
->window_menu
->flags
.mapped
)
486 wMenuUnmap(scr
->window_menu
);
488 if (scr
->window_menu
->entries
[0]->clientdata
) {
489 WWindow
*wwin
= (WWindow
*) scr
->window_menu
->entries
[0]->clientdata
;
491 wwin
->flags
.menu_open_for_me
= 0;
493 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
497 static void updateMenuForWindow(WMenu
* menu
, WWindow
* wwin
)
499 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
500 WScreen
*scr
= wwin
->screen_ptr
;
503 updateOptionsMenu(menu
, wwin
);
505 updateMakeShortcutMenu(menu
, wwin
);
507 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!= NULL
&& !WFLAGP(wapp
->main_window_desc
, no_appicon
));
509 wMenuSetEnabled(menu
, MC_CLOSE
, (wwin
->protocols
.DELETE_WINDOW
&& !WFLAGP(wwin
, no_closable
)));
511 if (wwin
->flags
.miniaturized
) {
512 static char *text
= NULL
;
514 text
= _("Deminiaturize");
516 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
518 static char *text
= NULL
;
520 text
= _("Miniaturize");
522 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
525 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
527 if (wwin
->flags
.maximized
) {
528 static char *text
= NULL
;
530 text
= _("Unmaximize");
532 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
534 static char *text
= NULL
;
536 text
= _("Maximize");
538 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
540 wMenuSetEnabled(menu
, MC_MAXIMIZE
, IS_RESIZABLE(wwin
));
542 wMenuSetEnabled(menu
, MC_MOVERESIZE
, IS_RESIZABLE(wwin
)
543 && !wwin
->flags
.miniaturized
);
545 if (wwin
->flags
.shaded
) {
546 static char *text
= NULL
;
550 menu
->entries
[MC_SHADE
]->text
= text
;
552 static char *text
= NULL
;
556 menu
->entries
[MC_SHADE
]->text
= text
;
559 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
560 && !wwin
->flags
.miniaturized
);
562 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
564 if (!wwin
->flags
.inspector_open
) {
565 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
567 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
570 /* set the client data of the entries to the window */
571 for (i
= 0; i
< menu
->entry_no
; i
++) {
572 menu
->entries
[i
]->clientdata
= wwin
;
575 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
576 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
577 if (i
== scr
->current_workspace
) {
578 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
580 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
584 menu
->flags
.realized
= 0;
588 void OpenWindowMenu(WWindow
* wwin
, int x
, int y
, int keyboard
)
591 WScreen
*scr
= wwin
->screen_ptr
;
593 wwin
->flags
.menu_open_for_me
= 1;
595 if (!scr
->window_menu
) {
596 scr
->window_menu
= createWindowMenu(scr
);
598 /* hack to save some memory allocation/deallocation */
599 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
600 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
601 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
603 updateWorkspaceMenu(scr
->workspace_submenu
);
606 menu
= scr
->window_menu
;
607 if (menu
->flags
.mapped
) {
609 if (menu
->entries
[0]->clientdata
== wwin
) {
614 updateMenuForWindow(menu
, wwin
);
616 x
-= menu
->frame
->core
->width
/ 2;
617 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+ wwin
->frame
->core
->width
)
618 x
= wwin
->frame_x
+ wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
619 if (x
< wwin
->frame_x
)
622 if (!wwin
->flags
.internal_window
)
623 wMenuMapAt(menu
, x
, y
, keyboard
);
626 void OpenMiniwindowMenu(WWindow
* wwin
, int x
, int y
)
629 WScreen
*scr
= wwin
->screen_ptr
;
631 wwin
->flags
.menu_open_for_me
= 1;
633 if (!scr
->window_menu
) {
634 scr
->window_menu
= createWindowMenu(scr
);
636 /* hack to save some memory allocation/deallocation */
637 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
638 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
639 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
641 updateWorkspaceMenu(scr
->workspace_submenu
);
644 menu
= scr
->window_menu
;
645 if (menu
->flags
.mapped
) {
647 if (menu
->entries
[0]->clientdata
== wwin
) {
652 updateMenuForWindow(menu
, wwin
);
654 x
-= menu
->frame
->core
->width
/ 2;
656 wMenuMapAt(menu
, x
, y
, False
);