1 /* winmenu.c - command menu for windows
3 * Window Maker window manager
5 * Copyright (c) 1997, 1998 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
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
);
80 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 wwin
->flags
.omnipresent
^=1;
101 UpdateSwitchMenu(wwin
->screen_ptr
, wwin
, ACTION_CHANGE_WORKSPACE
);
108 execMenuCommand(WMenu
*menu
, WMenuEntry
*entry
)
110 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
113 CloseWindowMenu(menu
->frame
->screen_ptr
);
115 switch (entry
->order
) {
117 /* send delete message */
118 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
123 if (wPreferences
.dont_confirm_kill
124 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
125 _("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
126 _("Yes"), _("No"), NULL
)==WAPRDefault
) {
127 if (!wwin
->flags
.destroyed
)
134 if (wwin
->flags
.miniaturized
) {
135 wDeiconifyWindow(wwin
);
137 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
138 wClientSendProtocol(wwin
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
,
141 wIconifyWindow(wwin
);
147 if (wwin
->flags
.maximized
)
148 wUnmaximizeWindow(wwin
);
150 wMaximizeWindow(wwin
, MAX_VERTICAL
|MAX_HORIZONTAL
);
154 if (wwin
->flags
.shaded
)
155 wUnshadeWindow(wwin
);
161 if (!wwin
->flags
.miniaturized
)
162 wSelectWindow(wwin
, !wwin
->flags
.selected
);
164 wIconSelect(wwin
->icon
);
168 wKeyboardMoveResizeWindow(wwin
);
172 if (wwin
->wm_class
|| wwin
->wm_instance
)
173 wShowInspectorForWindow(wwin
);
177 wapp
= wApplicationOf(wwin
->main_window
);
178 wHideApplication(wapp
);
186 switchWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
188 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
190 wSelectWindow(wwin
, False
);
191 wWindowChangeWorkspace(wwin
, entry
->order
);
196 makeShortcutCommand(WMenu
*menu
, WMenuEntry
*entry
)
198 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
200 wwin
->screen_ptr
->shortcutWindow
[entry
->order
-WO_ENTRIES
] = wwin
;
202 wSelectWindow(wwin
, !wwin
->flags
.selected
);
205 wSelectWindow(wwin
, !wwin
->flags
.selected
);
211 updateWorkspaceMenu(WMenu
*menu
)
213 WScreen
*scr
= menu
->frame
->screen_ptr
;
214 char title
[MAX_WORKSPACENAME_WIDTH
+1];
220 for (i
=0; i
<scr
->workspace_count
; i
++) {
221 if (i
< menu
->entry_no
) {
222 if (strcmp(menu
->entries
[i
]->text
,scr
->workspaces
[i
]->name
)!=0) {
223 free(menu
->entries
[i
]->text
);
224 strcpy(title
, scr
->workspaces
[i
]->name
);
225 menu
->entries
[i
]->text
= wstrdup(title
);
226 menu
->flags
.realized
= 0;
229 strcpy(title
, scr
->workspaces
[i
]->name
);
231 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
233 menu
->flags
.realized
= 0;
237 if (!menu
->flags
.realized
)
243 updateMakeShortcutMenu(WMenu
*menu
, WWindow
*wwin
)
245 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
253 buffer
= wmalloc(strlen(_("Set Shortcut"))+16);
255 for (i
=WO_ENTRIES
; i
<smenu
->entry_no
; i
++) {
257 int shortcutNo
= i
-WO_ENTRIES
;
258 WWindow
*twin
= wwin
->screen_ptr
->shortcutWindow
[shortcutNo
];
259 WMenuEntry
*entry
= smenu
->entries
[i
];
261 sprintf(buffer
, "%s %i", _("Set Shortcut"), shortcutNo
+1);
264 entry
->flags
.indicator_on
= 0;
266 entry
->flags
.indicator_on
= 1;
268 entry
->flags
.indicator_type
= MI_CHECK
;
270 entry
->flags
.indicator_type
= MI_DIAMOND
;
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
)
307 updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
)
309 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
311 /* keep on top check */
312 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
313 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
314 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
)?1:0;
315 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
317 /* keep at bottom check */
318 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
319 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
320 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
)?1:0;
321 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
323 /* omnipresent check */
324 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
325 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
327 smenu
->flags
.realized
= 0;
333 makeWorkspaceMenu(WScreen
*scr
)
337 menu
= wMenuCreate(scr
, NULL
, False
);
339 wwarning(_("could not create submenu for window menu"));
343 updateWorkspaceMenu(menu
);
350 makeMakeShortcutMenu(WScreen
*scr
, WMenu
*menu
)
357 menu = wMenuCreate(scr, NULL, False);
359 wwarning(_("could not create submenu for window menu"));
364 for (i
=0; i
<MAX_WINDOW_SHORTCUTS
; i
++) {
367 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
369 entry
->flags
.indicator
= 1;
378 makeOptionsMenu(WScreen
*scr
)
383 menu
= wMenuCreate(scr
, NULL
, False
);
385 wwarning(_("could not create submenu for window menu"));
389 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
,
391 entry
->flags
.indicator
= 1;
392 entry
->flags
.indicator_type
= MI_CHECK
;
394 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
,
396 entry
->flags
.indicator
= 1;
397 entry
->flags
.indicator_type
= MI_CHECK
;
399 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
,
401 entry
->flags
.indicator
= 1;
402 entry
->flags
.indicator_type
= MI_CHECK
;
409 createWindowMenu(WScreen
*scr
)
416 menu
= wMenuCreate(scr
, NULL
, False
);
418 * Warning: If you make some change that affects the order of the
419 * entries, you must update the command #defines in the top of
422 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
423 if (wKeyBindings
[WKBD_MAXIMIZE
].keycode
!=0) {
424 kcode
= wKeyBindings
[WKBD_MAXIMIZE
].keycode
;
426 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
427 entry
->rtext
= wstrdup(tmp
);
430 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
432 if (wKeyBindings
[WKBD_MINIATURIZE
].keycode
!=0) {
433 kcode
= wKeyBindings
[WKBD_MINIATURIZE
].keycode
;
435 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
436 entry
->rtext
= wstrdup(tmp
);
439 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
440 if (wKeyBindings
[WKBD_SHADE
].keycode
!=0) {
441 kcode
= wKeyBindings
[WKBD_SHADE
].keycode
;
443 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
444 entry
->rtext
= wstrdup(tmp
);
447 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
448 if (wKeyBindings
[WKBD_HIDE
].keycode
!=0) {
449 kcode
= wKeyBindings
[WKBD_HIDE
].keycode
;
451 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
452 entry
->rtext
= wstrdup(tmp
);
455 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
456 if (wKeyBindings
[WKBD_MOVERESIZE
].keycode
!=0) {
457 kcode
= wKeyBindings
[WKBD_MOVERESIZE
].keycode
;
459 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
460 entry
->rtext
= wstrdup(tmp
);
463 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
464 if (wKeyBindings
[WKBD_SELECT
].keycode
!=0) {
465 kcode
= wKeyBindings
[WKBD_SELECT
].keycode
;
467 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
468 entry
->rtext
= wstrdup(tmp
);
471 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
472 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
473 if (scr
->workspace_submenu
)
474 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
476 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
478 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
479 wMenuEntrySetCascade(menu
, entry
,
480 makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
483 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
484 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
487 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
488 if (wKeyBindings
[WKBD_CLOSE
].keycode
!=0) {
489 kcode
= wKeyBindings
[WKBD_CLOSE
].keycode
;
490 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
491 entry
->rtext
= wstrdup(tmp
);
494 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
501 CloseWindowMenu(WScreen
*scr
)
503 if (scr
->window_menu
) {
504 if (scr
->window_menu
->flags
.mapped
)
505 wMenuUnmap(scr
->window_menu
);
507 if (scr
->window_menu
->entries
[0]->clientdata
) {
508 WWindow
*wwin
= (WWindow
*)scr
->window_menu
->entries
[0]->clientdata
;
510 wwin
->flags
.menu_open_for_me
= 0;
512 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
519 updateMenuForWindow(WMenu
*menu
, WWindow
*wwin
)
521 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
522 WScreen
*scr
= wwin
->screen_ptr
;
525 updateOptionsMenu(menu
, wwin
);
527 updateMakeShortcutMenu(menu
, wwin
);
529 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!=NULL
530 && !WFLAGP(wapp
->main_window_desc
, no_appicon
));
532 wMenuSetEnabled(menu
, MC_CLOSE
,
533 (wwin
->protocols
.DELETE_WINDOW
534 && !WFLAGP(wwin
, no_closable
)));
536 if (wwin
->flags
.miniaturized
) {
537 static char *text
= NULL
;
538 if (!text
) text
= _("Deminiaturize");
540 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
542 static char *text
= NULL
;
543 if (!text
) text
= _("Miniaturize");
545 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
548 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
550 if (wwin
->flags
.maximized
) {
551 static char *text
= NULL
;
552 if (!text
) text
= _("Unmaximize");
554 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
556 static char *text
= NULL
;
557 if (!text
) text
= _("Maximize");
559 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
561 wMenuSetEnabled(menu
, MC_MAXIMIZE
, !WFLAGP(wwin
, no_resizable
));
564 wMenuSetEnabled(menu
, MC_MOVERESIZE
, !WFLAGP(wwin
, no_resizable
)
565 && !wwin
->flags
.miniaturized
);
567 if (wwin
->flags
.shaded
) {
568 static char *text
= NULL
;
569 if (!text
) text
= _("Unshade");
571 menu
->entries
[MC_SHADE
]->text
= text
;
573 static char *text
= NULL
;
574 if (!text
) text
= _("Shade");
576 menu
->entries
[MC_SHADE
]->text
= text
;
579 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
580 && !wwin
->flags
.miniaturized
);
582 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
584 if ((wwin
->wm_class
|| wwin
->wm_instance
) && !wwin
->flags
.inspector_open
) {
585 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
587 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
590 /* set the client data of the entries to the window */
591 for (i
= 0; i
< menu
->entry_no
; i
++) {
592 menu
->entries
[i
]->clientdata
= wwin
;
595 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
596 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
597 if (i
== scr
->current_workspace
) {
598 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
600 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
604 menu
->flags
.realized
= 0;
610 OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
613 WScreen
*scr
= wwin
->screen_ptr
;
615 wwin
->flags
.menu_open_for_me
= 1;
617 if (!scr
->window_menu
) {
618 scr
->window_menu
= createWindowMenu(scr
);
620 /* hack to save some memory allocation/deallocation */
621 free(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
622 free(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
623 free(scr
->window_menu
->entries
[MC_SHADE
]->text
);
625 updateWorkspaceMenu(scr
->workspace_submenu
);
628 menu
= scr
->window_menu
;
629 if (menu
->flags
.mapped
) {
631 if (menu
->entries
[0]->clientdata
==wwin
) {
636 updateMenuForWindow(menu
, wwin
);
638 x
-= menu
->frame
->core
->width
/2;
639 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+wwin
->frame
->core
->width
)
640 x
= wwin
->frame_x
+wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
641 if (x
< wwin
->frame_x
)
644 if (!wwin
->flags
.internal_window
)
645 wMenuMapAt(menu
, x
, y
, keyboard
);
650 OpenMiniwindowMenu(WWindow
*wwin
, int x
, int y
)
653 WScreen
*scr
= wwin
->screen_ptr
;
655 wwin
->flags
.menu_open_for_me
= 1;
657 if (!scr
->window_menu
) {
658 scr
->window_menu
= createWindowMenu(scr
);
660 /* hack to save some memory allocation/deallocation */
661 free(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
662 free(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
663 free(scr
->window_menu
->entries
[MC_SHADE
]->text
);
665 updateWorkspaceMenu(scr
->workspace_submenu
);
668 menu
= scr
->window_menu
;
669 if (menu
->flags
.mapped
) {
671 if (menu
->entries
[0]->clientdata
==wwin
) {
676 updateMenuForWindow(menu
, wwin
);
678 x
-= menu
->frame
->core
->width
/2;
680 wMenuMapAt(menu
, x
, y
, False
);