#!/usr/bin/perl -w ############################################################################## # Create an fvwm menu for the most recently used applications and documents # Optionally provide titles and icons in the menu # Applications should be logged thus: # echo "`date +%Y:%m:%d:%H:%M`,program,options" >> $DB # titles may be specified thus: # fvwmtitlestring:appname # e.g # Mahjongg%gnome-mahjongg.png%:mahjongg # By default anything over 1 day old is ignored. Anything used less than # once (sic) is ignored. Both of these values are configurable. # # documents are done differently. I have a shell function which logs edits # to a file, separated by spaces. I use the same file here. The format # looks like this (without the leading '#') #/tmp/log /home/cro/sh/launch.sh /home/cro/pl/showrecent.pl # # Author: Chris Rouch # Date: 05/05/2004 # Changelog: # # 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 Time::Local; # constants use constant HOURS => 60*60; use constant COUNT => 1; use constant MAX => 20; use constant TIME => 24; #Hours use constant MENU => "Recent"; use constant MENUTITLE => "Most Recent"; use constant DB => "$ENV{HOME}/.launchdb"; use constant EDHIST => "$ENV{HOME}/.edhistory"; use constant TITLES => "$ENV{HOME}/.launchtitles"; # general global variables use vars qw ($PROG $USAGE $OPTIONS); use vars qw (%db %count %opts %titles @edit); # command line variables my $time=TIME; # max time ago in minutes (0 means no max) my $count=COUNT; # min count my $max=MAX; # max number of entries (0 means no max) my $db=DB; # db to use my $titles=TITLES; # list of titles my $menu=MENU; # menu name to use my $menutitle=MENUTITLE; # menu title to use my $verbose; # make some noise my $purge; # remove entries older than $time # command line switch my %opts= ( "d|db=s" => \$db, "t|time=s" => \$time, "m|menu=s" => \$menu, "title=s" => \$menutitle, "c|count=s" => \$count, "v|verbose" => \$verbose, "p|purge" => \$purge, ); ($PROG=$0) =~ s!.*/!!; SetUsage(\%opts,\$OPTIONS); $USAGE="Usage: $PROG $OPTIONS"; die "$USAGE\n" unless GetOptions(%opts); # program begins here ReadDB(\%db,\%count,\%opts); if (%db) { if ($purge) { PurgeDB(\%db,\%opts); } else { ReadTitles(\%titles); ReadEdit(\@edit); WriteMenu(\%db,\%count,\%opts,\%titles); WriteEdit(\@edit) } } ############### end of main ############# sub PurgeDB { my ($rdb,$ropts) = @_; my ($year,$month,$day,$hour,$min,$apptime); return unless ($time > 0); foreach my $app (keys %$rdb) { ($year,$month,$day,$hour,$min) = split(/:/,$$rdb{$app}); $apptime=timelocal(0,$min,$hour,$day,$month-1,$year-1900); my $diff = time - $apptime; if ((time - $apptime) > ($time*HOURS)) { # too old delete $$rdb{$app}; print "deleted $app: $year,$month,$day,$hour,$min\n" if ($verbose); } else { print "$app is still current\n" if ($verbose); } } open(O,">$db") or die "failed to open $db for writing - $!\n"; foreach my $app (sort {$$rdb{$a} cmp $$rdb{$b}} keys %$rdb) { print O "$$rdb{$app},$app,$$ropts{$app}\n"; } close(O) or die "failed to update $db\n"; } sub ReadTitles { my ($rtitles) = @_; my ($app,$title); if (open(I,$titles)) { while () { next if m!^\s*$!; # ignore blanks chomp; ($title,$app) = split(/:/); $$rtitles{$app} = $title; # print ">$app< = $title\n"; } close(I); } } sub ReadDB { my ($rdb,$rcount,$ropts) = @_; my ($timestamp,$app,$opts); if (open(I,$db)) { while () { chomp; ($timestamp,$app,$opts) = split(/,/); $$rcount{$app}++; $$rdb{$app} = $timestamp; # always use latest $$ropts{$app} = $opts; # use last opts } close(I); } } sub WriteMenu { my ($rdb,$rcount,$ropts,$rtitles) = @_; my ($year,$month,$day,$hour,$min,$apptime,$title,$entries); # prune based on count foreach my $app (keys %$rcount) { if ($$rcount{$app} < $count) { delete $$rcount{$app}; delete $$rdb{$app}; } } # prune based on time if ($time > 0) { foreach my $app (keys %$rdb) { ($year,$month,$day,$hour,$min) = split(/:/,$$rdb{$app}); $apptime=timelocal(0,$min,$hour,$day,$month-1,$year-1900); if ((time - $apptime) > ($time*HOURS)) { # too old delete $$rcount{$app}; delete $$rdb{$app}; } # print "$app apptime $apptime\n"; } } print qq[DestroyMenu recreate "$menu"\n]; print qq[AddToMenu "$menu" "Recent Applications" Title\n]; # sort it in most recent=first order $entries=0; foreach my $app (sort {$$rdb{$b} cmp $$rdb{$a}} keys %$rdb) { # print "app $app $$rtitles{$app}\n"; if ($$rtitles{$app}) { $title=$$rtitles{$app} } else { $title=$app; } print qq[AddToMenu "$menu" "$title" Exec exec launch $app $$ropts{$app}\n]; $entries++; if (($max > 0) and ($entries >= $max)) { last; } } } sub ReadEdit { my ($redit) = @_; if (open(I,EDHIST)) { while () { chomp; push(@$redit,split); } close(I); } } sub WriteEdit { my ($redit) = @_; print qq[AddToMenu "$menu" "Recent Documents" Title\n]; foreach my $file (@$redit) { print qq[AddToMenu "$menu" "edit $file%emacs.png%" Exec exec launch editor $file\n]; } } 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 #############