#!/usr/bin/perl -w ############################################################################## # Generate a heirarchical fvwm menu from # /usr/share/applications/*.desktop files. If you have scale.pl installed # and the supplied icon is too big it creates a scaled down image on-the-fly # and saves this in a cache ($HOME/.fvwm/icons by default). # You may also need a list of icons for the menu titles, by default # $HOME/.fvwm/sysicons.txt, which has the format # menu title: iconname # # Author: Chris Rouch # Date: 26/3/2004 # Changelog: # # Requires Image::Size perl module (available from cpan.org) # Image rescaling requires scale.pl installed as $HOME/scripts/scale # (available from the same place you got this) # # Copyright (c) Chris Rouch 2004 # # 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 2 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, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # or see http://www.gnu.org/licenses/gpl.txt ############################################################################## use strict; use Getopt::Long; use Image::Size; use File::Find (); # constants use constant OK => 1; use constant APPDIR=>"/usr/share/applications"; use constant TOPMENU=>"otherapps"; use constant TOPTITLE=>"System Applications"; use constant ICONSIZE=>16; use constant ERROR=>"/usr/share/icons/Bluecurve/48x48/stock/dialog-error.png"; use constant ICONDIR=>"$ENV{HOME}/.fvwm/icons"; use constant SYSICONS=>"$ENV{HOME}/.fvwm/sysicons.txt"; use constant SCALE => "$ENV{HOME}/scripts/scale"; # general global variables use vars qw ($PROG $USAGE $OPTIONS); use vars qw (%sysicons); # command line variables my $topmenu=TOPMENU; # name of menu to create my $toptitle=TOPTITLE; # title of menu to create my $verbose; # make some noise my $icons; # show icons my $kde=0; # show OnlyShowIn=KDE items my $gnome=0; # show OnlyShowIn=GNOME items my $style; # menu style to use (by ChangeMenuStyle) my $sysicons=SYSICONS; # file holding list of titles => icons my $scale=SCALE; # command to use for on-the-fly scaling my $icondir=ICONDIR; # Where to cache the scaled icons my $dynamic; # Create dynamic popups # command line switch my %opts= ( "n|name=s" => \$topmenu, "t|title=s" => \$toptitle, "v|verbose" => \$verbose, "i|icons" => \$icons, "g|gnome" => \$gnome, "k|kde" => \$kde, "dynamic" => \$dynamic, "s|style=s" => \$style, "sysicons=s" => \$sysicons, "scale=s" => \$scale, "icondir=s" => \$icondir, ); ($PROG=$0) =~ s!.*/!!; SetUsage(\%opts,\$OPTIONS); $USAGE="Usage: $PROG $OPTIONS"; die "$USAGE\n" unless GetOptions(%opts); # program begins here ReadSysIcons(\%sysicons); if ($dynamic) { print qq[DestroyMenu recreate "$topmenu"\n]; print qq[DestroyMenu recreate "$topmenu-sub-applications"\n]; print qq[DestroyMenu recreate "$topmenu-sub-settings"\n]; print qq[DestroyMenu recreate "$topmenu-utilities"\n]; print qq[DestroyMenu recreate "$topmenu-system"\n]; } foreach my $icon (values %sysicons) { GenIcon($icon); } GetApps(APPDIR); ############### end of main ############# sub ReadSysIcons { my ($rsysicons) = @_; my ($title,$icon); if (open(S,$sysicons)) { while () { chomp; ($title,$icon) = split(/:/); $$rsysicons{$title} = $icon; } close(S); } # else no problem if this file doesn't exist } sub GetApps { my ($dir) = @_; my (@cats,%categories,$cats,$file,$name,$exec,%progs,%done,$pushed); my ($menu,%icons,$icon,$restricted); print "examine $dir\n" if ($verbose); opendir(DIR,$dir) or die "Cannot read $dir - $!\n"; while($file=readdir(DIR)) { print "check $file\n" if ($verbose); next unless ($file=~m!.desktop$!); $cats=''; @cats=(); $icon=''; $name=''; $exec=''; open(F,"$dir/$file") or die "Cannot read $dir/$file - $!\n"; while () { chomp; if (m!^Name=!) { ($name=$_) =~ s!^Name=!!; } elsif (m!^Exec=!) { ($exec=$_) =~ s!^Exec=!!; # zap any parameters if there are %'s present $exec=~s!\s+.*$!! if ($exec =~ m!%!); } elsif (m!^OnlyShowIn=!) { ($restricted=$_) =~ s!^OnlyShowIn=!!; $restricted =~ s!;.*$!!; print "kde $kde, gnome $gnome vs $restricted\n" if ($verbose); if ($kde and ($restricted eq 'KDE')) { print "kde ok\n" if ($verbose); next; } elsif ($gnome and ($restricted eq 'GNOME')) { print "gnome ok\n" if ($verbose); next; } else { # this file is not to be used $name=''; $exec=''; last; } } elsif (m!^Icon=!) { if ($icons) { ($icon=$_) =~ s!^Icon=!!; } } elsif (m!^Categories=!) { ($cats=$_) =~ s!^Categories=!!; @cats=split(';',$cats); } } close(F); if ($name and $exec and @cats) { $progs{$name} = $exec; if ($icon) { $icon=GenIcon($icon); $icons{$name} = $icon; } $pushed=0; foreach my $cat (@cats) { next if ($cat =~ m!^$!); next if ($cat =~ m!^Application$!); next if ($cat =~ m!^GTK$!); next if ($cat =~ m!^GNOME$!); next if ($cat =~ m!^KDE$!); next if ($cat =~ m!^X-Red-Hat!); if ($pushed > 0) { next; } $cat="Utilities" if ($cat eq "Utility"); $cat="Games" if ($cat =~ m!game!i); $cat="Settings" if ($cat =~ m!settings!i); $cat="System" if ($cat =~ m!system!i); $cat="Settings" if ($cat =~ m!internet!i); print "Add $name for $cat ($#cats)\n" if ($verbose); push(@{$categories{$cat}},$name); $pushed++; } } } if ($icons) { print qq[AddToMenu $topmenu "$toptitle\%$sysicons{'Applications'}\%" Title\n]; print qq[AddToMenu $topmenu "Applications\%$sysicons{'Applications'}\%" Popup "$topmenu-sub-applications"\n]; print qq[AddToMenu $topmenu "Settings\%$sysicons{'Settings'}\%" Popup "$topmenu-sub-settings"\n]; # print qq[AddToMenu $topmenu "Utilities\%$sysicons{'Utilities'}\%" Popup "$topmenu-Utilities"\n]; print qq[AddToMenu "$topmenu-sub-applications" "Applications\%$sysicons{'Applications'}\%" Title\n]; print qq[AddToMenu "$topmenu-sub-settings" "Settings\%$sysicons{'Settings'}%" Title\n]; # print qq[AddToMenu "$topmenu-Utilities" "Utilities\%$sysicons{'Utilities'}%" Title\n]; } else { print qq[AddToMenu $topmenu "$toptitle" Title\n]; print qq[AddToMenu $topmenu "Applications" Popup "$topmenu-sub-applications"\n]; print qq[AddToMenu $topmenu "Settings" Popup "$topmenu-sub-settings"\n]; print qq[AddToMenu "$topmenu-sub-applications" "Applications" Title\n]; print qq[AddToMenu "$topmenu-sub-settings" "Settings" Title\n]; # print qq[AddToMenu "$topmenu-utilities" "Utilities" Title\n]; } if ($style) { print qq[ ChangeMenuStyle $style $topmenu ChangeMenuStyle $style $topmenu-sub-applications ChangeMenuStyle $style $topmenu-sub-settings ]; # ChangeMenuStyle $style $topmenu-utilities } foreach my $cat (sort keys %categories) { if ($cat =~ m!games!i) { $menu="$topmenu-sub-applications"; } elsif ($cat =~ m!system!i) { $menu="$topmenu" } elsif ($cat =~ m!x-!i) { $menu="$topmenu-sub-settings" } elsif ($cat =~ m!settings!i) { $menu="$topmenu-sub-settings" } elsif ($cat =~ m!utilities!i) { $menu="$topmenu" } else { $menu="$topmenu-sub-applications"; } print qq[DestroyMenu recreate "$topmenu-$cat"\n] if ($dynamic); if (($icons) and ($sysicons{$cat})) { print qq[AddToMenu $menu "$cat\%$sysicons{$cat}%" Popup "$topmenu-$cat"\n]; print qq[AddToMenu "$topmenu-$cat" "$cat\%$sysicons{$cat}%" Title\n]; } else { print qq[AddToMenu $menu "$cat" Popup "$topmenu-$cat"\n]; print qq[AddToMenu "$topmenu-$cat" "$cat" Title\n]; } if ($style) { print "ChangeMenuStyle $style $topmenu-$cat\n"; } foreach my $name (sort @{$categories{$cat}}) { next if ($done{"$cat-$name"}); if ($icons{$name}) { print qq[AddToMenu "$topmenu-$cat" "$name\%$icons{$name}\%" Exec exec launch $progs{$name}\n]; } else { print qq[AddToMenu "$topmenu-$cat" "$name" Exec exec launch $progs{$name}\n]; } # print "\t$name: $progs{$name}\n"; $done{"$cat-$name"}=1; } } } sub GenIcon { my ($icon) = @_; my ($width,$height,$base,$png); print "use $icon\n" if ($verbose); $icon .= ".png" unless ($icon =~ m!\.!); ($base=$icon) =~ s!.*/!!; if ($icon =~ m!.xpm$!) { ($png=$base) =~ s!xpm$!png!; if ( -f "$icondir/$png") { return $png; } else { return ERROR . "%($icon)"; } } print "use $base\n" if ($verbose); return $base if ( -f "$icondir/$base"); # ok if ($icon =~ m!^/!) { print "use $icon\n" if ($verbose); return ERROR unless (-f $icon); ($width,$height)=imgsize($icon); if ($height > ICONSIZE) { if (-f "$scale" ) { print "resize $icon from $height\n" if ($verbose); system("cp $icon $icondir/$base"); system("$scale -h 16 -inplace -alpha $icondir/$base"); return $base; } else { warn "Cannot scale $icon\n"; return $icon; } } } else { print "locate $icon\n" if ($verbose); open(CMD,"locate $icon|") or die "failed to run locate - $!\n"; while () { chomp; next unless m!^/usr!; ($width,$height)=imgsize($_); if ($height > ICONSIZE) { if (-f $scale) { print "resize $_ from $height\n" if ($verbose); system("cp $_ $icondir/$base"); system("$scale -h 16 -inplace -alpha $icondir/$base"); } else { warn "Cannot scale $_\n"; $base=$_; } } else { $base=$_; } last; } close(CMD); return $base; } } sub SetUsage { my ($ropts,$rstr) = @_; foreach my $key (keys %$ropts) { if ($key =~ /\|/) { $key=~ s!^.*\|!!; } if ($key =~ /=/) { $key =~ s!=.*$! value!; } $key = '-' . $key; $$rstr .= '[' . $key . '] '; } } ############### end of file #############