aboutsummaryrefslogblamecommitdiff
path: root/fluxbox/fluxbox-generate_menu
blob: 14fdc5abdb8401e38328da1beaa7376c4cf2cfa4 (plain) (tree)




















































































































































































                                                                                                 
#!/usr/bin/perl
#
# Alternate fluxbox-generate_menu.  Searches all the installed
# .desktop files to generate the menu.
#

use strict;
use warnings;
use File::Find;
use File::Which;

my $fluxbox_menu = "$ENV{HOME}/.fluxbox/menu";
my @desktop_dirs = ('/usr/share/applications', "$ENV{HOME}/.local/share/applications");
my @desktop_files = ();
my %desktop_entries;

for my $dir (@desktop_dirs) {
  opendir (my $dh, $dir) or die "opendir($dir)";
  my @files = grep { /\.desktop$/ && -f "$dir/$_" } readdir ($dh);
  push @desktop_files, map { "$dir/$_" } @files;
  closedir ($dh);
}

foreach my $file (@desktop_files) {
  open (my $fh, "<$file") or die "open($file)";
  my %entry = (file => $file);
  my $header_found = 0;
  foreach my $line (<$fh>) {
    chomp $line;
    next if $line =~ m/^\s*#/;
    if ($line =~ m/\[Desktop Entry\]/) { $header_found = 1; next; }
    next unless $header_found;
    last if $line =~ m/^\[.+\]/;

    my ($k, $v) = split (/=/, $line);
    next unless $k && $v;
    next if $k =~ m/\[[-\w@]+\]$/; # Skip alt language entries;
    next if $k eq 'OnlyShowIn' && $v !~ m/fluxbox/i;

    $entry{$k} = $v;
  }
  if ($entry{Name} &&
      ! (($entry{Hidden} && $entry{Hidden} eq 'true') ||
         ($entry{NoDisplay} && $entry{NoDisplay} eq 'true'))) {
    $desktop_entries{$entry{Name}} = \%entry;
  }
}

# See https://specifications.freedesktop.org/menu-spec/latest/apa.html
# Sorted in descending order of menu preference
my @menus = qw/TerminalEmulator
               Network
               AudioVideo
               Graphics
               Science
               Development
               Game
               Utility
               Office
               System
               Other/;
my %menus;

name: foreach my $name (sort keys %desktop_entries) {
  my @categories = split (';', $desktop_entries{$name}->{Categories} // 'Other');
  next if grep { /^(X-)?(XFCE|LXDE)(-Settings)?$/ } @categories;
  next unless which split (' ', $desktop_entries{$name}->{Exec});

  foreach my $menu (@menus) {
    if (grep { /^$menu$/ } @categories) {
      $menus{$menu}->{$name} = $desktop_entries{$name};
      next name;
    }
  }

  $menus{Other}->{$desktop_entries{$name}->{Name}} = $desktop_entries{$name};
}

rename $fluxbox_menu, "$fluxbox_menu.bak" or die "rename($fluxbox_menu,$fluxbox_menu.bak)";

open (my $fh, ">$fluxbox_menu") or die "open($fluxbox_menu)";

print $fh <<"HEADER";
# Generated by fluxbox-generate_menu

[begin] (Fluxbox)
[encoding] {UTF-8}
  [exec] (Terminator) {terminator}  </usr/share/icons/hicolor/48x48/apps/terminator.png>
  [exec] (firefox) {firefox} </usr/share/icons/nuoveXT2/32x32/apps/firefox.png>
  [exec] (thunderbird) {thunderbird} </usr/share/icons/hicolor/256x256/apps/thunderbird-icon.png>
  [exec] (Run) {fbrun}
  [exec] (Suspend to RAM) {gotosleep ram}
  [exec] (Suspend to Disk) {gotosleep disk}
HEADER

foreach my $menu (@menus) {
  next unless exists $menus{$menu};
  my %rename = (TerminalEmulator => 'Terminals',
                AudioVideo       => 'Media',
                Game             => 'Games');

  my $menuname = $rename{$menu} // $menu;

  print $fh "[submenu] ($menuname)\n";

  foreach my $name (sort keys %{$menus{$menu}}) {
    my $icontext = '';
    my $foundicon;
    my $icon =  $desktop_entries{$name}->{Icon};

    if ($icon) {
      if (-f $icon) {
        $icontext = " <$icon>";
      } else {
        # Play a game of find the icon
        find (sub {
                if ($_ =~ m/$icon/ && ! defined ($foundicon) && $_ =~ m/\.(png|jpg|jpeg|xpm)$/) {
                  $foundicon = $File::Find::name;
                }
              }, ("$ENV{HOME}/.local/share/icons",
                  qw(/usr/share/icons/hicolor
                     /usr/share/icons/
                     /usr/share/pixmaps)));
        if (defined ($foundicon)) {
          $icontext = " <$foundicon>";
        }
      }
    }

    my $exec = $desktop_entries{$name}->{Exec};
    # Remove file/url arguments (they make no sense here) and deprecated entries
    $exec =~ s/%[fFuUdDnNvm]//g;
    # Replace %i (icon), %c (name), and %k (desktop file)
    $exec =~ s/%i/--icon $foundicon/g if $foundicon;
    $exec =~ s/%c/$name/g;
    $exec =~ s/%k/$desktop_entries{$name}->{file}/g;

    print $fh "  [exec] ($name) {$exec}$icontext\n";
  }

  print $fh "[end]\n";
}

print $fh <<"USER";
[submenu] (User Programs)
   [exec] (VMware) {/usr/bin/vmware}
   [exec] (KiCad) {/usr/bin/kicad}
   [exec] (Eagle) {/opt/bin/eagle}
   [exec] (GThumb) {/usr/bin/gthumb}
   [exec] (QBitTorrent) {/usr/bin/qbittorrent}
   [exec] (HandBrake) {/usr/bin/ghb}
[end]
USER

print $fh <<"FOOTER";
[submenu] (Fluxbox menu)
  [config] (Configure)
[submenu] (Styles)
  [include] (/usr/share/fluxbox/menu.d/styles/)
[end]
  [workspaces] (Workspace List)
[submenu] (Tools)
  [exec] (Window name) {xprop WM_CLASS|cut -d \" -f 2|gxmessage -file - -center}
  [exec] (Run) {fbrun}
  [exec] (Regen Menu) {fluxbox-generate_menu -ds -is}
[end]
[submenu] (Window Managers)
  [restart] (openbox) {openbox} </usr/share/pixmaps/openbox.png>
[end]
  [commanddialog] (Fluxbox Command)
  [reconfig] (Reload config)
  [restart] (Restart)
  [exec] (About) {(fluxbox -v; fluxbox -info | sed 1d) | gxmessage -file - -center}
  [separator]
  [exit] (Exit)
[end]
[endencoding]
[end]
FOOTER

close ($fh);