1
// MenuCreator.cc for Fluxbox
2
// Copyright (c) 2004 Henrik Kinnunen (fluxgen at fluxbox dot org)
3
// and Simon Bowden (rathnor at users.sourceforge.net)
5
// Permission is hereby granted, free of charge, to any person obtaining a
6
// copy of this software and associated documentation files (the "Software"),
7
// to deal in the Software without restriction, including without limitation
8
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
// and/or sell copies of the Software, and to permit persons to whom the
10
// Software is furnished to do so, subject to the following conditions:
12
// The above copyright notice and this permission notice shall be included in
13
// all copies or substantial portions of the Software.
15
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21
// DEALINGS IN THE SOFTWARE.
23
// $Id: MenuCreator.cc 4110 2005-09-14 20:28:15Z fluxgen $
25
#include "MenuCreator.hh"
28
#include "CommandParser.hh"
30
#include "CommandParser.hh"
32
#include "WindowCmd.hh"
35
#include "IconMenu.hh"
36
#include "WorkspaceMenu.hh"
37
#include "LayerMenu.hh"
38
#include "SendToMenu.hh"
40
#include "FbMenuParser.hh"
41
#include "StyleMenuItem.hh"
42
#include "RootCmdMenuItem.hh"
44
#include "FbTk/I18n.hh"
45
#include "FbTk/MultiButtonMenuItem.hh"
46
#include "FbTk/RefCount.hh"
47
#include "FbTk/MacroCommand.hh"
48
#include "FbTk/SimpleCommand.hh"
49
#include "FbTk/StringUtil.hh"
50
#include "FbTk/FileUtil.hh"
51
#include "FbTk/MenuSeparator.hh"
52
#include "FbTk/MenuIcon.hh"
57
static void createStyleMenu(FbTk::Menu &parent, const std::string &label,
58
const std::string &directory) {
59
// perform shell style ~ home directory expansion
60
string stylesdir(FbTk::StringUtil::expandFilename(directory));
62
if (!FbTk::FileUtil::isDirectory(stylesdir.c_str()))
65
FbTk::Directory dir(stylesdir.c_str());
67
// create a vector of all the filenames in the directory
69
std::vector<std::string> filelist(dir.entries());
70
for (size_t file_index = 0; file_index < dir.entries(); ++file_index)
71
filelist[file_index] = dir.readFilename();
73
std::sort(filelist.begin(), filelist.end(), less<string>());
75
// for each file in directory add filename and path to menu
76
for (size_t file_index = 0; file_index < dir.entries(); file_index++) {
77
std::string style(stylesdir + '/' + filelist[file_index]);
78
// add to menu only if the file is a regular file, and not a
79
// .file or a backup~ file
80
if ((FbTk::FileUtil::isRegularFile(style.c_str()) &&
81
(filelist[file_index][0] != '.') &&
82
(style[style.length() - 1] != '~')) ||
83
FbTk::FileUtil::isRegularFile((style + "/theme.cfg").c_str()) ||
84
FbTk::FileUtil::isRegularFile((style + "/style.cfg").c_str()))
85
parent.insert(new StyleMenuItem(filelist[file_index], style));
87
// update menu graphics
89
Fluxbox::instance()->saveMenuFilename(stylesdir.c_str());
93
static void createRootCmdMenu(FbTk::Menu &parent, const string &label,
94
const string &directory, const string &cmd) {
95
// perform shell style ~ home directory expansion
96
string rootcmddir(FbTk::StringUtil::expandFilename(directory));
98
if (!FbTk::FileUtil::isDirectory(rootcmddir.c_str()))
101
FbTk::Directory dir(rootcmddir.c_str());
103
// create a vector of all the filenames in the directory
105
vector<string> filelist(dir.entries());
106
for (size_t file_index = 0; file_index < dir.entries(); ++file_index)
107
filelist[file_index] = dir.readFilename();
109
sort(filelist.begin(), filelist.end(), less<string>());
111
// for each file in directory add filename and path to menu
112
for (size_t file_index = 0; file_index < dir.entries(); file_index++) {
114
string rootcmd(rootcmddir+ '/' + filelist[file_index]);
115
// add to menu only if the file is a regular file, and not a
116
// .file or a backup~ file
117
if ((FbTk::FileUtil::isRegularFile(rootcmd.c_str()) &&
118
(filelist[file_index][0] != '.') &&
119
(rootcmd[rootcmd.length() - 1] != '~')))
120
parent.insert(new RootCmdMenuItem(filelist[file_index], rootcmd, cmd));
122
// update menu graphics
124
Fluxbox::instance()->saveMenuFilename(rootcmddir.c_str());
131
explicit ParseItem(FbTk::Menu *menu):m_menu(menu) {}
133
inline void load(Parser &p) {
134
p>>m_key>>m_label>>m_cmd>>m_icon;
136
inline const std::string &icon() const { return m_icon.second; }
137
inline const std::string &command() const { return m_cmd.second; }
138
inline const std::string &label() const { return m_label.second; }
139
inline const std::string &key() const { return m_key.second; }
140
inline FbTk::Menu *menu() { return m_menu; }
142
Parser::Item m_key, m_label, m_cmd, m_icon;
146
class MenuContext: public LayerObject {
148
void moveToLayer(int layer_number) {
149
if (WindowCmd<void>::window() == 0)
151
WindowCmd<void>::window()->moveToLayer(layer_number);
153
int layerNumber() const {
154
if (WindowCmd<void>::window() == 0)
156
return WindowCmd<void>::window()->layerItem().getLayerNum();
160
static void translateMenuItem(Parser &parse, ParseItem &item);
163
static void parseMenu(Parser &pars, FbTk::Menu &menu) {
164
ParseItem pitem(&menu);
165
while (!pars.eof()) {
167
if (pitem.key() == "end")
169
translateMenuItem(pars, pitem);
173
static void translateMenuItem(Parser &parse, ParseItem &pitem) {
174
if (pitem.menu() == 0)
175
throw string("translateMenuItem: We must have a menu in ParseItem!");
177
FbTk::Menu &menu = *pitem.menu();
178
const std::string &str_key = pitem.key();
179
const std::string &str_cmd = pitem.command();
180
const std::string &str_label = pitem.label();
182
const int screen_number = menu.screenNumber();
185
if (str_key == "end") {
187
} else if (str_key == "nop") {
188
menu.insert(str_label.c_str());
189
} else if (str_key == "icons") {
190
FbTk::Menu *submenu = MenuCreator::createMenuType("iconmenu", menu.screenNumber());
193
if (str_label.empty())
194
menu.insert(_FBTEXT(Menu, Icons, "Icons", "Iconic windows menu title"));
196
menu.insert(str_label.c_str(), submenu);
197
} else if (str_key == "exit") { // exit
198
FbTk::RefCount<FbTk::Command> exit_cmd(CommandParser::instance().parseLine("exit"));
199
if (str_label.empty())
200
menu.insert(_FBTEXT(Menu, Exit, "Exit", "Exit Command"), exit_cmd);
202
menu.insert(str_label.c_str(), exit_cmd);
203
} else if (str_key == "exec") {
204
// execute and hide menu
205
using namespace FbTk;
206
RefCount<Command> exec_cmd(CommandParser::instance().parseLine("exec " + str_cmd));
207
RefCount<Command> hide_menu(new SimpleCommand<FbTk::Menu>(menu,
209
MacroCommand *exec_and_hide = new FbTk::MacroCommand();
210
exec_and_hide->add(hide_menu);
211
exec_and_hide->add(exec_cmd);
212
RefCount<Command> exec_and_hide_cmd(exec_and_hide);
213
menu.insert(str_label.c_str(), exec_and_hide_cmd);
214
} else if (str_key == "macrocmd") {
215
using namespace FbTk;
216
RefCount<Command> macro_cmd(CommandParser::instance().parseLine("macrocmd " + str_cmd));
217
RefCount<Command> hide_menu(new SimpleCommand<FbTk::Menu>(menu,
219
MacroCommand *exec_and_hide = new FbTk::MacroCommand();
220
exec_and_hide->add(hide_menu);
221
exec_and_hide->add(macro_cmd);
222
RefCount<Command> exec_and_hide_cmd(exec_and_hide);
223
menu.insert(str_label.c_str(), exec_and_hide_cmd);
224
} else if (str_key == "style") { // style
225
menu.insert(new StyleMenuItem(str_label, str_cmd));
226
} else if (str_key == "config") {
227
BScreen *screen = Fluxbox::instance()->findScreen(screen_number);
229
menu.insert(str_label.c_str(), &screen->configMenu());
231
else if (str_key == "include") { // include
233
// this will make sure we dont get stuck in a loop
234
static size_t safe_counter = 0;
235
if (safe_counter > 10)
240
string newfile = FbTk::StringUtil::expandFilename(str_label);
241
if (FbTk::FileUtil::isDirectory(newfile.c_str())) {
242
// inject every file in this directory into the current menu
243
FbTk::Directory dir(newfile.c_str());
245
std::vector<std::string> filelist(dir.entries());
246
for (size_t file_index = 0; file_index < dir.entries(); ++file_index)
247
filelist[file_index] = dir.readFilename();
248
std::sort(filelist.begin(), filelist.end(), less<string>());
250
for (size_t file_index = 0; file_index < dir.entries(); file_index++) {
251
std::string thisfile(newfile + '/' + filelist[file_index]);
253
if (FbTk::FileUtil::isRegularFile(thisfile.c_str()) &&
254
(filelist[file_index][0] != '.') &&
255
(thisfile[thisfile.length() - 1] != '~')) {
256
MenuCreator::createFromFile(thisfile, menu, false);
257
Fluxbox::instance()->saveMenuFilename(thisfile.c_str());
262
// inject this file into the current menu
263
MenuCreator::createFromFile(newfile, menu, false);
264
Fluxbox::instance()->saveMenuFilename(newfile.c_str());
270
else if (str_key == "submenu") {
272
FbTk::Menu *submenu = MenuCreator::createMenu("", screen_number);
277
submenu->setLabel(str_cmd.c_str());
279
submenu->setLabel(str_label.c_str());
281
parseMenu(parse, *submenu);
282
submenu->updateMenu();
283
menu.insert(str_label.c_str(), submenu);
284
// save to screen list so we can delete it later
285
BScreen *screen = Fluxbox::instance()->findScreen(screen_number);
287
screen->saveMenu(*submenu);
290
else if (str_key == "stylesdir" || str_key == "stylesmenu") {
291
createStyleMenu(menu, str_label,
292
str_key == "stylesmenu" ? str_cmd : str_label);
293
} // end of stylesdir
294
else if (str_key == "themesdir" || str_key == "themesmenu") {
295
createStyleMenu(menu, str_label,
296
str_key == "themesmenu" ? str_cmd : str_label);
297
} // end of themesdir
298
else if (str_key == "wallpapers" || str_key == "wallpapermenu" ||
299
str_key == "rootcommands") {
300
createRootCmdMenu(menu, str_label, str_label,
301
str_cmd == "" ? "fbsetbg" : str_cmd);
302
} // end of wallpapers
303
else if (str_key == "workspaces") {
304
BScreen *screen = Fluxbox::instance()->findScreen(screen_number);
306
screen->workspaceMenu().setInternalMenu();
307
menu.insert(str_label.c_str(), &screen->workspaceMenu());
309
} else if (str_key == "separator") {
310
menu.insert(new FbTk::MenuSeparator());
312
else { // ok, if we didn't find any special menu item we try with command parser
313
// we need to attach command with arguments so command parser can parse it
314
string line = str_key + " " + str_cmd;
315
FbTk::RefCount<FbTk::Command> command(CommandParser::instance().parseLine(line));
317
// special NLS default labels
318
if (str_label.empty()) {
319
if (str_key == "reconfig" || str_key == "reconfigure") {
320
menu.insert(_FBTEXT(Menu, Reconfigure, "Reload Config", "Reload all the configs"), command);
322
} else if (str_key == "restart") {
323
menu.insert(_FBTEXT(Menu, Restart, "Restart", "Restart Command"), command);
327
menu.insert(str_label.c_str(), command);
330
if (menu.numberOfItems() != 0) {
331
FbTk::MenuItem *item = menu.find(menu.numberOfItems() - 1);
332
if (item != 0 && !pitem.icon().empty())
333
item->setIcon(pitem.icon().c_str(), menu.screenNumber());
338
static void parseWindowMenu(Parser &parse, FbTk::Menu &menu) {
340
ParseItem pitem(&menu);
341
while (!parse.eof()) {
343
if (MenuCreator::createWindowMenuItem(pitem.key(), pitem.label(), menu))
346
if (pitem.key() == "end") {
348
} else if (pitem.key() == "submenu") {
349
FbTk::Menu *submenu = MenuCreator::createMenu(pitem.label(), menu.screenNumber());
350
parseWindowMenu(parse, *submenu);
351
submenu->updateMenu();
352
menu.insert(pitem.label().c_str(), submenu);
354
} else { // try non window menu specific stuff
355
translateMenuItem(parse, pitem);
360
FbTk::Menu *MenuCreator::createMenu(const std::string &label, int screen_number) {
361
BScreen *screen = Fluxbox::instance()->findScreen(screen_number);
365
FbTk::Menu *menu = new FbMenu(screen->menuTheme(),
366
screen->imageControl(),
367
*screen->layerManager().
368
getLayer(Fluxbox::instance()->getMenuLayer()));
370
menu->setLabel(label.c_str());
375
bool getStart(FbMenuParser &parser, std::string &label) {
377
while (!parser.eof()) {
378
// get first begin line
380
if (pitem.key() == "begin") {
387
label = pitem.label();
391
FbTk::Menu *MenuCreator::createFromFile(const std::string &filename, int screen_number, bool require_begin) {
392
std::string real_filename = FbTk::StringUtil::expandFilename(filename);
393
FbMenuParser parser(real_filename);
394
if (!parser.isLoaded())
397
Fluxbox::instance()->saveMenuFilename(real_filename.c_str());
400
if (require_begin && !getStart(parser, label))
403
FbTk::Menu *menu = createMenu(label, screen_number);
405
parseMenu(parser, *menu);
411
bool MenuCreator::createFromFile(const std::string &filename,
412
FbTk::Menu &inject_into, bool require_begin) {
414
std::string real_filename = FbTk::StringUtil::expandFilename(filename);
415
FbMenuParser parser(real_filename);
416
if (!parser.isLoaded())
420
if (require_begin && !getStart(parser, label))
423
parseMenu(parser, inject_into);
428
bool MenuCreator::createWindowMenuFromFile(const std::string &filename,
429
FbTk::Menu &inject_into,
430
bool require_begin) {
431
std::string real_filename = FbTk::StringUtil::expandFilename(filename);
432
FbMenuParser parser(real_filename);
433
if (!parser.isLoaded())
438
if (require_begin && !getStart(parser, label))
441
parseWindowMenu(parser, inject_into);
446
FbTk::Menu *MenuCreator::createMenuType(const std::string &type, int screen_num) {
447
BScreen *screen = Fluxbox::instance()->findScreen(screen_num);
450
if (type == "iconmenu") {
451
return new IconMenu(*screen);
452
} else if (type == "workspacemenu") {
453
return new WorkspaceMenu(*screen);
454
} else if (type == "windowmenu") {
455
FbTk::Menu *menu = screen->createMenu("");
457
menu->removeAll(); // clear old items
458
menu->disableTitle(); // not titlebar
459
if (screen->windowMenuFilename().empty() ||
460
! createWindowMenuFromFile(screen->windowMenuFilename(), *menu, true)) {
461
char default_menu[][11] = {
475
for (int i=0; i < sizeof(default_menu); ++i)
476
createWindowMenuItem(default_menu[i], "", *menu);
478
menu->reconfigure(); // update graphics
485
bool MenuCreator::createWindowMenuItem(const std::string &type,
486
const std::string &label,
488
typedef FbTk::RefCount<FbTk::Command> RefCmd;
491
if (type == "shade") {
492
RefCmd shade_cmd(new WindowCmd<void>(&FluxboxWindow::shade));
493
menu.insert(label.empty()?_FBTEXT(Windowmenu, Shade, "Shade", "Shade the window"):label.c_str(), shade_cmd);
494
} else if (type == "maximize") {
495
RefCmd maximize_cmd(new WindowCmd<void>(&FluxboxWindow::maximizeFull));
496
RefCmd maximize_vert_cmd(new WindowCmd<void>(&FluxboxWindow::maximizeVertical));
497
RefCmd maximize_horiz_cmd(new WindowCmd<void>(&FluxboxWindow::maximizeHorizontal));
498
FbTk::MultiButtonMenuItem *maximize_item =
499
new FbTk::MultiButtonMenuItem(3,
501
_FBTEXT(Windowmenu, Maximize,
502
"Maximize", "Maximize the window"):
504
// create maximize item with:
505
// button1: Maximize normal
506
// button2: Maximize Vertical
507
// button3: Maximize Horizontal
508
maximize_item->setCommand(1, maximize_cmd);
509
maximize_item->setCommand(2, maximize_vert_cmd);
510
maximize_item->setCommand(3, maximize_horiz_cmd);
511
menu.insert(maximize_item);
512
} else if (type == "iconify") {
513
RefCmd iconify_cmd(new WindowCmd<void>(&FluxboxWindow::iconify));
514
menu.insert(label.empty()?_FBTEXT(Windowmenu, Iconify, "Iconify", "Iconify the window"):label.c_str(), iconify_cmd);
515
} else if (type == "close") {
516
RefCmd close_cmd(new WindowCmd<void>(&FluxboxWindow::close));
517
menu.insert(label.empty()?_FBTEXT(Windowmenu, Close, "Close", "Close the window"):label.c_str(), close_cmd);
518
} else if (type == "kill" || type == "killwindow") {
519
RefCmd kill_cmd(new WindowCmd<void>(&FluxboxWindow::kill));
520
menu.insert(label.empty()?_FBTEXT(Windowmenu, Kill, "Kill", "Kill the window"):label.c_str(), kill_cmd);
521
} else if (type == "lower") {
522
RefCmd lower_cmd(new WindowCmd<void>(&FluxboxWindow::lower));
523
menu.insert(label.empty()?_FBTEXT(Windowmenu, Lower, "Lower", "Lower the window"):label.c_str(), lower_cmd);
524
} else if (type == "raise") {
525
RefCmd raise_cmd(new WindowCmd<void>(&FluxboxWindow::raise));
526
menu.insert(label.empty()?_FBTEXT(Windowmenu, Raise, "Raise", "Raise the window"):label.c_str(), raise_cmd);
527
} else if (type == "stick") {
528
RefCmd stick_cmd(new WindowCmd<void>(&FluxboxWindow::stick));
529
menu.insert(label.empty()?_FBTEXT(Windowmenu, Stick, "Stick", "Stick the window"):label.c_str(), stick_cmd);
531
else if (type == "extramenus") {
532
BScreen *screen = Fluxbox::instance()->findScreen(menu.screenNumber());
533
BScreen::ExtraMenus::iterator it = screen->extraWindowMenus().begin();
534
BScreen::ExtraMenus::iterator it_end = screen->extraWindowMenus().end();
535
for (; it != it_end; ++it) {
536
it->second->disableTitle();
537
menu.insert(it->first, it->second);
540
} else if (type == "sendto") {
541
menu.insert(label.empty() ? _FBTEXT(Windowmenu, SendTo, "Send To...", "Send to menu item name"):
542
label.c_str(), new SendToMenu(*Fluxbox::instance()->findScreen(menu.screenNumber())));
543
}else if (type == "layer") {
544
BScreen *screen = Fluxbox::instance()->findScreen(menu.screenNumber());
548
static MenuContext context;
550
FbTk::Menu *submenu = new LayerMenu(screen->menuTheme(),
551
screen->imageControl(),
552
*screen->layerManager().
553
getLayer(Fluxbox::instance()->getMenuLayer()),
556
submenu->disableTitle();
557
menu.insert(label.empty()?_FBTEXT(Windowmenu, Layer, "Layer ...", "Layer menu"):label.c_str(), submenu);
560
} else if (type == "separator") {
561
menu.insert(new FbTk::MenuSeparator());