123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541 |
- #!/usr/bin/perl
- # Copyright (C) 2011-2014 Daniel "Trizen" Șuteu <echo dHJpemVueEBnbWFpbC5jb20K | base64 -d>.
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- # Openbox Menu Generator
- # A fast pipe/static menu generator for the Openbox Window Manager.
- # Program: obmenu-generator
- # License: GPLv3
- # Created on: 25 March 2011
- # Latest edit on: 26 March 2014
- # Websites: http://trizen.googlecode.com
- # http://trizenx.blogspot.com
- #use 5.014;
- #use strict;
- #use warnings;
- require Linux::DesktopFiles;
- $Linux::DesktopFiles::VERSION >= 0.08
- || die "Update Linux::DesktopFiles to a newer version! (requires >=0.08)\n";
- my $pkgname = 'obmenu-generator';
- my $version = 0.59;
- our ($CONFIG, $SCHEMA);
- my $output_h = *STDOUT;
- my ($pipe, $static, $icons, $reconfigure, $stdout_config, $update_config);
- my $home_dir =
- $ENV{HOME}
- || $ENV{LOGDIR}
- || (getpwuid($<))[7]
- || `echo -n ~`;
- my $xdg_config_home = "$home_dir/.config";
- my $menu_id = "root-menu";
- my $config_dir = "$xdg_config_home/$pkgname";
- my $schema_file = "$config_dir/schema.pl";
- my $config_file = "$config_dir/config.pl";
- my $openbox_conf = "$xdg_config_home/openbox";
- my $menufile = "$openbox_conf/menu.xml";
- my $icons_db = "$config_dir/icons.db";
- sub usage {
- print <<"HELP";
- usage: $0 [options]
- Options:
- -p : (re)generate a pipe menu
- -s : (re)generate a static menu
- -o : static menu file (default: ~/.config/openbox/menu.xml)
- -m : menu id (default: 'root-menu')
- -r : regenerate the config files
- -i : use icons in menus
- -d : regenerate icons.db (with -i)
- -u : update the config file
- -R : reconfigure openbox
- Help:
- -h : print this message
- -v : print the version number
- Examples:
- ** Static menu without icons:
- $0 -s
- ** Pipe menu with icons:
- $0 -p -i
- ** Reconfigure openbox:
- $0 -R
- ** Config file: $config_file
- ** Schema file: $schema_file
- HELP
- exit 0;
- }
- my $config_help = <<"HELP";
- || FILTERING
- | skip_filename_re : Skip a .desktop file if its name matches the regex.
- Name is from the last slash to the end. (filename.desktop)
- Example: qr/^(?:gimp|xterm)\\b/, # skips 'gimp' and 'xterm'
- | skip_entry : Skip a destkop file if the value from a given key matches the regex.
- Example: [
- {key => 'Name', re => qr/(?:about|terminal)/i},
- {key => 'Exec', re => qr/^xterm/},
- ],
- | substitutions : Substitute, by using a regex, in the values of the desktop files.
- Example: [
- {key => 'Exec', re => qr/xterm/, value => 'sakura'},
- {key => 'Exec', re => qr/\\\\\\\\/, value => '\\\\', global => 1}, # for wine apps
- ],
- || ICON SETTINGS
- | icon_dirs_first : When looking for icons, look in this directories first,
- before looking in the directories of the current icon theme.
- Example: [
- "\$ENV{HOME}/My icons",
- ],
- | icon_dirs_second : Look in this directories after looked in the directories of the
- current icon theme. (Before /usr/share/pixmaps)
- Example: [
- "/usr/share/icons/gnome",
- ],
- | icon_dirs_last : Look in this directories at the very last, after looked in
- /usr/share/pixmaps, /usr/share/icons/hicolor and some other
- directories.
- Example: [
- "/usr/share/icons/Tango",
- ],
- | strict_icon_dirs : A true value will make the module to look only inside the directories
- specified by you in either one of the above three options.
- | gtk_rc_filename : Absolute path to the GTK configuration file.
- | missing_image : Use this icon for missing icons (default: gtk-missing-image)
- || KEYS
- | name_keys : Valid keys for the item names.
- Example: ['Name[fr]', 'GenericName[fr]', 'Name'], # french menu
- || PATHS
- | desktop_files_paths : Absolute paths which contains .desktop files.
- Example: [
- '/usr/share/applications',
- "\$ENV{HOME}/.local/share/applications",
- glob("\$ENV{HOME}/.local/share/applications/wine/Programs/*"),
- ],
- || NOTES
- | Regular expressions:
- * use qr/RE/ instead of 'RE'
- * use qr/RE/i for case insenstive mode
- HELP
- if (@ARGV) {
- while (defined(my $arg = shift @ARGV)) {
- if ($arg eq '-i') {
- $icons = 1;
- }
- elsif ($arg eq '-S') {
- $stdout_config = 1;
- }
- elsif ($arg eq '-p') {
- $pipe = 1;
- }
- elsif ($arg eq '-r') {
- $reconfigure = 1;
- }
- elsif ($arg eq '-s') {
- $static = 1;
- }
- elsif ($arg eq '-d') {
- unlink $icons_db;
- }
- elsif ($arg eq '-u') {
- $update_config = 1;
- }
- elsif ($arg eq '-v') {
- print "$pkgname $version\n";
- exit 0;
- }
- elsif ($arg eq '-R') {
- exec 'openbox', '--reconfigure';
- }
- elsif ($arg eq '-o') {
- $menufile = shift(@ARGV) // die "$0: option '-o' requires an argument!\n";
- }
- elsif ($arg eq '-m') {
- $menu_id = shift(@ARGV) // die "$0: option '-m' requires an argument!\n";
- }
- elsif ($arg eq '-h') {
- usage();
- }
- else {
- die "$0: option `$arg' is invalid!\n";
- }
- }
- }
- if (not -d $config_dir) {
- require File::Path;
- File::Path::make_path($config_dir)
- or die "Can't create directory '${config_dir}': $!";
- }
- my $config_documentation = <<"EOD";
- #!/usr/bin/perl
- # $pkgname - configuration file
- # This file will be updated automatically every time when is needed.
- # Any additional comment and/or indentation will be lost.
- =for comment
- $config_help
- =cut
- EOD
- my %CONFIG = (
- 'Linux::DesktopFiles' => {
- keep_unknown_categories => 1,
- unknown_category_key => 'other',
- gtk_rc_filename => "$home_dir/.gtkrc-2.0",
- skip_entry => undef,
- substitutions => undef,
- skip_filename_re => undef,
- terminalize => 1,
- terminalization_format => q{%s -e '%s'},
- desktop_files_paths => ['/usr/share/applications'],
- icon_dirs_first => undef,
- icon_dirs_second => undef,
- icon_dirs_last => undef,
- strict_icon_dirs => undef,
- skip_svg_icons => 1,
- },
- name_keys => ['Name'],
- terminal => 'xterm',
- editor => 'geany',
- missing_icon => 'gtk-missing-image',
- VERSION => $version,
- );
- sub dump_configuration {
- require Data::Dump;
- open my $config_fh, '>', $config_file
- or die "Can't open file '${config_file}' for write: $!";
- my $dumped_config = q{our $CONFIG = } . Data::Dump::dump(\%CONFIG);
- print $config_fh $config_documentation, $dumped_config;
- close $config_fh;
- }
- if (not -e $config_file or $reconfigure) {
- dump_configuration();
- }
- if (not -e $schema_file) {
- if (-e (my $etc_schema_file = "/etc/xdg/$pkgname/schema.pl")) {
- require File::Copy;
- File::Copy::copy($etc_schema_file, $schema_file)
- or die "$0: can't copy file `$etc_schema_file' to `$schema_file': $!\n";
- }
- else {
- die "$0: schema file `$schema_file' does not exists!\n";
- }
- }
- require $schema_file; # Load the configuration files
- # Remove user's defined values
- my @valid_keys = grep exists $CONFIG{$_}, keys $CONFIG;
- @CONFIG{@valid_keys} = @{$CONFIG}{@valid_keys};
- # Keep user's defined values
- #@CONFIG{keys %{$CONFIG}} = values %{$CONFIG};
- if ($CONFIG{VERSION} != $version) {
- $update_config = 1;
- $CONFIG{VERSION} = $version;
- }
- if ($icons && !$pipe) {
- # Performance improvement
- # XXX: don't do this in production code!
- @INC{
- qw(
- strict.pm
- warnings.pm
- Tie/Hash.pm
- Carp.pm
- Exporter.pm
- warnings/register.pm
- )
- } = ();
- *{'warnings::warnif'} = sub { };
- }
- my $desk_obj = Linux::DesktopFiles->new(
- %{$CONFIG{'Linux::DesktopFiles'}},
- home_dir => $home_dir,
- categories => [map $_->{cat}[0], grep exists $_->{cat}, @$SCHEMA],
- keys_to_keep => [@{$CONFIG{name_keys}}, 'Exec', $icons ? 'Icon' : ()],
- $icons
- ? (
- abs_icon_paths => 1,
- icon_db_filename => $icons_db,
- )
- : (),
- terminal => $CONFIG{terminal},
- keep_empty_categories => 0,
- case_insensitive_cats => 1,
- );
- if ($pipe or $static) {
- my $menu_backup = $menufile . '.bak';
- if (not -e $menu_backup and -e $menufile) {
- require File::Copy;
- File::Copy::copy($menufile, $menu_backup);
- }
- if ($static) {
- open $output_h, '>', $menufile
- or die "Can't open file '${menufile}' for write: $!";
- }
- elsif ($pipe) {
- if (not -d $openbox_conf) {
- require File::Path;
- File::Path::make_path($openbox_conf)
- or die "Can't create directory '${openbox_conf}': $!";
- }
- require Cwd;
- my $exec_name = Cwd::abs_path($0) . ($icons ? q{ -i} : q{});
- if (not -x $exec_name) {
- $exec_name = "$^X $exec_name";
- }
- open my $fh, '>', $menufile
- or die "Can't open file '${menufile}' for write: $!";
- print $fh <<"PIPE_MENU_HEADER";
- <?xml version="1.0" encoding="utf-8"?>
- <openbox_menu xmlns="http://openbox.org/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://openbox.org/">
- <menu id="$menu_id" label="$pkgname" execute="$exec_name" />
- </openbox_menu>
- PIPE_MENU_HEADER
- close $fh;
- print <<'EOT';
- ** The pipe menu has been successfully generated!
- EOT
- exec 'openbox', '--reconfigure';
- }
- }
- my $generated_menu = $static
- ? <<"STATIC_MENU_HEADER"
- <?xml version="1.0" encoding="utf-8"?>
- <openbox_menu xmlns="http://openbox.org/"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://openbox.org/">
- <menu id="$menu_id" label="Applications">
- STATIC_MENU_HEADER
- : "<openbox_pipe_menu>\n";
- {
- my %cache;
- sub check_icon {
- $_[0] // return '';
- $cache{$_[0]} //= ((chr ord($_[0]) eq '/') ? $_[0] : $desk_obj->get_icon_path($_[0])
- || $desk_obj->get_icon_path($CONFIG{missing_icon}));
- }
- }
- sub prepare_item {
- $icons
- ? <<"ITEM_WITH_ICON"
- <item label="$_[1]" icon="${\check_icon($_[2])}"><action name="Execute"><execute>$_[0]</execute></action></item>
- ITEM_WITH_ICON
- : <<"ITEM";
- <item label="$_[1]"><action name="Execute"><execute>$_[0]</execute></action></item>
- ITEM
- }
- sub begin_category {
- $icons
- ? <<"MENU_WITH_ICON"
- <menu id="$_[0]" icon="${\check_icon($_[1])}" label="$_[0]">
- MENU_WITH_ICON
- : <<"MENU"
- <menu id="$_[0]" label="$_[0]">
- MENU
- }
- my $categories = $desk_obj->parse_desktop_files();
- foreach my $schema (@$SCHEMA) {
- if (exists $schema->{cat}) {
- exists($categories->{my $category = lc($schema->{cat}[0]) =~ tr/_a-z0-9/_/cr}) || next;
- $generated_menu .= begin_category($schema->{cat}[1], ($icons ? $schema->{cat}[2] : ())) . join(
- q{},
- (
- map $_->[1],
- sort { $a->[0] cmp $b->[0] }
- map [lc($_) => $_],
- map {
- my $name;
- my $exec = $_->{Exec};
- foreach my $key (@{$CONFIG{name_keys}}) {
- if (defined $_->{$key}) {
- $name = $_->{$key};
- last;
- }
- }
- foreach my $item ($name, $exec) {
- if ($item =~ tr/"&//) {
- $item =~ s/&/&/g;
- $item =~ s/"/"/g;
- }
- }
- $icons
- ? <<"ITEM_WITH_ICON"
- <item label="$name" icon="${\check_icon($_->{Icon})}"><action name="Execute"><execute>$exec</execute></action></item>
- ITEM_WITH_ICON
- : <<"ITEM";
- <item label="$name"><action name="Execute"><execute>$exec</execute></action></item>
- ITEM
- } @{$categories->{$category}}
- )
- )
- . qq[ </menu>\n];
- }
- elsif (exists $schema->{item}) {
- $generated_menu .= prepare_item(@{$schema->{item}});
- }
- elsif (exists $schema->{sep}) {
- $generated_menu .=
- defined $schema->{sep}
- ? qq[ <separator label="$schema->{sep}"/>\n]
- : qq[ <separator/>\n];
- }
- elsif (exists $schema->{begin_cat}) {
- $generated_menu .= begin_category(@{$schema->{begin_cat}});
- }
- elsif (exists $schema->{end_cat}) {
- $generated_menu .= qq[ </menu>\n];
- }
- elsif (exists $schema->{exit}) {
- my ($label, $icon) = @{$schema->{exit}};
- $generated_menu .= $icons
- ? <<"EXIT_WITH_ICON"
- <item label="$label" icon="${\check_icon($icon)}"><action name="Exit"/></item>
- EXIT_WITH_ICON
- : <<"EXIT";
- <item label="$label"><action name="Exit"/></item>
- EXIT
- }
- elsif (exists $schema->{raw}) {
- $generated_menu .= qq[ $schema->{raw}\n];
- }
- elsif (exists $schema->{pipe}) {
- my ($command, $label, $icon) = @{$schema->{pipe}};
- $generated_menu .= $icons
- ? <<"PIPE_WITH_ICON"
- <menu id="$label" label="$label" execute="$command" icon="${\check_icon($icon)}"/>
- PIPE_WITH_ICON
- : <<"PIPE";
- <menu id="$label" label="$label" execute="$command"/>
- PIPE
- }
- elsif (exists $schema->{obgenmenu}) {
- my ($name, $icon) = ref($schema->{obgenmenu}) eq 'ARRAY' ? @{$schema->{obgenmenu}} : $schema->{obgenmenu};
- $generated_menu .= ($icons ? <<"CONFIG_MENU_WITH_ICON" : <<"CONFIG_MENU") . <<'RECONFIGURE';
- <menu id="$name" label="$name" icon="${\check_icon($icon)}">
- CONFIG_MENU_WITH_ICON
- <menu id="$name" label="$name">
- CONFIG_MENU
- <item label="Reconfigure Openbox"><action name="Reconfigure" /></item>
- RECONFIGURE
- -e '/usr/bin/obconf' && ($generated_menu .= <<'EOL');
- <item label="Openbox Configuration Manager"><action name="Execute"><execute>obconf</execute></action></item>
- EOL
- $generated_menu .= <<"CONFIG_MENU";
- <item label="Configure autostarted apps"><action name="Execute"><execute>$CONFIG{editor} $openbox_conf/autostart</execute></action></item>
- <item label="Edit rc.xml"><action name="Execute"><execute>$CONFIG{editor} $openbox_conf/rc.xml</execute></action></item>
- <separator />
- <item label="Generate a pipe menu"><action name="Execute"><execute>$0 -p</execute></action></item>
- <item label="Generate a static menu"><action name="Execute"><execute>$0 -s</execute></action></item>
- <item label="Generate a pipe menu with icons"><action name="Execute"><execute>$0 -p -i</execute></action></item>
- <item label="Generate a static menu with icons"><action name="Execute"><execute>$0 -s -i</execute></action></item>
- <separator />
- <item label="Edit menu.xml"><action name="Execute"><execute>$CONFIG{editor} $menufile</execute></action></item>
- <item label="Edit the schema file"><action name="Execute"><execute>$CONFIG{editor} $schema_file</execute></action></item>
- <item label="Edit the configuration file"><action name="Execute"><execute>$CONFIG{editor} $config_file</execute></action></item>
- <separator />
- <item label="Regenerate configuration file"><action name="Execute"><execute>$0 -r</execute></action></item>
- </menu>
- CONFIG_MENU
- }
- else {
- warn "$0: Invalid key '", (keys %{$schema})[0], "' in schema file!\n";
- }
- }
- print $output_h $generated_menu, $static
- ? qq[ </menu>\n</openbox_menu>\n]
- : qq[</openbox_pipe_menu>\n];
- dump_configuration() if $update_config;
|