1 /* winmenu.c - command menu for windows
3 * Window Maker window manager
5 * Copyright (c) 1997-2002 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_HIDE_OTHERS 4
54 #define MC_MOVERESIZE 5
56 #define MC_DUMMY_MOVETO 7
57 #define MC_PROPERTIES 8
65 #define WO_KEEP_ON_TOP 0
66 #define WO_KEEP_AT_BOTTOM 1
67 #define WO_OMNIPRESENT 2
70 /**** Global data ***/
71 extern Time LastTimestamp
;
72 extern Atom _XA_WM_DELETE_WINDOW
;
73 extern Atom _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
;
75 extern WShortKey wKeyBindings
[WKBD_LAST
];
77 extern WPreferences wPreferences
;
79 static void updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
);
82 execWindowOptionCommand(WMenu
*menu
, WMenuEntry
*entry
)
84 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
86 switch (entry
->order
) {
88 if(wwin
->frame
->core
->stacking
->window_level
!=WMFloatingLevel
)
89 ChangeStackingLevel(wwin
->frame
->core
, WMFloatingLevel
);
91 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
94 case WO_KEEP_AT_BOTTOM
:
95 if(wwin
->frame
->core
->stacking
->window_level
!=WMSunkenLevel
)
96 ChangeStackingLevel(wwin
->frame
->core
, WMSunkenLevel
);
98 ChangeStackingLevel(wwin
->frame
->core
, WMNormalLevel
);
102 wWindowSetOmnipresent(wwin
, !wwin
->flags
.omnipresent
);
109 execMenuCommand(WMenu
*menu
, WMenuEntry
*entry
)
111 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
114 CloseWindowMenu(menu
->frame
->screen_ptr
);
116 switch (entry
->order
) {
118 /* send delete message */
119 wClientSendProtocol(wwin
, _XA_WM_DELETE_WINDOW
, LastTimestamp
);
124 if (wPreferences
.dont_confirm_kill
125 || wMessageDialog(menu
->frame
->screen_ptr
, _("Kill Application"),
126 _("This will kill the application.\nAny unsaved changes will be lost.\nPlease confirm."),
127 _("Yes"), _("No"), NULL
)==WAPRDefault
) {
128 if (!wwin
->flags
.destroyed
)
135 if (wwin
->flags
.miniaturized
) {
136 wDeiconifyWindow(wwin
);
138 if (wwin
->protocols
.MINIATURIZE_WINDOW
) {
139 wClientSendProtocol(wwin
, _XA_GNUSTEP_WM_MINIATURIZE_WINDOW
,
142 wIconifyWindow(wwin
);
148 if (wwin
->flags
.maximized
)
149 wUnmaximizeWindow(wwin
);
151 wMaximizeWindow(wwin
, MAX_VERTICAL
|MAX_HORIZONTAL
);
155 if (wwin
->flags
.shaded
)
156 wUnshadeWindow(wwin
);
162 if (!wwin
->flags
.miniaturized
)
163 wSelectWindow(wwin
, !wwin
->flags
.selected
);
165 wIconSelect(wwin
->icon
);
169 wKeyboardMoveResizeWindow(wwin
);
173 wShowInspectorForWindow(wwin
);
177 wapp
= wApplicationOf(wwin
->main_window
);
178 wHideApplication(wapp
);
182 wHideOtherApplications(wwin
);
190 switchWSCommand(WMenu
*menu
, WMenuEntry
*entry
)
192 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
194 wSelectWindow(wwin
, False
);
195 wWindowChangeWorkspace(wwin
, entry
->order
);
200 makeShortcutCommand(WMenu
*menu
, WMenuEntry
*entry
)
202 WWindow
*wwin
= (WWindow
*)entry
->clientdata
;
203 WScreen
*scr
= wwin
->screen_ptr
;
204 int index
= entry
->order
-WO_ENTRIES
;
206 if (scr
->shortcutWindows
[index
]) {
207 WMFreeArray(scr
->shortcutWindows
[index
]);
208 scr
->shortcutWindows
[index
] = NULL
;
211 if (wwin
->flags
.selected
&& scr
->selected_windows
) {
212 scr
->shortcutWindows
[index
] = WMDuplicateArray(scr
->selected_windows
);
213 /*WMRemoveFromArray(scr->shortcutWindows[index], wwin);
214 WMInsertInArray(scr->shortcutWindows[index], 0, wwin);*/
216 scr
->shortcutWindows
[index
] = WMCreateArray(4);
217 WMAddToArray(scr
->shortcutWindows
[index
], wwin
);
220 wSelectWindow(wwin
, !wwin
->flags
.selected
);
223 wSelectWindow(wwin
, !wwin
->flags
.selected
);
229 updateWorkspaceMenu(WMenu
*menu
)
231 WScreen
*scr
= menu
->frame
->screen_ptr
;
232 char title
[MAX_WORKSPACENAME_WIDTH
+1];
238 for (i
=0; i
<scr
->workspace_count
; i
++) {
239 if (i
< menu
->entry_no
) {
240 if (strcmp(menu
->entries
[i
]->text
,scr
->workspaces
[i
]->name
)!=0) {
241 wfree(menu
->entries
[i
]->text
);
242 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
243 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
244 menu
->entries
[i
]->text
= wstrdup(title
);
245 menu
->flags
.realized
= 0;
248 strncpy(title
, scr
->workspaces
[i
]->name
, MAX_WORKSPACENAME_WIDTH
);
249 title
[MAX_WORKSPACENAME_WIDTH
] = 0;
251 wMenuAddCallback(menu
, title
, switchWSCommand
, NULL
);
253 menu
->flags
.realized
= 0;
257 if (!menu
->flags
.realized
)
263 updateMakeShortcutMenu(WMenu
*menu
, WWindow
*wwin
)
265 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_SHORTCUT
]->cascade
];
274 buflen
= strlen(_("Set Shortcut"))+16;
275 buffer
= wmalloc(buflen
);
277 for (i
=WO_ENTRIES
; i
<smenu
->entry_no
; i
++) {
279 int shortcutNo
= i
-WO_ENTRIES
;
280 WMenuEntry
*entry
= smenu
->entries
[i
];
281 WMArray
*shortSelWindows
= wwin
->screen_ptr
->shortcutWindows
[shortcutNo
];
283 snprintf(buffer
, buflen
, "%s %i", _("Set Shortcut"), shortcutNo
+1);
285 if (!shortSelWindows
) {
286 entry
->flags
.indicator_on
= 0;
288 entry
->flags
.indicator_on
= 1;
289 if (WMCountInArray(shortSelWindows
, wwin
))
290 entry
->flags
.indicator_type
= MI_DIAMOND
;
292 entry
->flags
.indicator_type
= MI_CHECK
;
295 if (strcmp(buffer
, entry
->text
)!=0) {
297 entry
->text
= wstrdup(buffer
);
298 smenu
->flags
.realized
= 0;
301 kcode
= wKeyBindings
[WKBD_WINDOW1
+shortcutNo
].keycode
;
304 if ((tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0)))
305 && (!entry
->rtext
|| strcmp(tmp
, entry
->rtext
)!=0)) {
308 entry
->rtext
= wstrdup(tmp
);
309 smenu
->flags
.realized
= 0;
311 wMenuSetEnabled(smenu
, i
, True
);
313 wMenuSetEnabled(smenu
, i
, False
);
317 smenu
->flags
.realized
= 0;
320 entry
->clientdata
= wwin
;
323 if (!smenu
->flags
.realized
)
329 updateOptionsMenu(WMenu
*menu
, WWindow
*wwin
)
331 WMenu
*smenu
= menu
->cascades
[menu
->entries
[MC_OPTIONS
]->cascade
];
333 /* keep on top check */
334 smenu
->entries
[WO_KEEP_ON_TOP
]->clientdata
= wwin
;
335 smenu
->entries
[WO_KEEP_ON_TOP
]->flags
.indicator_on
=
336 (wwin
->frame
->core
->stacking
->window_level
== WMFloatingLevel
)?1:0;
337 wMenuSetEnabled(smenu
, WO_KEEP_ON_TOP
, !wwin
->flags
.miniaturized
);
339 /* keep at bottom check */
340 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->clientdata
= wwin
;
341 smenu
->entries
[WO_KEEP_AT_BOTTOM
]->flags
.indicator_on
=
342 (wwin
->frame
->core
->stacking
->window_level
== WMSunkenLevel
)?1:0;
343 wMenuSetEnabled(smenu
, WO_KEEP_AT_BOTTOM
, !wwin
->flags
.miniaturized
);
345 /* omnipresent check */
346 smenu
->entries
[WO_OMNIPRESENT
]->clientdata
= wwin
;
347 smenu
->entries
[WO_OMNIPRESENT
]->flags
.indicator_on
= IS_OMNIPRESENT(wwin
);
349 smenu
->flags
.realized
= 0;
355 makeWorkspaceMenu(WScreen
*scr
)
359 menu
= wMenuCreate(scr
, NULL
, False
);
361 wwarning(_("could not create submenu for window menu"));
365 updateWorkspaceMenu(menu
);
372 makeMakeShortcutMenu(WScreen
*scr
, WMenu
*menu
)
379 menu = wMenuCreate(scr, NULL, False);
381 wwarning(_("could not create submenu for window menu"));
386 for (i
=0; i
<MAX_WINDOW_SHORTCUTS
; i
++) {
388 entry
= wMenuAddCallback(menu
, "", makeShortcutCommand
, NULL
);
390 entry
->flags
.indicator
= 1;
399 makeOptionsMenu(WScreen
*scr
)
404 menu
= wMenuCreate(scr
, NULL
, False
);
406 wwarning(_("could not create submenu for window menu"));
410 entry
= wMenuAddCallback(menu
, _("Keep on top"), execWindowOptionCommand
,
412 entry
->flags
.indicator
= 1;
413 entry
->flags
.indicator_type
= MI_CHECK
;
415 entry
= wMenuAddCallback(menu
, _("Keep at bottom"), execWindowOptionCommand
,
417 entry
->flags
.indicator
= 1;
418 entry
->flags
.indicator_type
= MI_CHECK
;
420 entry
= wMenuAddCallback(menu
, _("Omnipresent"), execWindowOptionCommand
,
422 entry
->flags
.indicator
= 1;
423 entry
->flags
.indicator_type
= MI_CHECK
;
430 createWindowMenu(WScreen
*scr
)
437 menu
= wMenuCreate(scr
, NULL
, False
);
439 * Warning: If you make some change that affects the order of the
440 * entries, you must update the command #defines in the top of
443 entry
= wMenuAddCallback(menu
, _("Maximize"), execMenuCommand
, NULL
);
444 if (wKeyBindings
[WKBD_MAXIMIZE
].keycode
!=0) {
445 kcode
= wKeyBindings
[WKBD_MAXIMIZE
].keycode
;
447 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
448 entry
->rtext
= wstrdup(tmp
);
451 entry
= wMenuAddCallback(menu
, _("Miniaturize"), execMenuCommand
, NULL
);
452 if (wKeyBindings
[WKBD_MINIATURIZE
].keycode
!=0) {
453 kcode
= wKeyBindings
[WKBD_MINIATURIZE
].keycode
;
455 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
456 entry
->rtext
= wstrdup(tmp
);
459 entry
= wMenuAddCallback(menu
, _("Shade"), execMenuCommand
, NULL
);
460 if (wKeyBindings
[WKBD_SHADE
].keycode
!=0) {
461 kcode
= wKeyBindings
[WKBD_SHADE
].keycode
;
463 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
464 entry
->rtext
= wstrdup(tmp
);
467 entry
= wMenuAddCallback(menu
, _("Hide"), execMenuCommand
, NULL
);
468 if (wKeyBindings
[WKBD_HIDE
].keycode
!=0) {
469 kcode
= wKeyBindings
[WKBD_HIDE
].keycode
;
471 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
472 entry
->rtext
= wstrdup(tmp
);
475 entry
= wMenuAddCallback(menu
, _("Hide Others"), execMenuCommand
, NULL
);
476 if (wKeyBindings
[WKBD_HIDE_OTHERS
].keycode
!=0) {
477 kcode
= wKeyBindings
[WKBD_HIDE_OTHERS
].keycode
;
479 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
480 entry
->rtext
= wstrdup(tmp
);
483 entry
= wMenuAddCallback(menu
, _("Resize/Move"), execMenuCommand
, NULL
);
484 if (wKeyBindings
[WKBD_MOVERESIZE
].keycode
!=0) {
485 kcode
= wKeyBindings
[WKBD_MOVERESIZE
].keycode
;
487 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
488 entry
->rtext
= wstrdup(tmp
);
491 entry
= wMenuAddCallback(menu
, _("Select"), execMenuCommand
, NULL
);
492 if (wKeyBindings
[WKBD_SELECT
].keycode
!=0) {
493 kcode
= wKeyBindings
[WKBD_SELECT
].keycode
;
495 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
496 entry
->rtext
= wstrdup(tmp
);
499 entry
= wMenuAddCallback(menu
, _("Move To"), NULL
, NULL
);
500 scr
->workspace_submenu
= makeWorkspaceMenu(scr
);
501 if (scr
->workspace_submenu
)
502 wMenuEntrySetCascade(menu
, entry
, scr
->workspace_submenu
);
504 entry
= wMenuAddCallback(menu
, _("Attributes..."), execMenuCommand
, NULL
);
506 entry
= wMenuAddCallback(menu
, _("Options"), NULL
, NULL
);
507 wMenuEntrySetCascade(menu
, entry
,
508 makeMakeShortcutMenu(scr
, makeOptionsMenu(scr
)));
511 entry = wMenuAddCallback(menu, _("Select Shortcut"), NULL, NULL);
512 wMenuEntrySetCascade(menu, entry, makeMakeShortcutMenu(scr));
515 entry
= wMenuAddCallback(menu
, _("Close"), execMenuCommand
, NULL
);
516 if (wKeyBindings
[WKBD_CLOSE
].keycode
!=0) {
517 kcode
= wKeyBindings
[WKBD_CLOSE
].keycode
;
518 if (kcode
&& (tmp
= XKeysymToString(XKeycodeToKeysym(dpy
, kcode
, 0))))
519 entry
->rtext
= wstrdup(tmp
);
522 entry
= wMenuAddCallback(menu
, _("Kill"), execMenuCommand
, NULL
);
529 CloseWindowMenu(WScreen
*scr
)
531 if (scr
->window_menu
) {
532 if (scr
->window_menu
->flags
.mapped
)
533 wMenuUnmap(scr
->window_menu
);
535 if (scr
->window_menu
->entries
[0]->clientdata
) {
536 WWindow
*wwin
= (WWindow
*)scr
->window_menu
->entries
[0]->clientdata
;
538 wwin
->flags
.menu_open_for_me
= 0;
540 scr
->window_menu
->entries
[0]->clientdata
= NULL
;
547 updateMenuForWindow(WMenu
*menu
, WWindow
*wwin
)
549 WApplication
*wapp
= wApplicationOf(wwin
->main_window
);
550 WScreen
*scr
= wwin
->screen_ptr
;
553 updateOptionsMenu(menu
, wwin
);
555 updateMakeShortcutMenu(menu
, wwin
);
557 wMenuSetEnabled(menu
, MC_HIDE
, wapp
!=NULL
558 && !WFLAGP(wapp
->main_window_desc
, no_appicon
));
560 wMenuSetEnabled(menu
, MC_CLOSE
,
561 (wwin
->protocols
.DELETE_WINDOW
562 && !WFLAGP(wwin
, no_closable
)));
564 if (wwin
->flags
.miniaturized
) {
565 static char *text
= NULL
;
566 if (!text
) text
= _("Deminiaturize");
568 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
570 static char *text
= NULL
;
571 if (!text
) text
= _("Miniaturize");
573 menu
->entries
[MC_MINIATURIZE
]->text
= text
;
576 wMenuSetEnabled(menu
, MC_MINIATURIZE
, !WFLAGP(wwin
, no_miniaturizable
));
578 if (wwin
->flags
.maximized
) {
579 static char *text
= NULL
;
580 if (!text
) text
= _("Unmaximize");
582 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
584 static char *text
= NULL
;
585 if (!text
) text
= _("Maximize");
587 menu
->entries
[MC_MAXIMIZE
]->text
= text
;
589 wMenuSetEnabled(menu
, MC_MAXIMIZE
, !WFLAGP(wwin
, no_resizable
));
592 wMenuSetEnabled(menu
, MC_MOVERESIZE
, !WFLAGP(wwin
, no_resizable
)
593 && !wwin
->flags
.miniaturized
);
595 if (wwin
->flags
.shaded
) {
596 static char *text
= NULL
;
597 if (!text
) text
= _("Unshade");
599 menu
->entries
[MC_SHADE
]->text
= text
;
601 static char *text
= NULL
;
602 if (!text
) text
= _("Shade");
604 menu
->entries
[MC_SHADE
]->text
= text
;
607 wMenuSetEnabled(menu
, MC_SHADE
, !WFLAGP(wwin
, no_shadeable
)
608 && !wwin
->flags
.miniaturized
);
610 wMenuSetEnabled(menu
, MC_DUMMY_MOVETO
, !IS_OMNIPRESENT(wwin
));
612 if (!wwin
->flags
.inspector_open
) {
613 wMenuSetEnabled(menu
, MC_PROPERTIES
, True
);
615 wMenuSetEnabled(menu
, MC_PROPERTIES
, False
);
618 /* set the client data of the entries to the window */
619 for (i
= 0; i
< menu
->entry_no
; i
++) {
620 menu
->entries
[i
]->clientdata
= wwin
;
623 for (i
= 0; i
< scr
->workspace_submenu
->entry_no
; i
++) {
624 scr
->workspace_submenu
->entries
[i
]->clientdata
= wwin
;
625 if (i
== scr
->current_workspace
) {
626 wMenuSetEnabled(scr
->workspace_submenu
, i
, False
);
628 wMenuSetEnabled(scr
->workspace_submenu
, i
, True
);
632 menu
->flags
.realized
= 0;
638 OpenWindowMenu(WWindow
*wwin
, int x
, int y
, int keyboard
)
641 WScreen
*scr
= wwin
->screen_ptr
;
643 wwin
->flags
.menu_open_for_me
= 1;
645 if (!scr
->window_menu
) {
646 scr
->window_menu
= createWindowMenu(scr
);
648 /* hack to save some memory allocation/deallocation */
649 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
650 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
651 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
653 updateWorkspaceMenu(scr
->workspace_submenu
);
656 menu
= scr
->window_menu
;
657 if (menu
->flags
.mapped
) {
659 if (menu
->entries
[0]->clientdata
==wwin
) {
664 updateMenuForWindow(menu
, wwin
);
666 x
-= menu
->frame
->core
->width
/2;
667 if (x
+ menu
->frame
->core
->width
> wwin
->frame_x
+wwin
->frame
->core
->width
)
668 x
= wwin
->frame_x
+wwin
->frame
->core
->width
- menu
->frame
->core
->width
;
669 if (x
< wwin
->frame_x
)
672 if (!wwin
->flags
.internal_window
)
673 wMenuMapAt(menu
, x
, y
, keyboard
);
678 OpenMiniwindowMenu(WWindow
*wwin
, int x
, int y
)
681 WScreen
*scr
= wwin
->screen_ptr
;
683 wwin
->flags
.menu_open_for_me
= 1;
685 if (!scr
->window_menu
) {
686 scr
->window_menu
= createWindowMenu(scr
);
688 /* hack to save some memory allocation/deallocation */
689 wfree(scr
->window_menu
->entries
[MC_MINIATURIZE
]->text
);
690 wfree(scr
->window_menu
->entries
[MC_MAXIMIZE
]->text
);
691 wfree(scr
->window_menu
->entries
[MC_SHADE
]->text
);
693 updateWorkspaceMenu(scr
->workspace_submenu
);
696 menu
= scr
->window_menu
;
697 if (menu
->flags
.mapped
) {
699 if (menu
->entries
[0]->clientdata
==wwin
) {
704 updateMenuForWindow(menu
, wwin
);
706 x
-= menu
->frame
->core
->width
/2;
708 wMenuMapAt(menu
, x
, y
, False
);