1 /*
2 * IceWM
3 *
4 * Copyright (C) 1997-2002 Marko Macek
5 */
6 #include "config.h"
7
8 #include "wmprog.h"
9 #include "prefs.h"
10 #include "wmwinmenu.h"
11 #include "wmapp.h"
12 #include "themes.h"
13 #include "browse.h"
14 #include "appnames.h"
15 #include "wmpref.h"
16 #include "wmswitch.h"
17 #include "intl.h"
18 #include <sys/stat.h>
19 #include <time.h>
20
DFile(IApp * app,const mstring & name,ref<YIcon> icon,upath path)21 DFile::DFile(IApp *app, const mstring &name, ref<YIcon> icon, upath path):
22 DObject(app, name, icon), fPath(path)
23 {
24 }
25
~DFile()26 DFile::~DFile() {
27 }
28
open()29 void DFile::open() {
30 const char *args[] = { openCommand, fPath.string(), nullptr };
31 app()->runProgram(openCommand, args);
32 }
33
DProgram(IApp * app,YSMListener * smActionListener,const mstring & name,ref<YIcon> icon,const bool restart,const char * wmclass,upath exe,YStringArray & args)34 DProgram::DProgram(
35 IApp *app,
36 YSMListener *smActionListener,
37 const mstring &name,
38 ref<YIcon> icon,
39 const bool restart,
40 const char *wmclass,
41 upath exe,
42 YStringArray &args)
43 : DObject(app, name, icon),
44 fRestart(restart),
45 fRes(newstr(wmclass)),
46 fPid(0),
47 fCmd(exe),
48 fArgs(args),
49 smActionListener(smActionListener)
50 {
51 if (fArgs.isEmpty() || fArgs.getString(fArgs.getCount() - 1))
52 fArgs.append(nullptr);
53 }
54
~DProgram()55 DProgram::~DProgram() {
56 delete[] fRes;
57 }
58
open()59 void DProgram::open() {
60 if (fRestart)
61 smActionListener->restartClient(fCmd.string(), fArgs.getCArray());
62 else if (fRes)
63 smActionListener->runOnce(fRes, &fPid, fCmd.string(), fArgs.getCArray());
64 else
65 app()->runProgram(fCmd.string(), fArgs.getCArray());
66 }
67
newProgram(IApp * app,YSMListener * smActionListener,const char * name,ref<YIcon> icon,const bool restart,const char * wmclass,upath exe,YStringArray & args)68 DProgram *DProgram::newProgram(
69 IApp *app,
70 YSMListener *smActionListener,
71 const char *name,
72 ref<YIcon> icon,
73 const bool restart,
74 const char *wmclass,
75 upath exe,
76 YStringArray &args)
77 {
78 DProgram* program = nullptr;
79 if (exe != null) {
80 const char* exestr = exe.string();
81 MSG(("LOOKING FOR: %s\n", exestr));
82 csmart path(path_lookup(exestr));
83 if (path) {
84 program = new DProgram(app, smActionListener, name, icon,
85 restart, wmclass, upath(path), args);
86 } else {
87 MSG(("Program %s (%s) not found.", name, exestr));
88 }
89 }
90 return program;
91 }
92
93 KProgramArrayType keyProgs;
94
KProgram(const char * key,DProgram * prog,bool bIsDynSwitchMenuProg)95 KProgram::KProgram(const char *key, DProgram *prog, bool bIsDynSwitchMenuProg) :
96 fKey(NoSymbol),
97 fMod(0),
98 bIsDynSwitchMenu(bIsDynSwitchMenuProg),
99 fProg(prog),
100 pSwitchWindow(nullptr)
101 {
102 YConfig::parseKey(key, &fKey, &fMod);
103 keyProgs.append(this);
104 }
105
106 class MenuProgSwitchItems: public ISwitchItems {
107 MenuProgMenu *menu;
108 int zTarget;
109
110 KeySym key;
111 unsigned int mod;
112
113 public:
MenuProgSwitchItems(DProgram * prog,KeySym key,unsigned keymod)114 MenuProgSwitchItems(DProgram* prog, KeySym key, unsigned keymod) :
115 ISwitchItems(), zTarget(0), key(key), mod(keymod) {
116 menu = new MenuProgMenu(wmapp, wmapp, nullptr /* no wmaction handling*/,
117 "switch popup internal menu", prog->fCmd, prog->fArgs);
118 }
updateList()119 virtual void updateList() override {
120 menu->refresh();
121 zTarget = 0;
122 }
getCount()123 virtual int getCount() override {
124 return menu->itemCount();
125 }
isKey(KeySym k,unsigned int mod)126 virtual bool isKey(KeySym k, unsigned int mod) override {
127 return k == this->key && mod == this->mod;
128 }
modifiers()129 unsigned modifiers() override {
130 return mod;
131 }
setWMClass(char * wmclass)132 virtual void setWMClass(char* wmclass) override {
133 if (wmclass) free(wmclass); // unimplemented
134 }
getWMClass()135 virtual char* getWMClass() override {
136 return nullptr;
137 }
138
139 // move the focused target up or down and return the new focused element
moveTarget(bool zdown)140 virtual void moveTarget(bool zdown) override {
141 int count = getCount();
142 zTarget += zdown ? 1 : -1;
143 if (zTarget >= count)
144 zTarget = 0;
145 if (zTarget < 0)
146 zTarget = max(0, count - 1);
147 // no further gimmicks
148 }
149 // move the focused target up or down and return the new focused element
setTarget(int where)150 virtual void setTarget(int where) override {
151 zTarget = clamp(where, 0, getCount() - 1);
152 }
153
154 // set target cursor and implementation specific stuff in the beginning
begin(bool zdown)155 virtual void begin(bool zdown) override {
156 updateList();
157 zTarget = 0;
158 moveTarget(zdown);
159 }
160
cancel()161 virtual void cancel() override {
162 }
accept()163 virtual void accept() override {
164 YMenuItem* item = menu->getItem(zTarget);
165 if (item) {
166 menu->actionPerformed(item->getAction(), 0);
167 }
168 }
169
getActiveItem()170 virtual int getActiveItem() override {
171 return zTarget;
172 }
getTitle(int idx)173 virtual mstring getTitle(int idx) override {
174 return inrange(idx, 0, getCount() - 1) ?
175 menu->getItem(idx)->getName() : null;
176 }
getIcon(int idx)177 virtual ref<YIcon> getIcon(int idx) override {
178 return inrange(idx, 0, getCount() - 1) ?
179 menu->getItem(idx)->getIcon() : null;
180 }
181 };
182
open(unsigned mods)183 void KProgram::open(unsigned mods) {
184 if (!fProg) return;
185
186 if (bIsDynSwitchMenu) {
187 if (!pSwitchWindow) {
188 ISwitchItems* items = new MenuProgSwitchItems(fProg, fKey, fMod);
189 pSwitchWindow = new SwitchWindow(desktop, items, quickSwitchVertical);
190 }
191 pSwitchWindow->begin(true, mods);
192 }
193 else
194 fProg->open();
195 }
196
MenuFileMenu(IApp * app,YSMListener * smActionListener,YActionListener * wmActionListener,mstring name,YWindow * parent)197 MenuFileMenu::MenuFileMenu(
198 IApp *app,
199 YSMListener *smActionListener,
200 YActionListener *wmActionListener,
201 mstring name,
202 YWindow *parent)
203 :
204 ObjectMenu(wmActionListener, parent),
205 MenuLoader(app, smActionListener, wmActionListener),
206 fName(name),
207 fModTime(0),
208 app(app)
209 {
210 }
211
~MenuFileMenu()212 MenuFileMenu::~MenuFileMenu() {
213 }
214
updatePopup()215 void MenuFileMenu::updatePopup() {
216 if (!autoReloadMenus && fPath != null)
217 return;
218
219 upath np = app->findConfigFile(upath(fName));
220 bool rel = false;
221
222
223 if (fPath == null) {
224 fPath = np;
225 rel = true;
226 } else {
227 if (np == null || np.equals(fPath)) {
228 fPath = np;
229 rel = true;
230 } else
231 np = null;
232 }
233
234 if (fPath == null) {
235 refresh();
236 } else {
237 struct stat sb;
238 if (stat(fPath.string(), &sb) != 0) {
239 fPath = null;
240 refresh();
241 } else if (sb.st_mtime > fModTime || rel) {
242 fModTime = sb.st_mtime;
243 refresh();
244 }
245 }
246 }
247
refresh()248 void MenuFileMenu::refresh() {
249 removeAll();
250 if (fPath != null)
251 loadMenus(fPath, this);
252 }
253
MenuProgMenu(IApp * app,YSMListener * smActionListener,YActionListener * wmActionListener,mstring name,upath command,YStringArray & args,long timeout,YWindow * parent)254 MenuProgMenu::MenuProgMenu(
255 IApp *app,
256 YSMListener *smActionListener,
257 YActionListener *wmActionListener,
258 mstring name,
259 upath command,
260 YStringArray &args,
261 long timeout,
262 YWindow *parent)
263 :
264 ObjectMenu(wmActionListener, parent),
265 MenuLoader(app, smActionListener, wmActionListener),
266 fName(name),
267 fCommand(command),
268 fArgs(args),
269 fModTime(0),
270 fTimeout(timeout)
271 {
272 }
273
~MenuProgMenu()274 MenuProgMenu::~MenuProgMenu() {
275 }
276
updatePopup()277 void MenuProgMenu::updatePopup() {
278 time_t now = seconds();
279 if (fModTime == 0 || (0 < fTimeout && now >= fModTime + fTimeout)) {
280 refresh();
281 fModTime = now;
282 }
283 }
284
refresh()285 void MenuProgMenu::refresh()
286 {
287 removeAll();
288 if (fCommand != null)
289 progMenus(fCommand.string(), fArgs.getCArray(), this);
290 }
291
StartMenu(IApp * app,YSMListener * smActionListener,YActionListener * wmActionListener,const char * name,YWindow * parent)292 StartMenu::StartMenu(
293 IApp *app,
294 YSMListener *smActionListener,
295 YActionListener *wmActionListener,
296 const char *name,
297 YWindow *parent)
298 : MenuFileMenu(app, smActionListener, wmActionListener, name, parent),
299 smActionListener(smActionListener),
300 wmActionListener(wmActionListener)
301 {
302 }
303
handleKey(const XKeyEvent & key)304 bool StartMenu::handleKey(const XKeyEvent &key) {
305 // If meta key, close the popup
306 if (key.type == KeyPress) {
307 KeySym k = keyCodeToKeySym(key.keycode);
308 int m = KEY_MODMASK(key.state);
309
310 if ((k == xapp->Win_L ||
311 k == XK_Multi_key ||
312 k == xapp->Win_R) && m == 0)
313 {
314 cancelPopup();
315 return true;
316 }
317 }
318 return MenuFileMenu::handleKey(key);
319 }
320
updatePopup()321 void StartMenu::updatePopup() {
322 MenuFileMenu::updatePopup();
323 }
324
FocusMenu()325 FocusMenu::FocusMenu() {
326 struct FocusModelNameAction {
327 FocusModel mode;
328 const char *name;
329 YAction action;
330 } foci[] = {
331 { FocusClick, _("_Click to focus"), actionFocusClickToFocus },
332 { FocusExplicit, _("_Explicit focus"), actionFocusExplicit },
333 { FocusSloppy, _("_Sloppy mouse focus"), actionFocusMouseSloppy },
334 { FocusStrict, _("S_trict mouse focus"), actionFocusMouseStrict },
335 { FocusQuiet, _("_Quiet sloppy focus"), actionFocusQuietSloppy },
336 { FocusCustom, _("Custo_m"), actionFocusCustom },
337 };
338 for (size_t k = 0; k < ACOUNT(foci); ++k) {
339 YMenuItem *item = addItem(foci[k].name, -2, null, foci[k].action);
340 if (wmapp->getFocusMode() == foci[k].mode) {
341 item->setEnabled(false);
342 item->setChecked(true);
343 }
344 }
345 }
346
HelpMenu(IApp * app,YSMListener * smActionListener,YActionListener * wmActionListener)347 HelpMenu::HelpMenu(
348 IApp *app,
349 YSMListener *smActionListener,
350 YActionListener *wmActionListener)
351 : ObjectMenu(wmActionListener)
352 {
353 struct HelpMenuItem {
354 const char *name;
355 const char *menu;
356 const char *clas;
357 } help[] = {
358 { ICEHELPIDX, _("_Manual"), "browser.Manual" },
359 { "icewm.1.html", _("_Icewm(1)"), "browser.IceWM" },
360 { "icewmbg.1.html", _("Icewm_Bg(1)"), "browser.IcewmBg" },
361 { "icesound.1.html", _("Ice_Sound(1)"), "browser.IceSound" },
362 };
363 for (size_t k = 0; k < ACOUNT(help); ++k) {
364 YStringArray args(3);
365 args.append(ICEHELPEXE);
366 if (k == 0) {
367 args.append(help[k].name);
368 } else {
369 upath path = upath(ICEHELPIDX).parent() + help[k].name;
370 args.append(path.string());
371 }
372 args.append(nullptr);
373
374 DProgram *prog = DProgram::newProgram(
375 app,
376 smActionListener,
377 help[k].menu,
378 null,
379 false,
380 help[k].clas,
381 ICEHELPEXE,
382 args);
383
384 if (prog)
385 addObject(prog, "help");
386 }
387 }
388
refresh()389 void StartMenu::refresh() {
390 MenuFileMenu::refresh();
391
392 addSeparator();
393
394 /// TODO #warning "make this into a menuprog (ala gnome.cc), and use mime"
395 if (nonempty(openCommand)) {
396 upath path[] = { upath::root(), YApplication::getHomeDir() };
397 ObjectMenu* sub;
398 for (const upath& p : path) {
399 sub = new BrowseMenu(app, smActionListener, wmActionListener, p);
400 DFile *file = new DFile(app, p, null, p);
401 addObject(file, "folder", sub, false);
402 }
403 addSeparator();
404 }
405
406 if (showPrograms) {
407 ObjectMenu *programs = new MenuFileMenu(app, smActionListener,
408 wmActionListener, "programs", nullptr);
409 /// if (programs->itemCount() > 0)
410 addSubmenu(_("Programs"), 0, programs, "programs");
411 }
412
413 if (showRun && nonempty(runDlgCommand)) {
414 addItem(_("_Run..."), -2, null, actionRun, "run");
415 }
416
417 if (showWindowList) {
418 addItem(_("_Windows"), -2, actionWindowList, windowListMenu, "windows");
419 }
420
421 YMenu* settings = new YMenu();
422
423 if (!showTaskBar && showAbout) {
424 settings->addItem(_("_About"), -2, actionAbout, nullptr, "about");
425 }
426
427 if (showHelp) {
428 HelpMenu* help = new HelpMenu(app, smActionListener, wmActionListener);
429 settings->addSubmenu(_("_Help"), -2, help, "help");
430 }
431
432 if (showFocusModeMenu) {
433 FocusMenu *focus = new FocusMenu();
434 settings->addSubmenu(_("_Focus"), -2, focus, "focus");
435 }
436
437 if (showSettingsMenu) {
438 PrefsMenu *prefs = new PrefsMenu();
439 settings->addSubmenu(_("_Preferences"), -2, prefs, "pref");
440 }
441
442 if (showThemesMenu) {
443 YMenu *themes = new ThemesMenu(app, smActionListener, wmActionListener);
444 settings->addSubmenu(_("_Themes"), -2, themes, "themes");
445 }
446
447 if (settings->itemCount()) {
448 addSubmenu(_("Se_ttings"), -2, settings, "settings");
449 } else {
450 delete settings;
451 }
452
453 if (showLogoutMenu) {
454 addSeparator();
455 if (showLogoutSubMenu)
456 addItem(_("_Logout..."), -2, actionLogout, logoutMenu, "logout");
457 else
458 addItem(_("_Logout..."), -2, null, actionLogout, "logout");
459 }
460 }
461
462 // vim: set sw=4 ts=4 et:
463