bl-obmenu-generator 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. #!/usr/bin/perl
  2. #
  3. # Copyright: martix
  4. # (C) June 2017 - June 2018
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. # Openbox menu generator with support for icons tailored according to the
  20. # specifics of the GNU/Linux distribution BunsenLabs.
  21. #
  22. # bl-obmenu-generator is a fork of obmenu-generator, which was originally
  23. # created by Trizen (2011), https://github.com/trizen/obmenu-generator
  24. #
  25. # Program: bl-obmenu-generator
  26. # License: GPLv3
  27. # Created on: 15 June 2017
  28. # Latest edit on: 14 June 2018
  29. # Website: https://www.notabug.org/martix/bl-obmenu-generator
  30. use 5.014;
  31. #use strict;
  32. #use warnings;
  33. use Linux::DesktopFiles 0.25;
  34. my $pkgname = 'bl-obmenu-generator';
  35. my $version = '0.84';
  36. our ($CONFIG, $SCHEMA);
  37. my $output_h = \*STDOUT;
  38. my ($pipe, $static, $with_icons, $reload_config, $db_clean, $reconfigure, $update_config, $reconf_openbox);
  39. my $home_dir =
  40. $ENV{HOME}
  41. || $ENV{LOGDIR}
  42. || (getpwuid($<))[7]
  43. || `echo -n ~`;
  44. my $xdg_config_home = "$home_dir/.config";
  45. my $menu_id = "root-menu";
  46. my $menu_label_text = "Applications";
  47. my $config_dir = "$xdg_config_home/$pkgname";
  48. my $schema_file = "$config_dir/schema.pl";
  49. my $config_file = "$config_dir/config.pl";
  50. my $openbox_conf = "$xdg_config_home/openbox";
  51. my $menufile = "$openbox_conf/menu.xml";
  52. my $cache_db = "$config_dir/cache.db";
  53. my $icons_dir = "$config_dir/icons";
  54. sub usage {
  55. print <<"HELP";
  56. usage: $0 [options]
  57. menu:
  58. -p : generate a dynamic menu (pipe)
  59. -s : generate a static menu
  60. -i : include icons
  61. -m <id> : menu id (default: 'root-menu')
  62. -t <label> : menu label text (default: 'Applications')
  63. other:
  64. -S <file> : absolute path to the schema.pl file
  65. -C <file> : absolute path to the config.pl file
  66. -o <file> : absolute path to the menu.xml file
  67. -u : update the config file
  68. -r : regenerate the config file
  69. -d : regenerate the cache file
  70. -c : reconfigure openbox automatically
  71. -R : reconfigure openbox and exit
  72. -h : print this message and exit
  73. -v : print version and exit
  74. examples:
  75. $0 -p -i # dynamic menu with icons
  76. $0 -s -c # static menu without icons
  77. => Config file: $config_file
  78. => Schema file: $schema_file
  79. HELP
  80. exit 0;
  81. }
  82. my $config_help = <<"HELP";
  83. || FILTERING
  84. | skip_filename_re : Skip a .desktop file if its name matches the regex.
  85. Name is from the last slash to the end. (e.g.: name.desktop)
  86. Example: qr/^(?:gimp|xterm)\\b/, # skips 'gimp' and 'xterm'
  87. | skip_entry : Skip a desktop file if the value from a given key matches the regex.
  88. Example: [
  89. {key => 'Name', re => qr/(?:about|terminal)/i},
  90. {key => 'Exec', re => qr/^xterm/},
  91. {key => 'OnlyShowIn', re => qr/XFCE/},
  92. ],
  93. | substitutions : Substitute, by using a regex, in the values from the desktop files.
  94. Example: [
  95. {key => 'Exec', re => qr/xterm/, value => 'sakura', global => 1},
  96. ],
  97. || ICON SETTINGS
  98. | gtk_rc_filename : Absolute path to the GTK configuration file.
  99. | missing_icon : Use this icon for missing icons (default: gtk-missing-image)
  100. | icon_size : Preferred size for icons. (default: 48)
  101. | generic_fallback : Try to shorten icon name at '-' characters before looking at inherited themes. (default: 0)
  102. | force_icon_size : Always get the icon scaled to the requested size. (default: 0)
  103. || PATHS
  104. | desktop_files_paths : Absolute paths which contain .desktop files.
  105. Example: [
  106. '/usr/share/applications',
  107. "\$ENV{HOME}/.local/share/applications",
  108. glob("\$ENV{HOME}/.local/share/applications/wine/Programs/*"),
  109. ],
  110. || NOTES
  111. | Regular expressions:
  112. * use qr/.../ instead of '...'
  113. * use qr/.../i for case insensitive mode
  114. HELP
  115. if (@ARGV) {
  116. while (defined(my $arg = shift @ARGV)) {
  117. if ($arg eq '-i') {
  118. $with_icons = 1;
  119. }
  120. elsif ($arg eq '-p') {
  121. $pipe = 1;
  122. }
  123. elsif ($arg eq '-r') {
  124. $reconfigure = 1;
  125. }
  126. elsif ($arg eq '-s') {
  127. $static = 1;
  128. }
  129. elsif ($arg eq '-d') {
  130. $db_clean = 1;
  131. unlink $cache_db;
  132. }
  133. elsif ($arg eq '-u') {
  134. $update_config = 1;
  135. }
  136. elsif ($arg eq '-v') {
  137. print "$pkgname $version\n";
  138. exit 0;
  139. }
  140. elsif ($arg eq '-c') {
  141. $reconf_openbox = 1;
  142. }
  143. elsif ($arg eq '-R') {
  144. exec 'openbox', '--reconfigure';
  145. }
  146. elsif ($arg eq '-S') {
  147. $schema_file = shift(@ARGV) // die "$0: option '-S' requires an argument!\n";
  148. }
  149. elsif ($arg eq '-C') {
  150. $reload_config = 1;
  151. $config_file = shift(@ARGV) // die "$0: options '-C' requires an argument!\n";
  152. }
  153. elsif ($arg eq '-o') {
  154. $menufile = shift(@ARGV) // die "$0: option '-o' requires an argument!\n";
  155. }
  156. elsif ($arg eq '-m') {
  157. $menu_id = shift(@ARGV) // die "$0: option '-m' requires an argument!\n";
  158. }
  159. elsif ($arg eq '-t') {
  160. $menu_label_text = shift(@ARGV) // die "$0: option '-t' requires an argument!\n";
  161. }
  162. elsif ($arg eq '-h') {
  163. usage();
  164. }
  165. else {
  166. die "$0: option `$arg' is invalid!\n";
  167. }
  168. }
  169. }
  170. if (not -d $config_dir) {
  171. require File::Path;
  172. File::Path::make_path($config_dir)
  173. or die "$0: can't create configuration directory `$config_dir': $!\n";
  174. }
  175. if ($with_icons and not -d $icons_dir) {
  176. unlink($cache_db);
  177. require File::Path;
  178. File::Path::make_path($icons_dir)
  179. or warn "$0: can't create icon path `$icons_dir': $!\n";
  180. }
  181. my $config_documentation = <<"EOD";
  182. #!/usr/bin/perl
  183. # $pkgname - configuration file
  184. # This file will be updated automatically.
  185. # Any additional comment and/or indentation will be lost.
  186. =for comment
  187. $config_help
  188. =cut
  189. EOD
  190. my %CONFIG = (
  191. 'Linux::DesktopFiles' => {
  192. keep_unknown_categories => 1,
  193. unknown_category_key => 'other',
  194. skip_entry => undef,
  195. substitutions => undef,
  196. skip_filename_re => undef,
  197. terminalize => 1,
  198. terminalization_format => q{%s -e '%s'},
  199. #<<<
  200. desktop_files_paths => [
  201. '/usr/share/applications',
  202. '/usr/share/applications/kde4',
  203. "$home_dir/.local/share/applications",
  204. ],
  205. #>>>
  206. },
  207. terminal => 'x-terminal-emulator',
  208. editor => 'bl-text-editor',
  209. missing_icon => 'gtk-missing-image',
  210. gtk_rc_filename => "$home_dir/.gtkrc-2.0",
  211. icon_size => 48,
  212. force_icon_size => 0,
  213. generic_fallback => 0,
  214. locale_support => 1,
  215. VERSION => $version,
  216. );
  217. sub dump_configuration {
  218. require Data::Dump;
  219. open my $config_fh, '>', $config_file
  220. or die "Can't open file '${config_file}' for write: $!";
  221. my $dumped_config = q{our $CONFIG = } . Data::Dump::dump(\%CONFIG) . "\n";
  222. $dumped_config =~ s/\Q$home_dir\E/\$ENV{HOME}/g if ($home_dir eq $ENV{HOME});
  223. print $config_fh $config_documentation, $dumped_config;
  224. close $config_fh;
  225. }
  226. if (not -e $config_file or $reconfigure) {
  227. dump_configuration();
  228. }
  229. if (not -e $schema_file) {
  230. if (-e (my $etc_schema_file = "/etc/xdg/$pkgname/schema.pl")) {
  231. require File::Copy;
  232. File::Copy::copy($etc_schema_file, $schema_file)
  233. or warn "$0: can't copy file `$etc_schema_file' to `$schema_file': $!\n";
  234. }
  235. else {
  236. die "$0: schema file `$schema_file' does not exists!\n";
  237. }
  238. }
  239. # Load the configuration files
  240. require $schema_file;
  241. require $config_file if $reload_config;
  242. # Remove invalid user-defined keys
  243. my @valid_keys = grep exists $CONFIG{$_}, keys %$CONFIG;
  244. @CONFIG{@valid_keys} = @{$CONFIG}{@valid_keys};
  245. if ($CONFIG{VERSION} != $version) {
  246. $CONFIG{VERSION} = $version;
  247. dump_configuration();
  248. }
  249. #
  250. ## Remove the overhead of loading some unneeded modules.
  251. #
  252. @INC{
  253. qw(
  254. Carp.pm
  255. strict.pm
  256. Exporter.pm
  257. Tie/Hash.pm
  258. warnings.pm
  259. warnings/register.pm
  260. )
  261. } = ();
  262. my $desk_obj = Linux::DesktopFiles->new(
  263. %{$CONFIG{'Linux::DesktopFiles'}},
  264. categories => [map { exists($_->{cat}) ? $_->{cat}[0] : () } @$SCHEMA],
  265. keys_to_keep => ['Name', 'Exec',
  266. ($with_icons ? 'Icon' : ()),
  267. (
  268. ref($CONFIG{'Linux::DesktopFiles'}{skip_entry}) eq 'ARRAY'
  269. ? (map { $_->{key} } @{$CONFIG{'Linux::DesktopFiles'}{skip_entry}})
  270. : ()
  271. ),
  272. ],
  273. terminal => $CONFIG{terminal},
  274. case_insensitive_cats => 1,
  275. );
  276. if ($pipe or $static) {
  277. my $menu_backup = $menufile . '.bak';
  278. if (not -e $menu_backup and -e $menufile) {
  279. require File::Copy;
  280. File::Copy::copy($menufile, $menu_backup);
  281. }
  282. if ($static) {
  283. open $output_h, '>', $menufile
  284. or die "Can't open file '${menufile}' for write: $!";
  285. }
  286. elsif ($pipe) {
  287. if (not -d $openbox_conf) {
  288. require File::Path;
  289. File::Path::make_path($openbox_conf)
  290. or die "Can't create directory '${openbox_conf}': $!";
  291. }
  292. require Cwd;
  293. my $exec_name = Cwd::abs_path($0);
  294. if (not -x $exec_name) {
  295. $exec_name = "$^X $exec_name";
  296. }
  297. $with_icons && ($exec_name .= q{ -i});
  298. open my $fh, '>', $menufile
  299. or die "Can't open file '${menufile}' for write: $!";
  300. print $fh <<"PIPE_MENU_HEADER";
  301. <?xml version="1.0" encoding="utf-8"?>
  302. <openbox_menu xmlns="http://openbox.org/"
  303. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  304. xsi:schemaLocation="http://openbox.org/">
  305. <menu id="$menu_id" label="$pkgname" execute="$exec_name" />
  306. </openbox_menu>
  307. PIPE_MENU_HEADER
  308. close $fh;
  309. print STDERR <<'EOT';
  310. [*] A dynamic menu has been successfully generated!
  311. EOT
  312. exec 'openbox', '--reconfigure';
  313. }
  314. }
  315. my $generated_menu = $static
  316. ? <<"STATIC_MENU_HEADER"
  317. <?xml version="1.0" encoding="utf-8"?>
  318. <openbox_menu xmlns="http://openbox.org/"
  319. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  320. xsi:schemaLocation="http://openbox.org/">
  321. <menu id="$menu_id" label="$menu_label_text">
  322. STATIC_MENU_HEADER
  323. : "<openbox_pipe_menu>\n";
  324. sub get_icon_path {
  325. my ($name) = @_;
  326. state $gtk = do {
  327. delete($INC{'Exporter.pm'}) if !defined($INC{'Exporter.pm'});
  328. delete($INC{'Carp.pm'}) if !defined($INC{'Carp.pm'});
  329. delete($INC{'Tie/Hash.pm'}) if !defined($INC{'Tie/Hash.pm'});
  330. require Gtk2;
  331. require Digest::MD5;
  332. 'Gtk2'->init;
  333. 'Gtk2';
  334. };
  335. state $theme = do {
  336. "${gtk}::IconTheme"->get_default;
  337. };
  338. #<<<
  339. state $flags = "${gtk}::IconLookupFlags"->new(
  340. [
  341. ($CONFIG{force_icon_size} ? 'force-size' : ()),
  342. ($CONFIG{generic_fallback} ? 'generic-fallback' : ()),
  343. ]
  344. );
  345. #>>>
  346. foreach my $icon_name ($name, $CONFIG{missing_icon}) {
  347. #<<<
  348. my $pixbuf = eval {
  349. (substr($icon_name, 0, 1) eq '/')
  350. ? (substr($icon_name, -4) eq '.xpm')
  351. ? "${gtk}::Gdk::Pixbuf"->new_from_file($icon_name)->scale_simple($CONFIG{icon_size}, $CONFIG{icon_size}, 'hyper')
  352. : "${gtk}::Gdk::Pixbuf"->new_from_file_at_size($icon_name, $CONFIG{icon_size}, $CONFIG{icon_size})
  353. : $theme->load_icon($icon_name, $CONFIG{icon_size}, $flags);
  354. };
  355. #>>>
  356. if (defined($pixbuf)) {
  357. my $md5 = Digest::MD5::md5_hex($pixbuf->get_pixels);
  358. my $path = "$icons_dir/$md5.png";
  359. $pixbuf->save($path, 'png') if not -e $path;
  360. return $path;
  361. }
  362. }
  363. return '';
  364. }
  365. # Regenerate the cache db if the config or schema file has been modified
  366. if (!$db_clean and ((-M $config_file) < (-M $cache_db) or (-M _) > (-M $schema_file))) {
  367. print STDERR "[*] Regenerating the cache DB...\n";
  368. unlink $cache_db;
  369. $db_clean = 1;
  370. }
  371. require GDBM_File;
  372. tie my %cache_db, 'GDBM_File', $cache_db, &GDBM_File::GDBM_WRCREAT, 0640;
  373. # Regenerate the icon db if the GTKRC file has been modified
  374. if ($with_icons) {
  375. my $gtkrc_mtime = (stat $CONFIG{gtk_rc_filename})[9];
  376. if ($db_clean) {
  377. $cache_db{__GTKRC_MTIME__} = $gtkrc_mtime;
  378. }
  379. elsif (!exists($cache_db{__GTKRC_MTIME__}) or $cache_db{__GTKRC_MTIME__} != $gtkrc_mtime) {
  380. print STDERR "[*] Regenerating the cache DB...\n";
  381. untie %cache_db;
  382. unlink $cache_db;
  383. tie %cache_db, 'GDBM_File', $cache_db, &GDBM_File::GDBM_WRCREAT, 0640;
  384. $cache_db{__GTKRC_MTIME__} = $gtkrc_mtime;
  385. }
  386. }
  387. {
  388. my %fast_cache;
  389. sub check_icon {
  390. $fast_cache{$_[0] // return ''} //= ($cache_db{$_[0]} //= get_icon_path($_[0]));
  391. }
  392. }
  393. sub begin_category {
  394. $with_icons
  395. ? <<"MENU_WITH_ICON"
  396. <menu id="${\rand()}" icon="${\check_icon($_[1])}" label="$_[0]">
  397. MENU_WITH_ICON
  398. : <<"MENU"
  399. <menu id="${\rand()}" label="$_[0]">
  400. MENU
  401. }
  402. my %categories;
  403. foreach my $file ($desk_obj->get_desktop_files) {
  404. my %info;
  405. if (exists $cache_db{$file}) {
  406. %info = split("\0\1\0", $cache_db{$file}, -1);
  407. }
  408. next if exists $info{__IGNORE__};
  409. my $mtime = (stat $file)[9];
  410. my $cache_ok = (%info and $info{__MTIME__} == $mtime);
  411. if (not $cache_ok) {
  412. my $entry = $desk_obj->parse_desktop_file($file) // do {
  413. $cache_db{$file} = join("\0\1\0", __IGNORE__ => 1);
  414. next;
  415. };
  416. #<<<
  417. %info = (
  418. Name => $entry->{Name},
  419. Exec => $entry->{Exec},
  420. (
  421. $with_icons
  422. ? (Icon => check_icon($entry->{Icon}))
  423. : ()
  424. ),
  425. __CATEGORIES__ => join(';', @{$entry->{Categories}}),
  426. __MTIME__ => $mtime,
  427. );
  428. #>>>
  429. eval {
  430. state $x = do {
  431. delete($INC{'Exporter.pm'}) if !defined($INC{'Exporter.pm'});
  432. delete($INC{'Carp.pm'}) if !defined($INC{'Carp.pm'});
  433. require Encode;
  434. require File::DesktopEntry;
  435. };
  436. $info{Name} = Encode::encode_utf8(File::DesktopEntry->new($file)->get('Name'));
  437. } if $CONFIG{locale_support};
  438. state $entities = {
  439. '&' => '&amp;',
  440. '"' => '&quot;',
  441. '_' => '__',
  442. '<' => '&lt;',
  443. '>' => '&gt;',
  444. };
  445. # Encode XML entities (if any)
  446. $info{Name} =~ tr/"&_<>//
  447. && $info{Name} =~ s/([&"_<>])/$entities->{$1}/g;
  448. $cache_db{$file} = join("\0\1\0", %info);
  449. }
  450. foreach my $category (split(/;/, $info{__CATEGORIES__})) {
  451. push @{$categories{$category}}, \%info;
  452. }
  453. }
  454. foreach my $schema (@$SCHEMA) {
  455. if (exists $schema->{cat}) {
  456. exists($categories{my $category = lc($schema->{cat}[0]) =~ tr/_a-z0-9/_/cr}) || next;
  457. $generated_menu .= begin_category($schema->{cat}[1], ($with_icons ? $schema->{cat}[2] : ())) . join(
  458. q{},
  459. (
  460. map $_->[1],
  461. sort { $a->[0] cmp $b->[0] }
  462. map [lc($_) => $_],
  463. map {
  464. $with_icons
  465. ? <<"ITEM_WITH_ICON"
  466. <item label="$_->{Name}" icon="$_->{Icon}"><action name="Execute"><command><![CDATA[$_->{Exec}]]></command></action></item>
  467. ITEM_WITH_ICON
  468. : <<"ITEM";
  469. <item label="$_->{Name}"><action name="Execute"><command><![CDATA[$_->{Exec}]]></command></action></item>
  470. ITEM
  471. } @{$categories{$category}}
  472. )
  473. )
  474. . qq[ </menu>\n];
  475. }
  476. elsif (exists $schema->{item}) {
  477. my ($command, $label, $icon) = @{$schema->{item}};
  478. $generated_menu .= $with_icons
  479. ? <<"ITEM_WITH_ICON"
  480. <item label="$label" icon="${\check_icon($icon)}"><action name="Execute"><command><![CDATA[$command]]></command></action></item>
  481. ITEM_WITH_ICON
  482. : <<"ITEM";
  483. <item label="$label"><action name="Execute"><command><![CDATA[$command]]></command></action></item>
  484. ITEM
  485. }
  486. elsif (exists $schema->{sep}) {
  487. $generated_menu .=
  488. defined($schema->{sep})
  489. ? qq[ <separator label="$schema->{sep}"/>\n]
  490. : qq[ <separator/>\n];
  491. }
  492. elsif (exists $schema->{beg}) {
  493. $generated_menu .= begin_category(@{$schema->{beg}});
  494. }
  495. elsif (exists $schema->{begin_cat}) {
  496. warn "[WARNING] <<begin_cat>> is deprecated: use <<beg>> instead!\n";
  497. $generated_menu .= begin_category(@{$schema->{begin_cat}});
  498. }
  499. elsif (exists $schema->{end}) {
  500. $generated_menu .= qq[ </menu>\n];
  501. }
  502. elsif (exists $schema->{end_cat}) {
  503. warn "[WARNING] <<end_cat>> is deprecated: use <<end>> instead!\n";
  504. $generated_menu .= qq[ </menu>\n];
  505. }
  506. elsif (exists $schema->{exit}) {
  507. my ($label, $icon) = @{$schema->{exit}};
  508. $generated_menu .= $with_icons
  509. ? <<"EXIT_WITH_ICON"
  510. <item label="$label" icon="${\check_icon($icon)}"><action name="Exit"/></item>
  511. EXIT_WITH_ICON
  512. : <<"EXIT";
  513. <item label="$label"><action name="Exit"/></item>
  514. EXIT
  515. }
  516. elsif (exists $schema->{raw}) {
  517. $generated_menu .= qq[ $schema->{raw}\n];
  518. }
  519. elsif (exists $schema->{file}) {
  520. sysopen(my $fh, $schema->{file}, 0) or die "Can't open file <<$schema->{file}>>: $!";
  521. sysread($fh, $generated_menu, -s $schema->{file}, length($generated_menu));
  522. }
  523. elsif (exists $schema->{pipe}) {
  524. my ($command, $label, $icon) = @{$schema->{pipe}};
  525. $generated_menu .= $with_icons
  526. ? <<"PIPE_WITH_ICON"
  527. <menu id="${\rand()}" label="$label" execute="$command" icon="${\check_icon($icon)}"/>
  528. PIPE_WITH_ICON
  529. : <<"PIPE";
  530. <menu id="${\rand()}" label="$label" execute="$command"/>
  531. PIPE
  532. }
  533. elsif (exists $schema->{obgenmenu}) {
  534. my ($name, $icon) = ref($schema->{obgenmenu}) eq 'ARRAY' ? @{$schema->{obgenmenu}} : $schema->{obgenmenu};
  535. $generated_menu .= ($with_icons ? <<"MENU_WITH_ICON" : <<"MENU");
  536. <menu id="${\rand()}" label="$name" icon="${\check_icon($icon)}">
  537. MENU_WITH_ICON
  538. <menu id="${\rand()}" label="$name">
  539. MENU
  540. $generated_menu .= ($with_icons ? <<"ITEMS_WITH_ICONS" : <<"ITEMS");
  541. <item label="Menu Schema" icon="${\check_icon('text-x-generic')}"><action name="Execute"><command><![CDATA[$CONFIG{editor} $schema_file]]></command></action></item>
  542. <item label="Menu Config" icon="${\check_icon('text-x-generic')}"><action name="Execute"><command><![CDATA[$CONFIG{editor} $config_file]]></command></action></item>
  543. <separator/>
  544. <item label="Generate a static menu" icon="${\check_icon('accessories-text-editor')}"><action name="Execute"><command><![CDATA[bl-obmenu-generator -s -c]]></command></action></item>
  545. <item label="Generate a static menu with icons" icon="${\check_icon('accessories-text-editor')}"><action name="Execute"><command><![CDATA[bl-obmenu-generator -s -i -c]]></command></action></item>
  546. <separator/>
  547. <item label="Generate a dynamic menu" icon="${\check_icon('accessories-text-editor')}"><action name="Execute"><command><![CDATA[bl-obmenu-generator -p]]></command></action></item>
  548. <item label="Generate a dynamic menu with icons" icon="${\check_icon('accessories-text-editor')}"><action name="Execute"><command><![CDATA[bl-obmenu-generator -p -i]]></command></action></item>
  549. <separator/>
  550. <menu id="${\rand()}" label="Openbox" icon="${\check_icon('openbox')}">
  551. <item label="Openbox Autostart" icon="${\check_icon('text-x-generic')}"><action name="Execute"><command><![CDATA[$CONFIG{editor} $openbox_conf/autostart]]></command></action></item>
  552. <item label="Openbox RC" icon="${\check_icon('text-x-generic')}"><action name="Execute"><command><![CDATA[$CONFIG{editor} $openbox_conf/rc.xml]]></command></action></item>
  553. <item label="Openbox Menu" icon="${\check_icon('text-x-generic')}"><action name="Execute"><command><![CDATA[$CONFIG{editor} $openbox_conf/menu.xml]]></command></action></item>
  554. <item label="Reconfigure" icon="${\check_icon('openbox')}"><action name="Reconfigure"/></item>
  555. </menu>
  556. <separator/>
  557. <item label="Refresh Icon Set" icon="${\check_icon('view-refresh')}"><action name="Execute"><command><![CDATA[bl-obmenu-generator -d]]></command></action></item>
  558. </menu>
  559. ITEMS_WITH_ICONS
  560. <item label="Menu Schema"><action name="Execute"><command><![CDATA[$CONFIG{editor} $schema_file]]></command></action></item>
  561. <item label="Menu Config"><action name="Execute"><command><![CDATA[$CONFIG{editor} $config_file]]></command></action></item>
  562. <separator/>
  563. <item label="Generate a static menu"><action name="Execute"><command><![CDATA[bl-obmenu-generator -s -c]]></command></action></item>
  564. <item label="Generate a static menu with icons"><action name="Execute"><command><![CDATA[bl-obmenu-generator -s -i -c]]></command></action></item>
  565. <separator/>
  566. <item label="Generate a dynamic menu"><action name="Execute"><command><![CDATA[bl-obmenu-generator -p]]></command></action></item>
  567. <item label="Generate a dynamic menu with icons"><action name="Execute"><command><![CDATA[bl-obmenu-generator -p -i]]></command></action></item>
  568. <separator/>
  569. <menu id="${\rand()}" label="Openbox">
  570. <item label="Openbox Autostart"><action name="Execute"><command><![CDATA[$CONFIG{editor} $openbox_conf/autostart]]></command></action></item>
  571. <item label="Openbox RC"><action name="Execute"><command><![CDATA[$CONFIG{editor} $openbox_conf/rc.xml]]></command></action></item>
  572. <item label="Openbox Menu"><action name="Execute"><command><![CDATA[$CONFIG{editor} $openbox_conf/menu.xml]]></command></action></item>
  573. <item label="Reconfigure Openbox"><action name="Reconfigure" /></item>
  574. </menu>
  575. <separator/>
  576. <item label="Refresh Icon Set"><action name="Execute"><command><![CDATA[bl-obmenu-generator -d]]></command></action></item>
  577. </menu>
  578. ITEMS
  579. }
  580. else {
  581. warn "$0: invalid key '", (keys %{$schema})[0], "' in schema file!\n";
  582. }
  583. }
  584. print $output_h $generated_menu, $static
  585. ? qq[ </menu>\n</openbox_menu>\n]
  586. : qq[</openbox_pipe_menu>\n];
  587. dump_configuration() if $update_config;
  588. if ($static) {
  589. print STDERR <<'EOT';
  590. [*] A static menu has been successfully generated!
  591. EOT
  592. if ($reconf_openbox) {
  593. exec 'openbox', '--reconfigure';
  594. }
  595. }