Add OPEN_PLMENU option to parse command generated proplist style menus
[wmaker-crm.git] / src / rootmenu.c
Commit [+]AuthorDateLineData
9d2e6ef9 scottc1998-09-29 22:36:29 +00001/* rootmenu.c- user defined menu
6830b057 dan2004-10-12 21:28:27 +00002 *
9af1c6c4 dan1998-10-21 14:43:47 +00003 * Window Maker window manager
6830b057 dan2004-10-12 21:28:27 +00004 *
4153e2fd dan2003-01-16 23:30:45 +00005 * Copyright (c) 1997-2003 Alfredo K. Kojima
6 * Copyright (c) 1998-2003 Dan Pascu
6830b057 dan2004-10-12 21:28:27 +00007 *
9d2e6ef9 scottc1998-09-29 22:36:29 +00008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
3c408fa1
TT
Tamas TEVESZ2010-10-08 01:32:11 +020018 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
9d2e6ef9 scottc1998-09-29 22:36:29 +000021 */
22
23#include "wconfig.h"
24
9d2e6ef9 scottc1998-09-29 22:36:29 +000025#include <stdio.h>
26#include <stdlib.h>
27#include <unistd.h>
28#include <sys/stat.h>
29#include <sys/wait.h>
30#include <sys/types.h>
31#include <string.h>
f65b99e6 Tamas TEVESZ2011-03-24 16:07:20 +010032#include <strings.h>
9d2e6ef9 scottc1998-09-29 22:36:29 +000033#include <ctype.h>
34#include <time.h>
35#include <dirent.h>
36
37#include <X11/Xlib.h>
38#include <X11/Xutil.h>
39#include <X11/Xatom.h>
40
41#include "WindowMaker.h"
42#include "actions.h"
43#include "menu.h"
6830b057 dan2004-10-12 21:28:27 +000044#include "funcs.h"
6bdc1318 Rodolfo García Peñas (kix)2012-06-24 12:35:24 +020045#include "main.h"
9d2e6ef9 scottc1998-09-29 22:36:29 +000046#include "dialog.h"
47#include "keybind.h"
48#include "stacking.h"
49#include "workspace.h"
50#include "defaults.h"
51#include "framewin.h"
52#include "session.h"
68208a01 Rodolfo García Peñas (kix)2013-01-08 09:08:26 +010053#include "shutdown.h"
9d2e6ef9 scottc1998-09-29 22:36:29 +000054#include "xmodifier.h"
33cc542e dan2001-10-04 03:07:34 +000055
56#include <WINGs/WUtil.h>
9d2e6ef9 scottc1998-09-29 22:36:29 +000057
3c323e1e Carlos R. Mafra2008-11-09 20:18:05 +010058#define MAX_SHORTCUT_LENGTH 32
9d2e6ef9 scottc1998-09-29 22:36:29 +000059
60extern char *Locale;
9d2e6ef9 scottc1998-09-29 22:36:29 +000061extern WDDomain *WDRootMenu;
0261c326 dan1999-01-06 15:22:33 +000062extern Cursor wCursor[WCUR_LAST];
9d2e6ef9 scottc1998-09-29 22:36:29 +000063extern WPreferences wPreferences;
64
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020065static WMenu *readMenuPipe(WScreen * scr, char **file_name);
c21ae6b5 Andreas Bierfert2013-02-10 10:56:20 +010066static WMenu *readPLMenuPipe(WScreen * scr, char **file_name);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020067static WMenu *readMenuFile(WScreen * scr, char *file_name);
68static WMenu *readMenuDirectory(WScreen * scr, char *title, char **file_name, char *command);
8311058a Carlos R. Mafra2012-04-30 14:36:43 -030069static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals);
9b792369 Christophe CURIS2012-06-23 15:21:43 +020070static void menu_parser_register_macros(WMenuParser parser);
9d2e6ef9 scottc1998-09-29 22:36:29 +000071
72typedef struct Shortcut {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +020073 struct Shortcut *next;
6830b057 dan2004-10-12 21:28:27 +000074
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +020075 int modifier;
76 KeyCode keycode;
77 WMenuEntry *entry;
78 WMenu *menu;
9d2e6ef9 scottc1998-09-29 22:36:29 +000079} Shortcut;
80
9d2e6ef9 scottc1998-09-29 22:36:29 +000081static Shortcut *shortcutList = NULL;
82
9d2e6ef9 scottc1998-09-29 22:36:29 +000083/*
84 * Syntax:
85 * # main menu
86 * "Menu Name" MENU
87 * "Title" EXEC command_to_exec -params
88 * "Submenu" MENU
89 * "Title" EXEC command_to_exec -params
90 * "Submenu" END
91 * "Workspaces" WORKSPACE_MENU
92 * "Title" built_in_command
93 * "Quit" EXIT
94 * "Quick Quit" EXIT QUICK
95 * "Menu Name" END
6830b057 dan2004-10-12 21:28:27 +000096 *
9d2e6ef9 scottc1998-09-29 22:36:29 +000097 * Commands may be preceded by SHORTCUT key
6830b057 dan2004-10-12 21:28:27 +000098 *
9d2e6ef9 scottc1998-09-29 22:36:29 +000099 * Built-in commands:
100 *
101 * INFO_PANEL - shows the Info Panel
102 * LEGAL_PANEL - shows the Legal info panel
103 * SHUTDOWN [QUICK] - closes the X server [without confirmation]
104 * REFRESH - forces the desktop to be repainted
105 * EXIT [QUICK] - exit the window manager [without confirmation]
106 * EXEC <program> - execute an external program
7f018497 kojima1999-04-24 20:08:20 +0000107 * SHEXEC <command> - execute a shell command
9d2e6ef9 scottc1998-09-29 22:36:29 +0000108 * WORKSPACE_MENU - places the workspace submenu
109 * ARRANGE_ICONS
110 * RESTART [<window manager>] - restarts the window manager
111 * SHOW_ALL - unhide all windows on workspace
112 * HIDE_OTHERS - hides all windows excep the focused one
113 * OPEN_MENU file - read menu data from file which must be a valid menu file.
114 * OPEN_MENU /some/dir [/some/other/dir ...] [WITH command -options]
115 * - read menu data from directory(ies) and
116 * eventually precede each with a command.
117 * OPEN_MENU | command
118 * - opens command and uses its stdout to construct and insert
119 * the resulting menu in current position. The output of
120 * command must be a valid menu description.
121 * The space between '|' and command is optional.
739fd1a5 kojima2001-12-04 12:05:45 +0000122 * || will do the same, but will not cache the contents.
c21ae6b5
AB
Andreas Bierfert2013-02-10 10:56:20 +0100123 * OPEN_PLMENU | command
124 * - opens command and uses its stdout which must be in proplist
125 * fromat to construct and insert the resulting menu in current
126 * position.
127 * The space between '|' and command is optional.
128 * || will do the same, but will not cache the contents.
9d2e6ef9 scottc1998-09-29 22:36:29 +0000129 * SAVE_SESSION - saves the current state of the desktop, which include
130 * all running applications, all their hints (geometry,
131 * position on screen, workspace they live on, the dock
132 * or clip from where they were launched, and
133 * if minimized, shaded or hidden. Also saves the current
134 * workspace the user is on. All will be restored on every
135 * start of windowmaker until another SAVE_SESSION or
136 * CLEAR_SESSION is used. If SaveSessionOnExit = Yes; in
137 * WindowMaker domain file, then saving is automatically
138 * done on every windowmaker exit, overwriting any
416e3a82 dan1999-01-25 19:06:50 +0000139 * SAVE_SESSION or CLEAR_SESSION (see below). Also save
140 * dock state now.
9d2e6ef9 scottc1998-09-29 22:36:29 +0000141 * CLEAR_SESSION - clears any previous saved session. This will not have
142 * any effect if SaveSessionOnExit is True.
6830b057 dan2004-10-12 21:28:27 +0000143 *
9d2e6ef9 scottc1998-09-29 22:36:29 +0000144 */
145
9d2e6ef9 scottc1998-09-29 22:36:29 +0000146#define M_QUICK 1
147
148/* menu commands */
149
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200150static void execCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000151{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200152 char *cmdline;
9af1c6c4 dan1998-10-21 14:43:47 +0000153
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200154 cmdline = ExpandOptions(menu->frame->screen_ptr, (char *)entry->clientdata);
9af1c6c4 dan1998-10-21 14:43:47 +0000155
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200156 XGrabPointer(dpy, menu->frame->screen_ptr->root_win, True, 0,
157 GrabModeAsync, GrabModeAsync, None, wCursor[WCUR_WAIT], CurrentTime);
158 XSync(dpy, 0);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000159
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200160 if (cmdline) {
161 ExecuteShellCommand(menu->frame->screen_ptr, cmdline);
162 wfree(cmdline);
163 }
164 XUngrabPointer(dpy, CurrentTime);
165 XSync(dpy, 0);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000166}
167
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200168static void exitCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000169{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200170 static int inside = 0;
171 int result;
9af1c6c4 dan1998-10-21 14:43:47 +0000172
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200173 /* prevent reentrant calls */
174 if (inside)
175 return;
176 inside = 1;
9af1c6c4 dan1998-10-21 14:43:47 +0000177
72150b1d dan2001-11-05 23:19:46 +0000178#define R_CANCEL 0
179#define R_EXIT 1
180
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200181 result = R_CANCEL;
182
183 if ((long)entry->clientdata == M_QUICK) {
184 result = R_EXIT;
185 } else {
186 int r, oldSaveSessionFlag;
187
188 oldSaveSessionFlag = wPreferences.save_session_on_exit;
189 r = wExitDialog(menu->frame->screen_ptr, _("Exit"),
190 _("Exit window manager?"), _("Exit"), _("Cancel"), NULL);
191
192 if (r == WAPRDefault) {
193 result = R_EXIT;
194 } else if (r == WAPRAlternate) {
195 /* Don't modify the "save session on exit" flag if the
196 * user canceled the operation. */
197 wPreferences.save_session_on_exit = oldSaveSessionFlag;
198 }
199 }
5178465b Carlos R. Mafra2010-03-17 17:44:14 +0100200 if (result == R_EXIT)
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200201 Shutdown(WSExitMode);
5178465b Carlos R. Mafra2010-03-17 17:44:14 +0100202
72150b1d dan2001-11-05 23:19:46 +0000203#undef R_EXIT
204#undef R_CANCEL
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200205 inside = 0;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000206}
207
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200208static void shutdownCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000209{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200210 static int inside = 0;
211 int result;
9af1c6c4 dan1998-10-21 14:43:47 +0000212
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200213 /* prevent reentrant calls */
214 if (inside)
215 return;
216 inside = 1;
9af1c6c4 dan1998-10-21 14:43:47 +0000217
9007a657 dan1998-11-23 11:32:19 +0000218#define R_CANCEL 0
219#define R_CLOSE 1
220#define R_KILL 2
9d2e6ef9 scottc1998-09-29 22:36:29 +0000221
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200222 result = R_CANCEL;
223 if ((long)entry->clientdata == M_QUICK)
224 result = R_CLOSE;
225 else {
6bc550d9
TS
Tobias Stoeckmann2012-05-05 09:57:20 +0200226 int r, oldSaveSessionFlag;
227
228 oldSaveSessionFlag = wPreferences.save_session_on_exit;
229
230 r = wExitDialog(menu->frame->screen_ptr,
231 _("Kill X session"),
232 _("Kill Window System session?\n"
233 "(all applications will be closed)"), _("Kill"), _("Cancel"), NULL);
234 if (r == WAPRDefault) {
235 result = R_KILL;
236 } else if (r == WAPRAlternate) {
237 /* Don't modify the "save session on exit" flag if the
238 * user canceled the operation. */
239 wPreferences.save_session_on_exit = oldSaveSessionFlag;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200240 }
241 }
242
243 if (result != R_CANCEL) {
6bc550d9 Tobias Stoeckmann2012-05-05 09:57:20 +0200244 Shutdown(WSKillMode);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200245 }
9007a657 dan1998-11-23 11:32:19 +0000246#undef R_CLOSE
247#undef R_CANCEL
248#undef R_KILL
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200249 inside = 0;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000250}
251
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200252static void restartCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000253{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200254 Shutdown(WSRestartPreparationMode);
255 Restart((char *)entry->clientdata, False);
256 Restart(NULL, True);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000257}
258
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200259static void refreshCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000260{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200261 wRefreshDesktop(menu->frame->screen_ptr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000262}
263
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200264static void arrangeIconsCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000265{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200266 wArrangeIcons(menu->frame->screen_ptr, True);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000267}
268
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200269static void showAllCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000270{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200271 wShowAllWindows(menu->frame->screen_ptr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000272}
273
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200274static void hideOthersCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000275{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200276 wHideOtherApplications(menu->frame->screen_ptr->focused_window);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000277}
278
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200279static void saveSessionCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000280{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200281 if (!wPreferences.save_session_on_exit)
282 wSessionSaveState(menu->frame->screen_ptr);
416e3a82 dan1999-01-25 19:06:50 +0000283
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200284 wScreenSaveState(menu->frame->screen_ptr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000285}
286
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200287static void clearSessionCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000288{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200289 wSessionClearState(menu->frame->screen_ptr);
290 wScreenSaveState(menu->frame->screen_ptr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000291}
292
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200293static void infoPanelCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000294{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200295 wShowInfoPanel(menu->frame->screen_ptr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000296}
297
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200298static void legalPanelCommand(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000299{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200300 wShowLegalPanel(menu->frame->screen_ptr);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000301}
302
9d2e6ef9 scottc1998-09-29 22:36:29 +0000303/********************************************************************/
304
761fd37e Rodolfo García Peñas (kix)2012-04-14 10:53:12 +0200305static char * getLocalizedMenuFile(char *menu)
e7495baf dan1999-02-17 11:06:40 +0000306{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200307 char *buffer, *ptr, *locale;
308 int len;
309
310 if (!Locale)
311 return NULL;
312
313 len = strlen(menu) + strlen(Locale) + 8;
314 buffer = wmalloc(len);
315
316 /* try menu.locale_name */
317 snprintf(buffer, len, "%s.%s", menu, Locale);
761fd37e Rodolfo García Peñas (kix)2012-04-14 10:53:12 +0200318 if (access(buffer, F_OK) == 0)
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200319 return buffer;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200320
321 /* position of locale in our buffer */
322 locale = buffer + strlen(menu) + 1;
323
324 /* check if it is in the form aa_bb.encoding and check for aa_bb */
325 ptr = strchr(locale, '.');
326 if (ptr) {
327 *ptr = 0;
761fd37e Rodolfo García Peñas (kix)2012-04-14 10:53:12 +0200328 if (access(buffer, F_OK) == 0)
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200329 return buffer;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200330 }
761fd37e Rodolfo García Peñas (kix)2012-04-14 10:53:12 +0200331
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200332 /* now check for aa */
333 ptr = strchr(locale, '_');
334 if (ptr) {
335 *ptr = 0;
761fd37e Rodolfo García Peñas (kix)2012-04-14 10:53:12 +0200336 if (access(buffer, F_OK) == 0)
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200337 return buffer;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200338 }
339
340 wfree(buffer);
341
342 return NULL;
e7495baf dan1999-02-17 11:06:40 +0000343}
344
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200345Bool wRootMenuPerformShortcut(XEvent * event)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000346{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200347 WScreen *scr = wScreenForRootWindow(event->xkey.root);
348 Shortcut *ptr;
349 int modifiers;
350 int done = 0;
9e615bcf dan2001-12-28 03:29:50 +0000351
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200352 /* ignore CapsLock */
353 modifiers = event->xkey.state & ValidModMask;
9e615bcf dan2001-12-28 03:29:50 +0000354
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200355 for (ptr = shortcutList; ptr != NULL; ptr = ptr->next) {
356 if (ptr->keycode == 0 || ptr->menu->menu->screen_ptr != scr)
357 continue;
7eb70547 dan2001-12-30 01:50:46 +0000358
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200359 if (ptr->keycode == event->xkey.keycode && ptr->modifier == modifiers) {
360 (*ptr->entry->callback) (ptr->menu, ptr->entry);
361 done = True;
362 }
363 }
7eb70547 dan2001-12-30 01:50:46 +0000364
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200365 return done;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000366}
367
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200368void wRootMenuBindShortcuts(Window window)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000369{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200370 Shortcut *ptr;
6830b057 dan2004-10-12 21:28:27 +0000371
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200372 ptr = shortcutList;
373 while (ptr) {
374 if (ptr->modifier != AnyModifier) {
375 XGrabKey(dpy, ptr->keycode, ptr->modifier | LockMask,
376 window, True, GrabModeAsync, GrabModeAsync);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000377#ifdef NUMLOCK_HACK
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200378 wHackedGrabKey(ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000379#endif
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200380 }
381 XGrabKey(dpy, ptr->keycode, ptr->modifier, window, True, GrabModeAsync, GrabModeAsync);
382 ptr = ptr->next;
383 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000384}
385
8311058a Carlos R. Mafra2012-04-30 14:36:43 -0300386static void rebindKeygrabs(WScreen * scr)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000387{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200388 WWindow *wwin;
6830b057 dan2004-10-12 21:28:27 +0000389
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200390 wwin = scr->focused_window;
6830b057 dan2004-10-12 21:28:27 +0000391
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200392 while (wwin != NULL) {
393 XUngrabKey(dpy, AnyKey, AnyModifier, wwin->frame->core->window);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000394
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200395 if (!WFLAGP(wwin, no_bind_keys)) {
396 wWindowSetKeyGrabs(wwin);
397 }
398 wwin = wwin->prev;
399 }
9d2e6ef9 scottc1998-09-29 22:36:29 +0000400}
401
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200402static void removeShortcutsForMenu(WMenu * menu)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000403{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200404 Shortcut *ptr, *tmp;
405 Shortcut *newList = NULL;
406
407 ptr = shortcutList;
408 while (ptr != NULL) {
409 tmp = ptr->next;
410 if (ptr->menu == menu) {
411 wfree(ptr);
412 } else {
413 ptr->next = newList;
414 newList = ptr;
415 }
416 ptr = tmp;
417 }
418 shortcutList = newList;
419 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000420}
421
19f0998c Christophe CURIS2012-07-08 14:20:24 +0200422static Bool addShortcut(const char *file, char *shortcutDefinition, WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000423{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200424 Shortcut *ptr;
425 KeySym ksym;
426 char *k;
427 char buf[MAX_SHORTCUT_LENGTH], *b;
428
429 ptr = wmalloc(sizeof(Shortcut));
430
e01d14ab Christophe CURIS2011-07-29 22:38:09 +0200431 wstrlcpy(buf, shortcutDefinition, MAX_SHORTCUT_LENGTH);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200432 b = (char *)buf;
433
434 /* get modifiers */
435 ptr->modifier = 0;
436 while ((k = strchr(b, '+')) != NULL) {
437 int mod;
438
439 *k = 0;
440 mod = wXModifierFromKey(b);
441 if (mod < 0) {
442 wwarning(_("%s: invalid key modifier \"%s\""), file, b);
443 wfree(ptr);
444 return False;
445 }
446 ptr->modifier |= mod;
447
448 b = k + 1;
449 }
450
451 /* get key */
452 ksym = XStringToKeysym(b);
453
454 if (ksym == NoSymbol) {
455 wwarning(_("%s:invalid kbd shortcut specification \"%s\" for entry %s"),
456 file, shortcutDefinition, entry->text);
457 wfree(ptr);
458 return False;
459 }
460
461 ptr->keycode = XKeysymToKeycode(dpy, ksym);
462 if (ptr->keycode == 0) {
463 wwarning(_("%s:invalid key in shortcut \"%s\" for entry %s"), file,
464 shortcutDefinition, entry->text);
465 wfree(ptr);
466 return False;
467 }
468
469 ptr->menu = menu;
470 ptr->entry = entry;
471
472 ptr->next = shortcutList;
473 shortcutList = ptr;
474
475 menu->menu->screen_ptr->flags.root_menu_changed_shortcuts = 1;
476
477 return True;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000478}
479
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200480static char *next_token(char *line, char **next)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000481{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200482 char *tmp, c;
483 char *ret;
484
485 *next = NULL;
486 while (*line == ' ' || *line == '\t')
487 line++;
488
489 tmp = line;
490
491 if (*tmp == '"') {
492 tmp++;
493 line++;
494 while (*tmp != 0 && *tmp != '"')
495 tmp++;
496 if (*tmp != '"') {
497 wwarning(_("%s: unmatched '\"' in menu file"), line);
498 return NULL;
499 }
500 } else {
501 do {
502 if (*tmp == '\\')
503 tmp++;
504
505 if (*tmp != 0)
506 tmp++;
507
508 } while (*tmp != 0 && *tmp != ' ' && *tmp != '\t');
509 }
510
511 c = *tmp;
512 *tmp = 0;
513 ret = wstrdup(line);
514 *tmp = c;
515
516 if (c == 0)
517 return ret;
518 else
519 tmp++;
520
521 /* skip blanks */
522 while (*tmp == ' ' || *tmp == '\t')
523 tmp++;
524
525 if (*tmp != 0)
526 *next = tmp;
527
528 return ret;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000529}
530
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200531static void separateCommand(char *line, char ***file, char **command)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000532{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200533 char *token, *tmp = line;
534 WMArray *array = WMCreateArray(4);
535 int count, i;
536
537 *file = NULL;
538 *command = NULL;
539 do {
540 token = next_token(tmp, &tmp);
541 if (token) {
542 if (strcmp(token, "WITH") == 0) {
543 if (tmp != NULL && *tmp != 0)
544 *command = wstrdup(tmp);
545 else
546 wwarning(_("%s: missing command"), line);
547 break;
548 }
549 WMAddToArray(array, token);
550 }
551 } while (token != NULL && tmp != NULL);
552
553 count = WMGetArrayItemCount(array);
554 if (count > 0) {
555 *file = wmalloc(sizeof(char *) * (count + 1));
556 (*file)[count] = NULL;
557 for (i = 0; i < count; i++) {
558 (*file)[i] = WMGetFromArray(array, i);
559 }
560 }
561 WMFreeArray(array);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000562}
563
70abda81
TT
Tamas TEVESZ2010-09-30 17:36:22 +0200564static WMenu *constructPLMenu(WScreen *screen, char *path)
565{
566 WMPropList *pl = NULL;
567 WMenu *menu = NULL;
568
569 if (!path)
570 return NULL;
571
572 pl = WMReadPropListFromFile(path);
573 if (!pl)
574 return NULL;
575
576 menu = configureMenu(screen, pl, False);
577 if (!menu)
578 return NULL;
579
580 menu->on_destroy = removeShortcutsForMenu;
581 return menu;
582}
583
c21ae6b5
AB
Andreas Bierfert2013-02-10 10:56:20 +0100584
585
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200586static void constructMenu(WMenu * menu, WMenuEntry * entry)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000587{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200588 WMenu *submenu;
589 struct stat stat_buf;
590 char **path;
591 char *cmd;
592 char *lpath = NULL;
593 int i, first = -1;
594 time_t last = 0;
595
596 separateCommand((char *)entry->clientdata, &path, &cmd);
597 if (path == NULL || *path == NULL || **path == 0) {
598 wwarning(_("invalid OPEN_MENU specification: %s"), (char *)entry->clientdata);
e6e3e1aa
TS
Tobias Stoeckmann2012-05-03 17:54:13 +0200599 if (cmd)
600 wfree(cmd);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200601 return;
602 }
603
604 if (path[0][0] == '|') {
605 /* pipe menu */
606
607 if (!menu->cascades[entry->cascade] || menu->cascades[entry->cascade]->timestamp == 0) {
608 /* parse pipe */
609
610 submenu = readMenuPipe(menu->frame->screen_ptr, path);
611
612 if (submenu != NULL) {
613 if (path[0][1] == '|')
614 submenu->timestamp = 0;
615 else
616 submenu->timestamp = 1; /* there's no automatic reloading */
617 }
618 } else {
619 submenu = NULL;
620 }
621
622 } else {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200623
70abda81
TT
Tamas TEVESZ2010-09-30 17:36:22 +0200624 /* try interpreting path as a proplist file */
625 submenu = constructPLMenu(menu->frame->screen_ptr, path[0]);
626 /* if unsuccessful, try it as an old-style file */
627 if (!submenu) {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200628
70abda81
TT
Tamas TEVESZ2010-09-30 17:36:22 +0200629 i = 0;
630 while (path[i] != NULL) {
631 char *tmp;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200632
70abda81
TT
Tamas TEVESZ2010-09-30 17:36:22 +0200633 if (strcmp(path[i], "-noext") == 0) {
634 i++;
635 continue;
636 }
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200637
70abda81
TT
Tamas TEVESZ2010-09-30 17:36:22 +0200638 tmp = wexpandpath(path[i]);
639 wfree(path[i]);
640 lpath = getLocalizedMenuFile(tmp);
641 if (lpath) {
642 wfree(tmp);
643 path[i] = lpath;
644 lpath = NULL;
645 } else {
646 path[i] = tmp;
647 }
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200648
70abda81
TT
Tamas TEVESZ2010-09-30 17:36:22 +0200649 if (stat(path[i], &stat_buf) == 0) {
650 if (last < stat_buf.st_mtime)
651 last = stat_buf.st_mtime;
652 if (first < 0)
653 first = i;
654 } else {
655 werror(_("%s:could not stat menu"), path[i]);
656 /*goto finish; */
657 }
658
659 i++;
660 }
661
662 if (first < 0) {
663 werror(_("%s:could not stat menu:%s"), "OPEN_MENU", (char *)entry->clientdata);
664 goto finish;
665 }
666 stat(path[first], &stat_buf);
667 if (!menu->cascades[entry->cascade]
668 || menu->cascades[entry->cascade]->timestamp < last) {
669
670 if (S_ISDIR(stat_buf.st_mode)) {
671 /* menu directory */
672 submenu = readMenuDirectory(menu->frame->screen_ptr, entry->text, path, cmd);
673 if (submenu)
674 submenu->timestamp = last;
675 } else if (S_ISREG(stat_buf.st_mode)) {
676 /* menu file */
677
678 if (cmd || path[1])
679 wwarning(_("too many parameters in OPEN_MENU: %s"),
680 (char *)entry->clientdata);
681
682 submenu = readMenuFile(menu->frame->screen_ptr, path[first]);
683 if (submenu)
684 submenu->timestamp = stat_buf.st_mtime;
685 } else {
686 submenu = NULL;
687 }
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200688 } else {
689 submenu = NULL;
690 }
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200691 }
692 }
693
694 if (submenu) {
695 wMenuEntryRemoveCascade(menu, entry);
696 wMenuEntrySetCascade(menu, entry, submenu);
697 }
698
699 finish:
700 i = 0;
701 while (path[i] != NULL)
702 wfree(path[i++]);
703 wfree(path);
704 if (cmd)
705 wfree(cmd);
9d2e6ef9 scottc1998-09-29 22:36:29 +0000706}
707
c21ae6b5
AB
Andreas Bierfert2013-02-10 10:56:20 +0100708static void constructPLMenuFromPipe(WMenu * menu, WMenuEntry * entry)
709{
710 WMenu *submenu = NULL;
711 char **path;
712 char *cmd;
713 int i;
714
715 separateCommand((char *)entry->clientdata, &path, &cmd);
716 if (path == NULL || *path == NULL || **path == 0) {
717 wwarning(_("invalid OPEN_PLMENU specification: %s"),
718 (char *)entry->clientdata);
719 if (cmd)
720 wfree(cmd);
721 return;
722 }
723
724 if (path[0][0] == '|') {
725 /* pipe menu */
726
727 if (!menu->cascades[entry->cascade]
728 || menu->cascades[entry->cascade]->timestamp == 0) {
729 /* parse pipe */
730 submenu = readPLMenuPipe(menu->frame->screen_ptr, path);
731
732 if (submenu != NULL) {
733 if (path[0][1] == '|')
734 submenu->timestamp = 0;
735 else
736 submenu->timestamp = 1; /* there's no automatic reloading */
737 }
738 }
739 }
740
741 if (submenu) {
742 wMenuEntryRemoveCascade(menu, entry);
743 wMenuEntrySetCascade(menu, entry, submenu);
744 }
745
746 i = 0;
747 while (path[i] != NULL)
748 wfree(path[i++]);
749
750 wfree(path);
751 if (cmd)
752 wfree(cmd);
753
754}
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200755static void cleanupWorkspaceMenu(WMenu * menu)
882b9a8e kojima2001-07-23 20:31:32 +0000756{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200757 if (menu->frame->screen_ptr->workspace_menu == menu)
758 menu->frame->screen_ptr->workspace_menu = NULL;
882b9a8e kojima2001-07-23 20:31:32 +0000759}
760
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200761static WMenuEntry *addWorkspaceMenu(WScreen * scr, WMenu * menu, char *title)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000762{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200763 WMenu *wsmenu;
764 WMenuEntry *entry;
765
766 if (scr->flags.added_workspace_menu) {
767 wwarning(_
768 ("There are more than one WORKSPACE_MENU commands in the applications menu. Only one is allowed."));
769 return NULL;
770 } else {
771 scr->flags.added_workspace_menu = 1;
772
773 wsmenu = wWorkspaceMenuMake(scr, True);
774 wsmenu->on_destroy = cleanupWorkspaceMenu;
775
776 scr->workspace_menu = wsmenu;
777 entry = wMenuAddCallback(menu, title, NULL, NULL);
778 wMenuEntrySetCascade(menu, entry, wsmenu);
779
780 wWorkspaceMenuUpdate(scr, wsmenu);
781 }
782 return entry;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000783}
784
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200785static void cleanupWindowsMenu(WMenu * menu)
882b9a8e kojima2001-07-23 20:31:32 +0000786{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200787 if (menu->frame->screen_ptr->switch_menu == menu)
788 menu->frame->screen_ptr->switch_menu = NULL;
882b9a8e kojima2001-07-23 20:31:32 +0000789}
790
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200791static WMenuEntry *addWindowsMenu(WScreen * scr, WMenu * menu, char *title)
b04c4ea5 kojima2001-02-17 21:44:22 +0000792{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200793 WMenu *wwmenu;
794 WWindow *wwin;
795 WMenuEntry *entry;
796
797 if (scr->flags.added_windows_menu) {
798 wwarning(_
799 ("There are more than one WINDOWS_MENU commands in the applications menu. Only one is allowed."));
800 return NULL;
801 } else {
802 scr->flags.added_windows_menu = 1;
803
804 wwmenu = wMenuCreate(scr, _("Window List"), False);
805 wwmenu->on_destroy = cleanupWindowsMenu;
806 scr->switch_menu = wwmenu;
807 wwin = scr->focused_window;
808 while (wwin) {
809 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
810
811 wwin = wwin->prev;
812 }
813 entry = wMenuAddCallback(menu, title, NULL, NULL);
814 wMenuEntrySetCascade(menu, entry, wwmenu);
815 }
816 return entry;
b04c4ea5 kojima2001-02-17 21:44:22 +0000817}
9d2e6ef9 scottc1998-09-29 22:36:29 +0000818
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200819static WMenuEntry *addMenuEntry(WMenu * menu, char *title, char *shortcut, char *command,
19f0998c Christophe CURIS2012-07-08 14:20:24 +0200820 char *params, const char *file_name)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000821{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200822 WScreen *scr;
823 WMenuEntry *entry = NULL;
824 Bool shortcutOk = False;
825
826 if (!menu)
827 return NULL;
828 scr = menu->frame->screen_ptr;
829 if (strcmp(command, "OPEN_MENU") == 0) {
830 if (!params) {
831 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
832 } else {
833 WMenu *dummy;
834 char *path;
835
836 path = wfindfile(DEF_CONFIG_PATHS, params);
837 if (!path) {
838 path = wstrdup(params);
839 }
840 dummy = wMenuCreate(scr, title, False);
841 dummy->on_destroy = removeShortcutsForMenu;
842 entry = wMenuAddCallback(menu, title, constructMenu, path);
2755b1e4 Tobias Stoeckmann2012-05-03 17:45:28 +0200843 entry->free_cdata = wfree;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200844 wMenuEntrySetCascade(menu, entry, dummy);
845 }
c21ae6b5
AB
Andreas Bierfert2013-02-10 10:56:20 +0100846 } else if (strcmp(command, "OPEN_PLMENU") == 0) {
847 if (!params) {
848 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
849 } else {
850 WMenu *dummy;
851 char *path;
852
853 path = wfindfile(DEF_CONFIG_PATHS, params);
854 if (!path)
855 path = wstrdup(params);
856
857 dummy = wMenuCreate(scr, title, False);
858 dummy->on_destroy = removeShortcutsForMenu;
859 entry = wMenuAddCallback(menu, title, constructPLMenuFromPipe, path);
860 entry->free_cdata = wfree;
861 wMenuEntrySetCascade(menu, entry, dummy);
862 }
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200863 } else if (strcmp(command, "EXEC") == 0) {
864 if (!params)
865 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
866 else {
867 entry = wMenuAddCallback(menu, title, execCommand, wstrconcat("exec ", params));
2755b1e4 Tobias Stoeckmann2012-05-03 17:45:28 +0200868 entry->free_cdata = wfree;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200869 shortcutOk = True;
870 }
871 } else if (strcmp(command, "SHEXEC") == 0) {
872 if (!params)
873 wwarning(_("%s:missing parameter for menu command \"%s\""), file_name, command);
874 else {
875 entry = wMenuAddCallback(menu, title, execCommand, wstrdup(params));
2755b1e4 Tobias Stoeckmann2012-05-03 17:45:28 +0200876 entry->free_cdata = wfree;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200877 shortcutOk = True;
878 }
879 } else if (strcmp(command, "EXIT") == 0) {
880
881 if (params && strcmp(params, "QUICK") == 0)
882 entry = wMenuAddCallback(menu, title, exitCommand, (void *)M_QUICK);
883 else
884 entry = wMenuAddCallback(menu, title, exitCommand, NULL);
885
886 shortcutOk = True;
887 } else if (strcmp(command, "SHUTDOWN") == 0) {
888
889 if (params && strcmp(params, "QUICK") == 0)
890 entry = wMenuAddCallback(menu, title, shutdownCommand, (void *)M_QUICK);
891 else
892 entry = wMenuAddCallback(menu, title, shutdownCommand, NULL);
893
894 shortcutOk = True;
895 } else if (strcmp(command, "REFRESH") == 0) {
896 entry = wMenuAddCallback(menu, title, refreshCommand, NULL);
897
898 shortcutOk = True;
899 } else if (strcmp(command, "WORKSPACE_MENU") == 0) {
900 entry = addWorkspaceMenu(scr, menu, title);
901
902 shortcutOk = True;
903 } else if (strcmp(command, "WINDOWS_MENU") == 0) {
904 entry = addWindowsMenu(scr, menu, title);
905
906 shortcutOk = True;
907 } else if (strcmp(command, "ARRANGE_ICONS") == 0) {
908 entry = wMenuAddCallback(menu, title, arrangeIconsCommand, NULL);
909
910 shortcutOk = True;
911 } else if (strcmp(command, "HIDE_OTHERS") == 0) {
912 entry = wMenuAddCallback(menu, title, hideOthersCommand, NULL);
913
914 shortcutOk = True;
915 } else if (strcmp(command, "SHOW_ALL") == 0) {
916 entry = wMenuAddCallback(menu, title, showAllCommand, NULL);
917
918 shortcutOk = True;
919 } else if (strcmp(command, "RESTART") == 0) {
920 entry = wMenuAddCallback(menu, title, restartCommand, params ? wstrdup(params) : NULL);
2755b1e4 Tobias Stoeckmann2012-05-03 17:45:28 +0200921 entry->free_cdata = wfree;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200922 shortcutOk = True;
923 } else if (strcmp(command, "SAVE_SESSION") == 0) {
924 entry = wMenuAddCallback(menu, title, saveSessionCommand, NULL);
925
926 shortcutOk = True;
927 } else if (strcmp(command, "CLEAR_SESSION") == 0) {
928 entry = wMenuAddCallback(menu, title, clearSessionCommand, NULL);
929 shortcutOk = True;
930 } else if (strcmp(command, "INFO_PANEL") == 0) {
931 entry = wMenuAddCallback(menu, title, infoPanelCommand, NULL);
932 shortcutOk = True;
933 } else if (strcmp(command, "LEGAL_PANEL") == 0) {
934 entry = wMenuAddCallback(menu, title, legalPanelCommand, NULL);
935 shortcutOk = True;
936 } else {
937 wwarning(_("%s:unknown command \"%s\" in menu config."), file_name, command);
938
939 return NULL;
940 }
941
942 if (shortcut && entry) {
943 if (!shortcutOk) {
944 wwarning(_("%s:can't add shortcut for entry \"%s\""), file_name, title);
945 } else {
946 if (addShortcut(file_name, shortcut, menu, entry)) {
947
948 entry->rtext = GetShortcutString(shortcut);
949 /*
950 entry->rtext = wstrdup(shortcut);
951 */
952 }
953 }
954 }
955
956 return entry;
9d2e6ef9 scottc1998-09-29 22:36:29 +0000957}
958
9d2e6ef9 scottc1998-09-29 22:36:29 +0000959/******************* Menu Configuration From File *******************/
960
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +0200961static void freeline(char *title, char *command, char *parameter, char *shortcut)
9d2e6ef9 scottc1998-09-29 22:36:29 +0000962{
88360074
TS
Tobias Stoeckmann2012-05-05 19:32:21 +0200963 wfree(title);
964 wfree(command);
965 wfree(parameter);
966 wfree(shortcut);
967}
968
19f0998c Christophe CURIS2012-07-08 14:20:24 +0200969static WMenu *parseCascade(WScreen * scr, WMenu * menu, WMenuParser parser)
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +0200970{
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +0200971 char *command, *params, *shortcut, *title;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200972
42cccb23 Christophe CURIS2012-07-08 01:24:03 +0200973 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200974
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +0200975 if (command == NULL || !command[0]) {
7d74648f Christophe CURIS2012-07-08 02:12:36 +0200976 WMenuParserError(parser, _("missing command in menu config") );
d356baeb Iain Patterson2012-05-29 13:21:23 +0000977 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200978 goto error;
979 }
980
981 if (strcasecmp(command, "MENU") == 0) {
982 WMenu *cascade;
983
984 /* start submenu */
985
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +0400986 cascade = wMenuCreate(scr, M_(title), False);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200987 cascade->on_destroy = removeShortcutsForMenu;
19f0998c Christophe CURIS2012-07-08 14:20:24 +0200988 if (!parseCascade(scr, cascade, parser)) {
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200989 wMenuDestroy(cascade, True);
990 } else {
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +0400991 wMenuEntrySetCascade(menu, wMenuAddCallback(menu, M_(title), NULL, NULL), cascade);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200992 }
993 } else if (strcasecmp(command, "END") == 0) {
994 /* end of menu */
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +0200995 freeline(title, command, params, shortcut);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +0200996 return menu;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +0200997 } else {
998 /* normal items */
19f0998c Christophe CURIS2012-07-08 14:20:24 +0200999 addMenuEntry(menu, M_(title), shortcut, command, params, WMenuParserGetFilename(parser));
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001000 }
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +02001001 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001002 }
1003
7d74648f Christophe CURIS2012-07-08 02:12:36 +02001004 WMenuParserError(parser, _("syntax error in menu file: END declaration missing") );
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001005
1006 error:
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +02001007 return NULL;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001008}
1009
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001010static WMenu *readMenuFile(WScreen * scr, char *file_name)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001011{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001012 WMenu *menu = NULL;
1013 FILE *file = NULL;
19f0998c Christophe CURIS2012-07-08 14:20:24 +02001014 WMenuParser parser;
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +02001015 char *command, *params, *shortcut, *title;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001016
139f912e Christophe CURIS2012-06-23 19:49:52 +02001017 file = fopen(file_name, "rb");
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001018 if (!file) {
139f912e
CC
Christophe CURIS2012-06-23 19:49:52 +02001019 werror(_("%s:could not open menu file"), file_name);
1020 return NULL;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001021 }
88a82ab8 Christophe CURIS2012-06-18 00:49:53 +02001022 parser = WMenuParserCreate(file_name, file, DEF_CONFIG_PATHS);
9b792369 Christophe CURIS2012-06-23 15:21:43 +02001023 menu_parser_register_macros(parser);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001024
42cccb23 Christophe CURIS2012-07-08 01:24:03 +02001025 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001026
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +02001027 if (command == NULL || !command[0]) {
7d74648f Christophe CURIS2012-07-08 02:12:36 +02001028 WMenuParserError(parser, _("missing command in menu config") );
d356baeb Iain Patterson2012-05-29 13:21:23 +00001029 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001030 break;
1031 }
1032 if (strcasecmp(command, "MENU") == 0) {
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001033 menu = wMenuCreate(scr, M_(title), True);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001034 menu->on_destroy = removeShortcutsForMenu;
19f0998c Christophe CURIS2012-07-08 14:20:24 +02001035 if (!parseCascade(scr, menu, parser)) {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001036 wMenuDestroy(menu, True);
d356baeb Iain Patterson2012-05-29 13:21:23 +00001037 menu = NULL;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001038 }
d356baeb Iain Patterson2012-05-29 13:21:23 +00001039 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001040 break;
1041 } else {
7d74648f Christophe CURIS2012-07-08 02:12:36 +02001042 WMenuParserError(parser, _("invalid menu file, MENU command is missing") );
d356baeb Iain Patterson2012-05-29 13:21:23 +00001043 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001044 break;
1045 }
d356baeb Iain Patterson2012-05-29 13:21:23 +00001046 freeline(title, command, params, shortcut);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001047 }
6830b057 dan2004-10-12 21:28:27 +00001048
19f0998c Christophe CURIS2012-07-08 14:20:24 +02001049 WMenuParserDelete(parser);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001050 fclose(file);
6830b057 dan2004-10-12 21:28:27 +00001051
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001052 return menu;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001053}
1054
1055/************ Menu Configuration From Pipe *************/
c21ae6b5
AB
Andreas Bierfert2013-02-10 10:56:20 +01001056static WMenu *readPLMenuPipe(WScreen * scr, char **file_name)
1057{
1058 WMPropList *plist = NULL;
1059 WMenu *menu = NULL;
1060 char *filename;
1061 char flat_file[MAXLINE];
1062 int i;
1063
1064 flat_file[0] = '\0';
1065
1066 for (i = 0; file_name[i] != NULL; i++) {
1067 strcat(flat_file, file_name[i]);
1068 strcat(flat_file, " ");
1069 }
1070 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
1071
1072 plist = WMReadPropListFromPipe(filename);
1073
1074 if (!plist)
1075 return NULL;
1076
1077 menu = configureMenu(scr, plist, False);
1078 if (!menu)
1079 return NULL;
1080
1081 menu->on_destroy = removeShortcutsForMenu;
1082 return menu;
1083}
9d2e6ef9 scottc1998-09-29 22:36:29 +00001084
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001085static WMenu *readMenuPipe(WScreen * scr, char **file_name)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001086{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001087 WMenu *menu = NULL;
1088 FILE *file = NULL;
19f0998c Christophe CURIS2012-07-08 14:20:24 +02001089 WMenuParser parser;
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +02001090 char *command, *params, *shortcut, *title;
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001091 char *filename;
1092 char flat_file[MAXLINE];
1093 int i;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001094
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001095 flat_file[0] = '\0';
5873385d kojima2002-02-15 21:30:42 +00001096
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001097 for (i = 0; file_name[i] != NULL; i++) {
1098 strcat(flat_file, file_name[i]);
1099 strcat(flat_file, " ");
1100 }
1101 filename = flat_file + (flat_file[1] == '|' ? 2 : 1);
9d2e6ef9 scottc1998-09-29 22:36:29 +00001102
5d8dfa47 Iain Patterson2012-08-25 10:28:04 +01001103 file = popen(filename, "r");
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001104 if (!file) {
139f912e
CC
Christophe CURIS2012-06-23 19:49:52 +02001105 werror(_("%s:could not open menu file"), filename);
1106 return NULL;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001107 }
88a82ab8 Christophe CURIS2012-06-18 00:49:53 +02001108 parser = WMenuParserCreate(flat_file, file, DEF_CONFIG_PATHS);
9b792369 Christophe CURIS2012-06-23 15:21:43 +02001109 menu_parser_register_macros(parser);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001110
42cccb23 Christophe CURIS2012-07-08 01:24:03 +02001111 while (WMenuParserGetLine(parser, &title, &command, &params, &shortcut)) {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001112
88360074 Tobias Stoeckmann2012-05-05 19:32:21 +02001113 if (command == NULL || !command[0]) {
7d74648f Christophe CURIS2012-07-08 02:12:36 +02001114 WMenuParserError(parser, _("missing command in menu config") );
d356baeb Iain Patterson2012-05-29 13:21:23 +00001115 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001116 break;
1117 }
1118 if (strcasecmp(command, "MENU") == 0) {
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001119 menu = wMenuCreate(scr, M_(title), True);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001120 menu->on_destroy = removeShortcutsForMenu;
19f0998c Christophe CURIS2012-07-08 14:20:24 +02001121 if (!parseCascade(scr, menu, parser)) {
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001122 wMenuDestroy(menu, True);
d356baeb Iain Patterson2012-05-29 13:21:23 +00001123 menu = NULL;
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001124 }
d356baeb Iain Patterson2012-05-29 13:21:23 +00001125 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001126 break;
1127 } else {
7d74648f Christophe CURIS2012-07-08 02:12:36 +02001128 WMenuParserError(parser, _("no title given for the root menu") );
d356baeb Iain Patterson2012-05-29 13:21:23 +00001129 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001130 break;
1131 }
d356baeb
IP
Iain Patterson2012-05-29 13:21:23 +00001132
1133 freeline(title, command, params, shortcut);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001134 }
1135
19f0998c Christophe CURIS2012-07-08 14:20:24 +02001136 WMenuParserDelete(parser);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001137 pclose(file);
1138
1139 return menu;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001140}
1141
9d2e6ef9 scottc1998-09-29 22:36:29 +00001142typedef struct {
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001143 char *name;
1144 int index;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001145} dir_data;
1146
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001147static int myCompare(const void *d1, const void *d2)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001148{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001149 dir_data *p1 = *(dir_data **) d1;
1150 dir_data *p2 = *(dir_data **) d2;
3aa1d977 dan1999-09-18 22:50:11 +00001151
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001152 return strcmp(p1->name, p2->name);
9d2e6ef9 scottc1998-09-29 22:36:29 +00001153}
1154
9b792369
CC
Christophe CURIS2012-06-23 15:21:43 +02001155/***** Preset some macro for file parser *****/
1156static void menu_parser_register_macros(WMenuParser parser)
1157{
1158 Visual *visual;
1159 char buf[32];
1160
1161 // Used to return CPP verion, now returns wmaker's version
1162 WMenuParserRegisterSimpleMacro(parser, "__VERSION__", VERSION);
1163
1164 // All macros below were historically defined by WindowMaker
1165 visual = DefaultVisual(dpy, DefaultScreen(dpy));
1166 snprintf(buf, sizeof(buf), "%d", visual->class);
1167 WMenuParserRegisterSimpleMacro(parser, "VISUAL", buf);
1168
1169 snprintf(buf, sizeof(buf), "%d", DefaultDepth(dpy, DefaultScreen(dpy)) );
1170 WMenuParserRegisterSimpleMacro(parser, "DEPTH", buf);
1171
1172 snprintf(buf, sizeof(buf), "%d", WidthOfScreen(DefaultScreenOfDisplay(dpy)) );
1173 WMenuParserRegisterSimpleMacro(parser, "SCR_WIDTH", buf);
1174
1175 snprintf(buf, sizeof(buf), "%d", HeightOfScreen(DefaultScreenOfDisplay(dpy)) );
1176 WMenuParserRegisterSimpleMacro(parser, "SCR_HEIGHT", buf);
1177
1178 WMenuParserRegisterSimpleMacro(parser, "DISPLAY", XDisplayName(DisplayString(dpy)) );
1179
1180 WMenuParserRegisterSimpleMacro(parser, "WM_VERSION", "\"" VERSION "\"");
1181}
1182
9d2e6ef9 scottc1998-09-29 22:36:29 +00001183/************ Menu Configuration From Directory *************/
0261c326 dan1999-01-06 15:22:33 +00001184
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001185static Bool isFilePackage(char *file)
0261c326 dan1999-01-06 15:22:33 +00001186{
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001187 int l;
0261c326 dan1999-01-06 15:22:33 +00001188
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001189 /* check if the extension indicates this file is a
1190 * file package. For now, only recognize .themed */
0261c326 dan1999-01-06 15:22:33 +00001191
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001192 l = strlen(file);
0261c326 dan1999-01-06 15:22:33 +00001193
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001194 if (l > 7 && strcmp(&(file[l - 7]), ".themed") == 0) {
1195 return True;
1196 } else {
1197 return False;
1198 }
0261c326 dan1999-01-06 15:22:33 +00001199}
1200
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001201static WMenu *readMenuDirectory(WScreen * scr, char *title, char **path, char *command)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001202{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001203 DIR *dir;
1204 struct dirent *dentry;
1205 struct stat stat_buf;
1206 WMenu *menu = NULL;
1207 char *buffer;
1208 WMArray *dirs = NULL, *files = NULL;
1209 WMArrayIterator iter;
1210 int length, i, have_space = 0;
1211 dir_data *data;
1212 int stripExtension = 0;
1213
1214 dirs = WMCreateArray(16);
1215 files = WMCreateArray(16);
1216
1217 i = 0;
1218 while (path[i] != NULL) {
1219 if (strcmp(path[i], "-noext") == 0) {
1220 stripExtension = 1;
1221 i++;
1222 continue;
1223 }
1224
1225 dir = opendir(path[i]);
1226 if (!dir) {
1227 i++;
1228 continue;
1229 }
1230
1231 while ((dentry = readdir(dir))) {
1232
1233 if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0)
1234 continue;
1235
1236 if (dentry->d_name[0] == '.')
1237 continue;
1238
1239 buffer = malloc(strlen(path[i]) + strlen(dentry->d_name) + 4);
1240 if (!buffer) {
1f219198 Tamas TEVESZ2010-09-27 16:12:57 +02001241 werror(_("out of memory while constructing directory menu %s"), path[i]);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001242 break;
1243 }
1244
1245 strcpy(buffer, path[i]);
1246 strcat(buffer, "/");
1247 strcat(buffer, dentry->d_name);
1248
1249 if (stat(buffer, &stat_buf) != 0) {
1f219198 Tamas TEVESZ2010-09-27 16:12:57 +02001250 werror(_("%s:could not stat file \"%s\" in menu directory"),
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001251 path[i], dentry->d_name);
1252 } else {
1253 Bool isFilePack = False;
1254
1255 data = NULL;
1256 if (S_ISDIR(stat_buf.st_mode)
1257 && !(isFilePack = isFilePackage(dentry->d_name))) {
1258
1259 /* access always returns success for user root */
1260 if (access(buffer, X_OK) == 0) {
1261 /* Directory is accesible. Add to directory list */
1262
1263 data = (dir_data *) wmalloc(sizeof(dir_data));
1264 data->name = wstrdup(dentry->d_name);
1265 data->index = i;
1266
1267 WMAddToArray(dirs, data);
1268 }
1269 } else if (S_ISREG(stat_buf.st_mode) || isFilePack) {
1270 /* Hack because access always returns X_OK success for user root */
9d2e6ef9 scottc1998-09-29 22:36:29 +00001271#define S_IXANY (S_IXUSR | S_IXGRP | S_IXOTH)
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001272 if ((command != NULL && access(buffer, R_OK) == 0) ||
1273 (command == NULL && access(buffer, X_OK) == 0 &&
1274 (stat_buf.st_mode & S_IXANY))) {
1275
1276 data = (dir_data *) wmalloc(sizeof(dir_data));
1277 data->name = wstrdup(dentry->d_name);
1278 data->index = i;
1279
1280 WMAddToArray(files, data);
1281 }
1282 }
1283 }
2755b1e4 Tobias Stoeckmann2012-05-03 17:45:28 +02001284 free(buffer);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001285 }
1286
1287 closedir(dir);
1288 i++;
1289 }
1290
1291 if (!WMGetArrayItemCount(dirs) && !WMGetArrayItemCount(files)) {
1292 WMFreeArray(dirs);
1293 WMFreeArray(files);
1294 return NULL;
1295 }
1296
1297 WMSortArray(dirs, myCompare);
1298 WMSortArray(files, myCompare);
1299
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001300 menu = wMenuCreate(scr, M_(title), False);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001301 menu->on_destroy = removeShortcutsForMenu;
1302
1303 WM_ITERATE_ARRAY(dirs, data, iter) {
1304 /* New directory. Use same OPEN_MENU command that was used
1305 * for the current directory. */
1306 length = strlen(path[data->index]) + strlen(data->name) + 6;
1307 if (stripExtension)
1308 length += 7;
1309 if (command)
1310 length += strlen(command) + 6;
1311 buffer = malloc(length);
1312 if (!buffer) {
1f219198 Tamas TEVESZ2010-09-27 16:12:57 +02001313 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001314 break;
1315 }
1316
1317 buffer[0] = '\0';
1318 if (stripExtension)
1319 strcat(buffer, "-noext ");
1320
1321 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1322
1323 if (have_space)
1324 strcat(buffer, "\"");
1325 strcat(buffer, path[data->index]);
1326
1327 strcat(buffer, "/");
1328 strcat(buffer, data->name);
1329 if (have_space)
1330 strcat(buffer, "\"");
1331 if (command) {
1332 strcat(buffer, " WITH ");
1333 strcat(buffer, command);
1334 }
1335
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001336 addMenuEntry(menu, M_(data->name), NULL, "OPEN_MENU", buffer, path[data->index]);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001337
1338 wfree(buffer);
1339 if (data->name)
1340 wfree(data->name);
1341 wfree(data);
1342 }
1343
1344 WM_ITERATE_ARRAY(files, data, iter) {
1345 /* executable: add as entry */
1346 length = strlen(path[data->index]) + strlen(data->name) + 6;
1347 if (command)
1348 length += strlen(command);
1349
1350 buffer = malloc(length);
1351 if (!buffer) {
1f219198 Tamas TEVESZ2010-09-27 16:12:57 +02001352 werror(_("out of memory while constructing directory menu %s"), path[data->index]);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001353 break;
1354 }
1355
1356 have_space = strchr(path[data->index], ' ') != NULL || strchr(data->name, ' ') != NULL;
1357 if (command != NULL) {
1358 strcpy(buffer, command);
1359 strcat(buffer, " ");
1360 if (have_space)
1361 strcat(buffer, "\"");
1362 strcat(buffer, path[data->index]);
1363 } else {
1364 if (have_space) {
1365 buffer[0] = '"';
1366 buffer[1] = 0;
1367 strcat(buffer, path[data->index]);
1368 } else {
1369 strcpy(buffer, path[data->index]);
1370 }
1371 }
1372 strcat(buffer, "/");
1373 strcat(buffer, data->name);
1374 if (have_space)
1375 strcat(buffer, "\"");
1376
1377 if (stripExtension) {
1378 char *ptr = strrchr(data->name, '.');
1379 if (ptr && ptr != data->name)
1380 *ptr = 0;
1381 }
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001382 addMenuEntry(menu, M_(data->name), NULL, "SHEXEC", buffer, path[data->index]);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001383
1384 wfree(buffer);
1385 if (data->name)
1386 wfree(data->name);
1387 wfree(data);
1388 }
1389
1390 WMFreeArray(files);
1391 WMFreeArray(dirs);
1392
1393 return menu;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001394}
1395
9d2e6ef9 scottc1998-09-29 22:36:29 +00001396/************ Menu Configuration From WMRootMenu *************/
1397
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001398static WMenu *makeDefaultMenu(WScreen * scr)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001399{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001400 WMenu *menu = NULL;
1401
1402 menu = wMenuCreate(scr, _("Commands"), True);
ca1c29cd
AF
Alexey I. Froloff2010-09-10 20:51:02 +04001403 wMenuAddCallback(menu, M_("XTerm"), execCommand, "xterm");
1404 wMenuAddCallback(menu, M_("rxvt"), execCommand, "rxvt");
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001405 wMenuAddCallback(menu, _("Restart"), restartCommand, NULL);
1406 wMenuAddCallback(menu, _("Exit..."), exitCommand, NULL);
1407 return menu;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001408}
1409
9d2e6ef9 scottc1998-09-29 22:36:29 +00001410/*
6830b057 dan2004-10-12 21:28:27 +00001411 *----------------------------------------------------------------------
9d2e6ef9 scottc1998-09-29 22:36:29 +00001412 * configureMenu--
1413 * Reads root menu configuration from defaults database.
6830b057 dan2004-10-12 21:28:27 +00001414 *
1415 *----------------------------------------------------------------------
9d2e6ef9 scottc1998-09-29 22:36:29 +00001416 */
8311058a Carlos R. Mafra2012-04-30 14:36:43 -03001417static WMenu *configureMenu(WScreen * scr, WMPropList * definition, Bool includeGlobals)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001418{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001419 WMenu *menu = NULL;
1420 WMPropList *elem;
1421 int i, count;
1422 WMPropList *title, *command, *params;
1423 char *tmp, *mtitle;
1424
1425 if (WMIsPLString(definition)) {
1426 struct stat stat_buf;
1427 char *path = NULL;
1428 Bool menu_is_default = False;
1429
1430 /* menu definition is a string. Probably a path, so parse the file */
1431
1432 tmp = wexpandpath(WMGetFromPLString(definition));
1433
1434 path = getLocalizedMenuFile(tmp);
1435
1436 if (!path)
1437 path = wfindfile(DEF_CONFIG_PATHS, tmp);
1438
1439 if (!path) {
1440 path = wfindfile(DEF_CONFIG_PATHS, DEF_MENU_FILE);
1441 menu_is_default = True;
1442 }
1443
1444 if (!path) {
1f219198 Tamas TEVESZ2010-09-27 16:12:57 +02001445 werror(_("could not find menu file \"%s\" referenced in WMRootMenu"), tmp);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001446 wfree(tmp);
1447 return NULL;
1448 }
1449
1450 if (stat(path, &stat_buf) < 0) {
1f219198 Tamas TEVESZ2010-09-27 16:12:57 +02001451 werror(_("could not access menu \"%s\" referenced in WMRootMenu"), path);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001452 wfree(path);
1453 wfree(tmp);
1454 return NULL;
1455 }
1456
1457 if (!scr->root_menu || stat_buf.st_mtime > scr->root_menu->timestamp
1458 /* if the pointer in WMRootMenu has changed */
1459 || WDRootMenu->timestamp > scr->root_menu->timestamp) {
1460
1461 if (menu_is_default) {
1462 wwarning(_
1463 ("using default menu file \"%s\" as the menu referenced in WMRootMenu could not be found "),
1464 path);
1465 }
1466
1467 menu = readMenuFile(scr, path);
1468 if (menu)
1469 menu->timestamp = WMAX(stat_buf.st_mtime, WDRootMenu->timestamp);
1470 } else {
1471 menu = NULL;
1472 }
1473 wfree(path);
1474 wfree(tmp);
1475
1476 return menu;
1477 }
1478
1479 count = WMGetPropListItemCount(definition);
1480 if (count == 0)
1481 return NULL;
1482
1483 elem = WMGetFromPLArray(definition, 0);
1484 if (!WMIsPLString(elem)) {
1485 tmp = WMGetPropListDescription(elem, False);
1486 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1487 wfree(tmp);
1488 return NULL;
1489 }
1490 mtitle = WMGetFromPLString(elem);
1491
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001492 menu = wMenuCreate(scr, M_(mtitle), False);
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001493 menu->on_destroy = removeShortcutsForMenu;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001494
0261c326 dan1999-01-06 15:22:33 +00001495#ifdef GLOBAL_SUBMENU_FILE
70abda81 Tamas TEVESZ2010-09-30 17:36:22 +02001496 if (includeGlobals) {
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001497 WMenu *submenu;
1498 WMenuEntry *mentry;
0261c326 dan1999-01-06 15:22:33 +00001499
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001500 submenu = readMenuFile(scr, GLOBAL_SUBMENU_FILE);
0261c326 dan1999-01-06 15:22:33 +00001501
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001502 if (submenu) {
1503 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1504 wMenuEntrySetCascade(menu, mentry, submenu);
1505 }
1506 }
0261c326 dan1999-01-06 15:22:33 +00001507#endif
1508
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001509 for (i = 1; i < count; i++) {
1510 elem = WMGetFromPLArray(definition, i);
0261c326 dan1999-01-06 15:22:33 +00001511#if 0
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001512 if (WMIsPLString(elem)) {
1513 char *file;
6830b057 dan2004-10-12 21:28:27 +00001514
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001515 file = WMGetFromPLString(elem);
6830b057 dan2004-10-12 21:28:27 +00001516
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001517 }
6830b057 dan2004-10-12 21:28:27 +00001518#endif
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001519 if (!WMIsPLArray(elem) || WMGetPropListItemCount(elem) < 2)
1520 goto error;
1521
1522 if (WMIsPLArray(WMGetFromPLArray(elem, 1))) {
1523 WMenu *submenu;
1524 WMenuEntry *mentry;
1525
1526 /* submenu */
70abda81 Tamas TEVESZ2010-09-30 17:36:22 +02001527 submenu = configureMenu(scr, elem, True);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001528 if (submenu) {
1529 mentry = wMenuAddCallback(menu, submenu->frame->title, NULL, NULL);
1530 wMenuEntrySetCascade(menu, mentry, submenu);
1531 }
1532 } else {
1533 int idx = 0;
1534 WMPropList *shortcut;
1535 /* normal entry */
1536
1537 title = WMGetFromPLArray(elem, idx++);
1538 shortcut = WMGetFromPLArray(elem, idx++);
1539 if (strcmp(WMGetFromPLString(shortcut), "SHORTCUT") == 0) {
1540 shortcut = WMGetFromPLArray(elem, idx++);
1541 command = WMGetFromPLArray(elem, idx++);
1542 } else {
1543 command = shortcut;
1544 shortcut = NULL;
1545 }
1546 params = WMGetFromPLArray(elem, idx++);
1547
1548 if (!title || !command)
1549 goto error;
1550
ca1c29cd Alexey I. Froloff2010-09-10 20:51:02 +04001551 addMenuEntry(menu, M_(WMGetFromPLString(title)),
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001552 shortcut ? WMGetFromPLString(shortcut) : NULL,
1553 WMGetFromPLString(command),
1554 params ? WMGetFromPLString(params) : NULL, "WMRootMenu");
1555 }
1556 continue;
1557
1558 error:
1559 tmp = WMGetPropListDescription(elem, False);
1560 wwarning(_("%s:format error in root menu configuration \"%s\""), "WMRootMenu", tmp);
1561 wfree(tmp);
1562 }
1563
1564 return menu;
9d2e6ef9 scottc1998-09-29 22:36:29 +00001565}
1566
9d2e6ef9 scottc1998-09-29 22:36:29 +00001567/*
6830b057 dan2004-10-12 21:28:27 +00001568 *----------------------------------------------------------------------
9d2e6ef9 scottc1998-09-29 22:36:29 +00001569 * OpenRootMenu--
1570 * Opens the root menu, parsing the menu configuration from the
1571 * defaults database.
1572 * If the menu is already mapped and is not sticked to the
1573 * root window, it will be unmapped.
6830b057 dan2004-10-12 21:28:27 +00001574 *
9d2e6ef9 scottc1998-09-29 22:36:29 +00001575 * Side effects:
1576 * The menu may be remade.
6830b057 dan2004-10-12 21:28:27 +00001577 *
9d2e6ef9 scottc1998-09-29 22:36:29 +00001578 * Notes:
1579 * Construction of OPEN_MENU entries are delayed to the moment the
1580 * user map's them.
6830b057 dan2004-10-12 21:28:27 +00001581 *----------------------------------------------------------------------
9d2e6ef9 scottc1998-09-29 22:36:29 +00001582 */
688a56e8 Carlos R. Mafra2009-08-20 00:59:40 +02001583void OpenRootMenu(WScreen * scr, int x, int y, int keyboard)
9d2e6ef9 scottc1998-09-29 22:36:29 +00001584{
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001585 WMenu *menu = NULL;
1586 WMPropList *definition;
1587 /*
1588 static WMPropList *domain=NULL;
1589
1590 if (!domain) {
1591 domain = WMCreatePLString("WMRootMenu");
1592 }
1593 */
1594
1595 scr->flags.root_menu_changed_shortcuts = 0;
1596 scr->flags.added_workspace_menu = 0;
1597 scr->flags.added_windows_menu = 0;
1598
1599 if (scr->root_menu && scr->root_menu->flags.mapped) {
1600 menu = scr->root_menu;
1601 if (!menu->flags.buttoned) {
1602 wMenuUnmap(menu);
1603 } else {
1604 wRaiseFrame(menu->frame->core);
1605
1606 if (keyboard)
1607 wMenuMapAt(menu, 0, 0, True);
1608 else
1609 wMenuMapCopyAt(menu, x - menu->frame->core->width / 2, y);
1610 }
1611 return;
1612 }
1613
1614 definition = WDRootMenu->dictionary;
1615
1616 /*
1617 definition = PLGetDomain(domain);
1618 */
1619 if (definition) {
1620 if (WMIsPLArray(definition)) {
1621 if (!scr->root_menu || WDRootMenu->timestamp > scr->root_menu->timestamp) {
70abda81 Tamas TEVESZ2010-09-30 17:36:22 +02001622 menu = configureMenu(scr, definition, True);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001623 if (menu)
1624 menu->timestamp = WDRootMenu->timestamp;
1625
1626 } else
1627 menu = NULL;
1628 } else {
70abda81 Tamas TEVESZ2010-09-30 17:36:22 +02001629 menu = configureMenu(scr, definition, True);
688a56e8
CM
Carlos R. Mafra2009-08-20 00:59:40 +02001630 }
1631 }
1632
1633 if (!menu) {
1634 /* menu hasn't changed or could not be read */
1635 if (!scr->root_menu) {
1636 wMessageDialog(scr, _("Error"),
1637 _("The applications menu could not be loaded. "
1638 "Look at the console output for a detailed "
1639 "description of the errors."), _("OK"), NULL, NULL);
1640
1641 menu = makeDefaultMenu(scr);
1642 scr->root_menu = menu;
1643 }
1644 menu = scr->root_menu;
1645 } else {
1646 /* new root menu */
1647 if (scr->root_menu) {
1648 wMenuDestroy(scr->root_menu, True);
1649 }
1650 scr->root_menu = menu;
1651 }
1652 if (menu) {
1653 int newx, newy;
1654
1655 if (keyboard && x == 0 && y == 0) {
1656 newx = newy = 0;
1657 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
1658 newx = x - menu->frame->core->width / 2;
1659 newy = y - menu->frame->core->height / 2;
1660 } else {
1661 newx = x - menu->frame->core->width / 2;
1662 newy = y;
1663 }
1664 wMenuMapAt(menu, newx, newy, keyboard);
1665 }
1666
1667 if (scr->flags.root_menu_changed_shortcuts)
8311058a Carlos R. Mafra2012-04-30 14:36:43 -03001668 rebindKeygrabs(scr);
9d2e6ef9 scottc1998-09-29 22:36:29 +00001669}