#!/usr/local/bin/perl # $Id: belt,v 1.21 2002/08/13 11:39:42 max Exp $ # Copyright (c) 1999, 2000, 2001, 2002 # Max Zomborszki. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. use Pod::Text; use Tk; use Tk::Balloon; use Getopt::Long; $| = 1; # ========================================================================== # Installation # ========================================================================== # $iconloc is the location where belts own icons are # installed. $iconpath is the path to search for icons. These two are # used to construct a @iconsearchpath which is used to find all icons # (yes, that means that you do not really have to place belts own # icons in $iconloc but can place them anywhere in $iconpath, but no, # don't do it!) $iconloc is really only still here for compatibility # reasons. You might (and should) just as well only use --iconpath my $iconloc = '/usr/local/belt/icons/'; my $iconpath = '/misc/graphics/icons/reduced/48x48/'; # ========================================================================== # Variables # ========================================================================== my $main = MainWindow->new(); my $Xname = $main->Class; my $verbose = 0; my @STARTARGS = @ARGV; my $debug = 0; my $apid; my $auid; # ========================================================================== # Options # ========================================================================== # Priority of options within parenthesis. Higher number = higher priority. # (1) DEFAULTS values (Priority: widgetDefault/20) $main->optionAdd("$Xname*autopost" => "false", "widgetDefault"); $main->optionAdd("$Xname*autopostdelay" => "300", "widgetDefault"); $main->optionAdd("$Xname*autounpost" => "false", "widgetDefault"); $main->optionAdd("$Xname*autounpostdelay" => "1500", "widgetDefault"); $main->optionAdd("$Xname*overrideredirect" => "true", "widgetDefault"); $main->optionAdd("$Xname*unpost" => "false", "widgetDefault"); $main->optionAdd("$Xname*bhactive" => "true", "widgetDefault"); $main->optionAdd("$Xname*bhdelay" => 1500, "widgetDefault"); $main->optionAdd("$Xname*bhmaindelay" => 5000, "widgetDefault"); $main->optionAdd("$Xname*bhbackground" => "yellow", "widgetDefault"); $main->optionAdd("$Xname*bhfont" => "fixed", "widgetDefault"); $main->optionAdd("$Xname*maxicons" => 8, "widgetDefault"); $main->optionAdd("$Xname*dynamic" => "true", "widgetDefault"); $main->optionAdd("$Xname*ontop" => "true", "widgetDefault"); $main->optionAdd("$Xname*placement" => "+0-0", "widgetDefault"); $main->optionAdd("$Xname*direction" => "right", "widgetDefault"); $main->optionAdd("$Xname*icondirectory" => $iconloc, "widgetDefault"); $main->optionAdd("$Xname*iconpath" => $iconpath, "widgetDefault"); $main->optionAdd("$Xname*iconsize" => 48, "widgetDefault"); $main->optionAdd("$Xname*configdirectory" => "~/.beltconf/", "widgetDefault"); $main->optionAdd("$Xname*menufile" => "menu.conf", "widgetDefault"); $main->optionAdd("$Xname*configfile" => "resources", "widgetDefault"); # (2) XDEFAULTS (Priority: userDefault/60) # Read by the system. # (4) Command-line-options (Priority: interactive/80) GetOptions( "extracticons" => \&extracticons, "version" => sub { print ' Belt $Revision: 1.21 $ ' . "\n"; exit; }, "help" => \&help, "verbose!" => \$verbose, "placement=s" => \&setoption, "overrideredirect=s" => \&setoption, "autopost=s" => \&setoption, "autopostdelay=i" => \&setoption, "autounpost=s" => \&setoption, "autounpostdelay=i" => \&setoption, "unpost=s" => \&setoption, "bhactive=s" => \&setoption, "bhdelay=i" => \&setoption, "bhmaindelay=i" => \&setoption, "bhbackground=s" => \&setoption, "bhfont=s" => \&setoption, "maxicons=i" => \&setoption, "dynamic=s" => \&setoption, "ontop=s" => \&setoption, "direction=s" => \&setoption, "icondirectory=s" => \&setoption, "iconpath=s" => \&setoption, "iconsize=i" => \&setoption, "configdirectory=s" => \&setoption, "configfile=s" => \&setoption, "menufile=s" => \&setoption, "debug" => \$debug, ); # (3) ~/.beltconf/resources (Priority: 70) # Yes, this is point three but done after point four. Still it # is the priority that decides if a commandline option overrides # this or not. my $tmpdir = $main->optionGet("configdirectory", "$Xname"); my $tmpfile = $main->optionGet("configfile", "$Xname"); $tmpdir = &correctpath($tmpdir); if (-f $tmpdir . $tmpfile) { $main->optionReadfile($tmpdir . $tmpfile, 70); } # ========================================================================== # Options Finalizing # ========================================================================== my $orrd = ($main->optionGet("overrideredirect", "$Xname") =~ /^true$/i) ? 1 : 0; my $unpost = ($main->optionGet("unpost", "$Xname") =~ /^true$/i) ? 1 : 0; my $bhactive = ($main->optionGet("bhactive", "$Xname") =~ /^true$/i) ? 1 : 0; my $autopost = ($main->optionGet("autopost", "$Xname") =~ /^true$/i) ? 1 : 0; my $autopostdelay = $main->optionGet("autopostdelay", "$Xname"); my $autounpost = ($main->optionGet("autounpost", "$Xname") =~ /^true$/i) ? 1 : 0; my $autounpostdelay = $main->optionGet("autounpostdelay", "$Xname"); my $bhdelay = $main->optionGet("bhdelay", "$Xname"); my $bhmaindelay = $main->optionGet("bhmaindelay", "$Xname"); my $bhbg = $main->optionGet("bhbackground", "$Xname"); my $bhfont = $main->optionGet("bhfont", "$Xname"); my $maxicons = $main->optionGet("maxicons", "$Xname"); my $dynamic = ($main->optionGet("dynamic", "$Xname") =~ /^true$/i) ? 1 : 0; my $ontop = ($main->optionGet("ontop", "$Xname") =~ /^true$/i) ? 1 : 0; my $placement = $main->optionGet("placement", "$Xname"); my $direction = $main->optionGet("direction", "$Xname"); my $icondir = $main->optionGet("icondirectory", "$Xname"); my $iconpath = $main->optionGet("iconpath", "$Xname"); my $size = $main->optionGet("iconsize", "$Xname"); my $confdir = $main->optionGet("configdirectory", "$Xname"); my $conffile = $main->optionGet("configfile", "$Xname"); my $menufile = $main->optionGet("menufile", "$Xname"); $confdir = &correctpath($confdir); # Construct iconsearchpath from icondir and iconpath my @iconsearchpath = &splitandfixpath("$icondir:$iconpath"); die "autopostdelay cannot be less than zero\n" if ($autopostdelay < 0); die "autounpostdelay cannot be less than zero\n" if ($autounpostdelay < 0); die "bhdelay cannot be less than one\n" if ($bhdelay < 1); die "bhmaindelay cannot be less than one\n" if ($bhmaindelay < 1); die "maxicons cannot be less than one\n" if ($maxicons < 1); die "placement can only be a placement specification\n" if ($placement !~ /^[\-\+][0-9]+[\-\+][0-9]+$/); die "size cannot be less than one\n" if ($size < 1); if ($verbose or $debug) { print "-------------------------------------------------\n"; print "autopost $autopost\n"; print "autopostdelay $autopostdelay\n"; print "autounpost $autounpost\n"; print "autounpostdelay $autounpostdelay\n"; print "Application Name $Xname\n"; print "overrideredirect $orrd\n"; print "unpost $unpost\n"; print "bhactive $bhactive\n"; print "bhdelay $bhdelay\n"; print "bhmaindelay $bhmaindelay\n"; print "bhbg $bhbg\n"; print "bhfont $bhfont\n"; print "maxicons $maxicons\n"; print "dynamic $dynamic\n"; print "ontop $ontop\n"; print "placement $placement\n"; print "direction $direction\n"; print "size $size\n"; print "confdir $confdir\n"; print "menufile $menufile\n"; print "icondir $icondir\n"; print "iconpath $iconpath\n"; print "iconsearchpath (searched in order)\n"; foreach my $e (@iconsearchpath) { print " $e\n"; } print "-------------------------------------------------\n"; } # ========================================================================== # Variables # ========================================================================== my (%imgs, $open, $close, $scrfwd, $scrbck, $pckdir, $first, $last); # ========================================================================== # Setting up the GUI # ========================================================================== $main->geometry($placement); $main->overrideredirect($orrd); $main->appname("Belt"); my $bh = $main->Balloon(-initwait => $bhdelay, -state => "balloon", -balloonposition => "mouse", -background => $bhbg, -font => $bhfont, ); foreach (qw/DH DS LH LS RH RS UH US noicon/) { my $file = &findicon($_ . '.gif'); die "Couldn't find $_.gif " if (! -f $file); $imgs{$_} = $main->Photo(-file => $file); } if ($direction eq 'left') { $close = $imgs{'RS'}; $open = $imgs{'LS'}; $scrfwd = $imgs{'LH'}; $scrbck = $imgs{'RH'}; $pckdir = 'right'; } elsif ($direction eq 'up') { $close = $imgs{'DS'}; $open = $imgs{'US'}; $scrfwd = $imgs{'UH'}; $scrbck = $imgs{'DH'}; $pckdir = 'bottom'; } elsif ($direction eq 'down') { $close = $imgs{'US'}; $open = $imgs{'DS'}; $scrfwd = $imgs{'DH'}; $scrbck = $imgs{'UH'}; $pckdir = 'top'; } else { $close = $imgs{'LS'}; $open = $imgs{'RS'}; $scrfwd = $imgs{'RH'}; $scrbck = $imgs{'LH'}; $pckdir = 'left'; } my $b = $main->Button(-image => $open, -command => \&post, )->pack(-side => $pckdir); $bh->attach($b, -initwait => $bhmaindelay, -balloonmsg => "Ctrl-Right-DoubleClick to exit\nCtrl-Middle-DoubleClick to restart"); $b->bind("", sub{ &destroyimages(); destroy $main; }); $b->bind("", sub{ &destroyimages(); destroy $main; exec $0, @STARTARGS; }); $main->bind("", sub{ $main->raise(); }) if ($ontop); if ($autopost) { $b->bind("", sub { &postafter() }); $b->bind("", sub { &postcancel(); &unpostcancel() }); } if ($autounpost) { $main->bind("", sub { &unpostafter() }); $main->bind("", sub { &unpostcancel() }); } my $frm = $main->Frame(-relief => 'flat', -borderwidth => '0', ); my $bck = $frm->Button(-image => $scrbck, -command => \&scrollbackward, ); my $f = $frm->Frame(-relief => 'flat', -borderwidth => '0', ); my $fwd = $frm->Button(-image => $scrfwd, -command => \&scrollforward, ); my $cl = $frm->Button(-image => $close, -command => \&unpost, ); my @list; if (-f $confdir . $menufile) { if (open (MENUCONF, $confdir . $menufile )) { while () { chomp; next if (/^\#/); my ($text, $icon, $program) = (split /\|/, $_,7)[2,5,6]; if ($program ne "" ) { my $im; # Fix the path of the icon if necessary. $icon = &findicon($icon); if (-f $icon) { $im = $main->Photo(-file => $icon, -height => $size, -width => $size, ); $imgs{$im} = $im; } else { $im = $imgs{'noicon'}; } my $c = $f->Button(-command => sub{ &unpost() if ($unpost); system($program . ' &'); }, -relief => 'groove', -image => $im, ); $c->bind("", sub{ &unpost(); system($program . ' &'); }); $bh->attach($c, -balloonmsg => $text) if ($bhactive); push @list, $c; } } close (MENUCONF); } } if ($dynamic && $ENV{"WMMENUPATH"}) { my @menupath = split /\:/, $ENV{"WMMENUPATH"}; my $confpath; foreach $confpath (@menupath) { if (open (MENUCONF, $confpath . "/menu.conf")) { while () { chomp; next if (/^\#/); my ($text, $icon, $program) = (split /\|/, $_,7)[2,5,6]; if ($program ne "" ) { my $im; # Fix the path of the icon if necessary. $icon = &findicon($icon); if (-f $icon) { $im = $main->Photo(-file => $icon, -height => $size, -width => $size, ); $imgs{$im} = $im; } else { $im = $imgs{'noicon'}; } my $c = $f->Button(-command => sub{ &unpost() if ($unpost); system($program . ' &'); }, -relief => 'groove', -image => $im, ); $bh->attach($c, -balloonmsg => $text) if ($bhactive); push @list, $c; } } close (MENUCONF); } } } $bck->pack(-side => $pckdir, ) if ((scalar @list) > $maxicons); $f->pack(-side => $pckdir, -fill => 'both'); $fwd->pack(-side => $pckdir, ) if ((scalar @list) > $maxicons); $cl->pack(-side => $pckdir, ); if ((scalar @list) > $maxicons) { my $i; $first = 0; $last = $maxicons - 1; for ($i = 0; $i < $maxicons; $i++) { $list[$i]->pack(-side => $pckdir); } } else { my $button; foreach $button (@list) { $button->pack(-side => $pckdir); } } MainLoop; # ========================================================================== # Subroutines # ========================================================================== sub destroyimages { foreach (keys %imgs) { $imgs{$_}->destroy(); } } sub unpost { &unpostcancel(); $frm->packForget(); $b->configure(-command => \&post); $b->configure(-image => $open); } sub post { &postcancel(); $frm->pack(-side => $pckdir); $b->configure(-command => \&unpost); $b->configure(-image => $close); } sub postafter { return if defined $apid; $apid = $b->after($autopostdelay, sub{ &post() }); print "After POST: $apid\n" if ($debug); } sub unpostafter { return if defined $auid; $auid = $main->after($autounpostdelay, sub{ &unpost }); print "After UNPOST: $auid\n" if ($debug); } sub postcancel { return unless defined $apid; $b->afterCancel($apid); print "Cancelled POST: $apid\n" if ($debug); $apid = undef; } sub unpostcancel { return unless defined $auid; $main->afterCancel($auid); print "Cancelled UNPOST: $auid\n" if ($debug); $auid = undef; } sub scrollforward { if ($last < $#list) { $list[$first]->packForget(); $first++; $last++; $list[$last]->pack(-side => $pckdir); } } sub scrollbackward { if ($first > 0) { $list[$last]->packForget(); $last--; $first--; $list[$first]->pack(-side => $pckdir, -before => $list[$first+1]); } } sub expandtilde { my $path = shift; # Expand tilde for this user. $path =~ s/^~\//$ENV{'HOME'}\//; # Expand tilde for other users if($path =~ /^~([^\/]*)/) { my $user = $1; my $realpath = (getpwnam($user))[7]; if (defined $realpath) { $path =~ s/^~([^\/]*)/$realpath/; } else { die "No such user '" . $user . "' when expanding '" . $path . "'\n"; } } return $path; } sub correctpath { my $dir = shift @_; $dir = &expandtilde($dir); # Add a trailing slash $dir =~ s/([^\/])$/$1\//; return $dir; } sub splitandfixpath { my $p = shift; my @rp = (); foreach my $e (split ":", $p) { next if $e =~ /^\s*$/; $e = &correctpath($e); push @rp, $e unless grep {$_ eq $e} @rp; } return @rp; } sub findicon { my $i = shift || return undef; # Leading slash indicates absolute path, nothing to do. if ($i =~ m/^\//) { print STDERR "FINDICON: Absolute path for $i. Aborting.\n" if ($debug); return $i; } # Leading tilde indicates that we should expand it. if ($i =~ m/^~/) { print STDERR "FINDICON: Leading tilde for $i. Expanding.\n" if ($debug); return &expandtilde($i); } foreach my $p (@iconsearchpath) { my $correctpath = $p . $i; print STDERR "FINDICON: Searching for $correctpath.\n" if ($debug); if (-f $correctpath) { print "FINDICON: Found $correctpath.\n\n" if ($debug); return $correctpath; } } print STDERR "FINDICON: Didn't find anything for $i.\n\n" if ($debug); return undef; } sub setoption { my ($name, $val) = @_; print STDERR "SETOPTION: Setting $Xname*$name to $val\n" if ($debug); $main->optionAdd($Xname . '*' . $name => $val, 'interactive'); } sub extracticons { my $s; my %h; $s = "\x47\x49\x46\x38\x39\x61\x30\x00\x10\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x30\x00\x10\x00\x00\x02\x29\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x7b\x81\xde\xbc\x63\xa9\x35\xe1\x07\x02\xcb\x48\x96\x09\x9a"; $s .= "\xaa\x06\xdb\xaa\x70\x5c\x9a\x74\x76\xe7\xfa\xce\xf7\x7e\x52\x00"; $s .= "\x00\x3b"; $h{'DH.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x30\x00\x10\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x30\x00\x10\x00\x00\x02\x27\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x7b\x81\xde\xbc\x63\xd9\x85\xda\x37\x89\x1c\x49\x99\x00\x5a"; $s .= "\x89\xac\xe5\xbd\xf0\x28\x67\xf5\x8d\xe7\xfa\xce\x27\x05\x00\x3b"; $h{'DS.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x10\x00\x30\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x10\x00\x30\x00\x00\x02\x35\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x8b\xb3\x96\x80\x77\x08\x84\x60\xf7\x35\x62\x50\x2e\x27\x6a"; $s .= "\xa6\x29\xb2\x1a\x2f\xec\x3a\xeb\x9c\x9c\x78\x4e\x46\xe1\xae\x88"; $s .= "\x3d\x80\x9b\xa2\xf1\x88\x4c\x2a\x97\xcc\x62\x01\x00\x3b"; $h{'LH.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x10\x00\x30\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x10\x00\x30\x00\x00\x02\x30\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x8b\xb3\x96\x80\x77\x08\x84\x60\xf8\x35\xa4\x68\x9e\xa5\xa2"; $s .= "\xa2\x6c\xbb\x26\xb0\xd3\x3e\xea\xe8\xd2\xb9\x3e\xc5\xdb\x0f\x0c"; $s .= "\x0a\x87\xc4\xa2\x71\x58\x00\x00\x3b"; $h{'LS.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x10\x00\x30\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x10\x00\x30\x00\x00\x02\x36\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x8b\xb3\x5e\x60\x82\x1e\x7d\xe0\xd3\x7d\x10\x68\x3a\x63\xca"; $s .= "\x8c\x01\xab\xb8\xaf\x7c\xb8\x70\xb2\xd2\x08\xaa\xef\xf3\x79\x6b"; $s .= "\xf5\x62\x9b\xa2\xf1\x88\x4c\x2a\x97\xcc\x26\xa3\x00\x00\x3b"; $h{'RH.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x10\x00\x30\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x10\x00\x30\x00\x00\x02\x31\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x8b\xb3\x5e\x60\x82\x1e\x7d\xe0\x23\x8e\x4d\x69\x72\xa8\x83"; $s .= "\x7e\x67\x9b\x1e\xf0\x5b\xb2\xb5\xed\x92\xb9\x2e\xc5\xdb\x0f\x0c"; $s .= "\x0a\x87\xc4\xa2\xf1\x68\x28\x00\x00\x3b"; $h{'RS.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x30\x00\x10\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x30\x00\x10\x00\x00\x02\x2d\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x8b\x15\xc8\xfc\x80\xdf\x65\x20\x18\x56\x64\x70\x96\x50\x8a"; $s .= "\x6e\xea\xc3\x1a\xf1\xeb\xb9\xcb\xfc\x7e\xfa\xce\xdb\xf4\x0f\x0c"; $s .= "\x0a\x87\x9c\x02\x00\x3b"; $h{'UH.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x30\x00\x10\x00\x80\x00\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x30\x00\x10\x00\x00\x02\x28\x8c\x8f\xa9\xcb\xed\x0f\xa3\x9c\xb4"; $s .= "\xda\x8b\x15\xc8\xfc\x80\xdf\x65\xdf\x18\x5a\xe3\x59\x4e\xe7\x9a"; $s .= "\x42\xeb\xdb\x36\xef\x1c\x6b\x33\x5d\xe7\xfa\xce\xf7\x56\x01\x00"; $s .= "\x3b"; $h{'US.gif'} = $s; $s = "\x47\x49\x46\x38\x39\x61\x30\x00\x30\x00\x80\xff\x00\x00\x00\x00"; $s .= "\xc0\xc0\xc0\x21\xf9\x04\x01\x00\x00\x01\x00\x2c\x00\x00\x00\x00"; $s .= "\x30\x00\x30\x00\x00\x02\x9c\x8c\x8f\xa9\xcb\xed\x0f\xa3\x74\xa0"; $s .= "\x5a\x3b\xb3\xba\xfc\xea\xdc\x85\xde\xf7\x88\x66\x45\x36\xe7\x9a"; $s .= "\x6e\x26\xf2\xb6\x87\xc8\x84\xb2\x41\xd7\xb6\x9c\xeb\xdc\xbd\x53"; $s .= "\x75\x6e\x33\x0c\x65\x48\x04\x21\x93\x91\x20\xb3\xe4\x7c\x2e\x62"; $s .= "\x52\x5f\xb4\x5a\xec\x61\x61\xda\x6d\x76\xe9\xfd\xfe\xc2\x89\x2e"; $s .= "\xd9\x7c\x06\x93\xc5\xa8\x75\x59\xed\xbe\xba\xe7\xf4\x94\x3c\x3d"; $s .= "\xae\xdf\xc3\x7b\x6f\x7f\xfb\x87\x15\x58\x47\x58\xa8\x37\x83\xd3"; $s .= "\x56\x96\x08\x90\x85\x83\x08\xf5\xd8\xf8\xb8\x38\x19\xd0\x68\x89"; $s .= "\x39\x69\xa9\x72\xe9\x99\xb9\xe1\x09\x83\x88\x29\x4a\x91\x68\xca"; $s .= "\x29\x49\xca\x7a\xa9\x3a\x05\x69\xb4\xe8\x9a\xd9\x06\x6a\x88\x9b"; $s .= "\xdb\x52\x00\x00\x3b"; $h{'noicon.gif'} = $s; foreach (keys %h) { print "Extracting " . $_ . "... "; open (F, '>' . $_) or die "Cannot write to " . $_ . "\n"; binmode F; print F $h{$_}; close F; print "done\n"; } print "All icons have been extracted. They should now be moved to the\n"; print "correct directory, probably " . $iconloc . ".\n"; exit; } # ========================================================================== # Help # ========================================================================== sub help { pod2text($0); exit; } __END__ =head1 NAME belt - Configurable buttonmenu for X =head1 SYNOPSIS belt [ options ] =head1 DESCRIPTION Belt is a configurable "belt" that pops out a row of buttons for easy access to programs. It is inspired by the Macintosh version. =head1 USAGE The belt is retracted as default. By clicking on the belt button the belt pops out revealing buttons. The user can then click on the buttons on the belt to start the desired programs. Depending upon how it is configured the belt may hide back after starting the program using a left-click. Right-clicking always retracts the belt after starting the program. =head1 OPTIONS You don not have to type out the entire name of the option, as long as it is unique. A boolean value is either the string I or I. =over =item --overrideredirect boolean Should the overrideredirect directive be used for the main window. =item --unpost boolean Should the belt retract when a program is started from the buttons. =item --autopost boolean Should the belt extract the when mouse cursor is over the extract button. The time the mouse cursor has to be over the button is specified by the --autopostdelay option. =item --autopostdelay integer Specifies the delay from the mouse cursor enter the button area to the belt is extracted. If the cursor leaves the button within this time the belt is not extracted. =item --autounpost boolean Should the belt retract the when mouse cursor leaves the belt. The time the mouse cursor has to be out of the belt area is specified by the --autounpostdelay option. =item --autounpostdelay integer Specifies the delay from the mouse cursor leaves the button area to the belt is retracted. If the cursor enters the area again within this time the belt is not retracted. =item --bhactive boolean Sets whether the balloonhelp is active or not. The balloonhelp shows the associated name for each button if active. =item --bhdelay milliseconds Sets the time in milliseconds before a balloonhelp popup is displayed for a button. =item --bhmaindelay milliseconds Sets the time in milliseconds before a balloonhelp popup is displayed for the mainbutton. =item --bhbackground color Sets the background color for the balloonhelp popups. =item --bhfont fontname Sets the font for the balloonhelp popups. =item --maxicons integer Sets the maximum number of icons allowed on the belt. If the number of icons exceed this amount, arrows will appear to allow scrolling. =item --dynamic boolean Sets whether the program is affected by the WMMENUPATH environment variable when searching for program definitions. =item --ontop boolean Sets whether the belt should be on top, ie. if it should be raised as soon as it gets focus. =item --placement placement The placement of the belt. Should be one of the corners and not a size specification, just a placement specification (ie. +0-0). =item --direction string Which direction should the belt extend. The string can be I, I, I or I. The belt does not extend outside the screen which can lead to unintuitive packing order for the buttons if placement and directions does not match. =item --icondirectory directory The directory where the icons used by belt is located. This is I neccessarily the locations of the programicons. They can be located here but also in one of the directories specified by the --iconpath switch or by setting an absolute path in the F file. The directory specified in by this switch is searched before any directories in the --iconpath path. =item --iconpath path A path to search for the icons used by belt. =item --iconsize size The size of the icons. All icons (except for the arrows) are expected to be square. This is a limit of the largest icon. The icons can be of different sizes, but the same size is preferred. Icons larger than iconsize will be cropped. =item --configdirectory directory The location of the userconfiguration files. The directory name should have a trailing slash. The directory name is subject to a primitive form of tilde expansion. =item --configfile filename The filename of the configurationfile you wish to use if you do not want to use the default one. =item --menufile filename The filename of the local menu configuration file to be used. This can be used if you want multiple instances of belt. Filename should be relative to the directory specified by configdirectory. =item --extracticons Extracts the special (arrow)icons to the current directory. They should be moved to the directory where the special icons are to be stored. =item --verbose Show the configuration and add errormessages. =item --help Show this help. =back =head1 X RESOURCES These are the X resources that affect the appearance of belt and the default values. =over =item Belt*autopost: false =item Belt*autopostdelay: 300 =item Belt*autounpost: false =item Belt*autounpostdelay: 1500 =item Belt*unpost: false =item Belt*bhactive: true =item Belt*bhdelay: 1500 =item Belt*bhmaindelay: 5000 =item Belt*bhbackground: yellow =item Belt*bhfont: fixed =item Belt*maxicons: 8 =item Belt*dynamic: true =item Belt*ontop: true =item Belt*placement: +0-0 =item Belt*direction: right =item Belt*icondirectory: /usr/local/belt/icons/ =item Belt*iconpath: /misc/graphics/icons/reduced/48x48 =item Belt*iconsize: 48 =item Belt*configdirectory: ~/.beltconf/ =item Belt*configfile: F =item Belt*menufile: F =back They correspond directly to a command line option. =head1 THE CONFIG FILES There are primarily two types of files. Program settings is generally xresources and thus can lie in F<.Xdefaults>/F<.Xresources> or in the resources file in the users configuration directory. Button definitions follow the dynamic-menu style as in use at the Unix system at E.KTH.SE. The files should be called F and reside in a directory found in the WMMENUPATH or in the users configuration directory for local settings. The name of the local button definitions file can be overridden by the menufile directive. The format of the F-file is: module|cat|text|miniicon|applicationclass|icon|executable Where I is the name of the module, I is one of the predefined the category, I is the label in the final menu, I is a small icon for use in the window decoration, I is the X Window System class for the application, I is the path and icon associated with the application and I is the path, file and switches needed to run the program. Only I, I and I is used by belt. For example the line: ||XtermWide|||/usr/local/graphics/xterm.xpm|/bin/X11/xterm -132 is sufficient for use with belt. One should however specify all fields if applicable. Icons can either be specified with a full path or a relative path. If the path is relative first the icons is searched for in the icondirectory and then in the directories specified by iconpath. If the first character on the line is a # the line is considered to be a comment. =head1 CONFIGURATION The programs default settings has the lowest priority. The default settings can be overridden by xresources in the X resource database (ie F<.Xdefaults> or F<.Xresources>). These can in turn be overridden by resources specified in the sers configuration file, usually F<~/.beltconf/resources>. Command line options always override any previous settings. =head1 MULTIPLE INSTANCES It is possible to start several instances of belt. You should probably have different placement specifiers and/or direction specifiers. Also it might be a good idea to have different menufiles for the different instances as well as using the dynamic directive to turn off dynamic buttons from all instances but one. =head1 SMALLER/LARGER ICONS By default the icons should be 48x48, as indicated by iconsize. The only exception are the special arrowicons F<[UDRL][SH].gif> which are just one third as wide (default is 48x16). You can create your own icons and use them instead. The problem is that when you use the dynamic menu you cannot change icons for those entries. You can only change icons for icons for the local button definitions. Suppose that you have some icons that are 24x24 and the local button definition file is called F and resides in your configuration directory. Also you have made some small versions of the special icons with size 24x8 for the arrows and 24x24 for F. Then you can start belt with the command: belt --iconsize=24 --menufile=smallmenu.conf --iconpath=/home/staff/max/belt/SmallIcons/:/misc/icons/small =head1 EXITING To quit the application you can double-click on the button which posts the button bar using the right mouse button while holding down the Control key. Yes, it is a bit awkward but that combo you should not press by mistake. =head1 RESTARTING If you have edited the configuration files and wish to restart belt double-click on the button which posts the button bar using the middle button while holding down the Control key. This effectively destroys the old button bar and exec a new one with the same arguments as the original one was started with. =head1 FILES =over =item F Solid arrow pointing down (48x16). =item F Solid arrow pointing up (48x16). =item F Solid arrow pointing right (16x48). =item F Solid arrow pointing left (16x48). =item F Hollow arrow pointing down (48x16). =item F Hollow arrow pointing up (48x16). =item F Hollow arrow pointing right (16x48). =item F Hollow arrow pointing left (16x48). =item F Icon used when no icon is found. =item F Default name for local configuration file. =item F Name for local resource settings file, overriding X resources. =item F<.Xdefaults> or F<.Xresources> where the normal X resources are stored =back =head1 AUTHOR This software is Copyright 1999, 2000, 2001, 2002 by Max Zomborszki. Full licence can be found at the top of the code. =head1 ACKNOWLEDGMENTS Larry Wall for creating Perl and everyone making great modules for Perl. =head1 SEE ALSO Perl(1), X(1X) =head1 KNOWN PROBLEMS Lots of bugs. The autopost/autounpost features have not been tested extensively and initial versions had obvious problems. =head1 BUGS Send bug reports to max@e.kth.se =cut