#!/usr/pkg/bin/perl -w
#
# 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
#

#-----------------------------------------------------------------------------
# Copyright 2000, Olivier Chapuis
#-----------------------------------------------------------------------------

#-----------------------------------------------------------------------------
# Filter this script to pod2man to get a man page:
#   pod2man -c "Fvwm Utility" fvwm-themes-menuapp | nroff -man | less -e
#-----------------------------------------------------------------------------

# this may work but ...
#use strict;
use Getopt::Long;

my $prefix = $ENV{'prefix'} || '/usr/pkg';
my $ROOT_PREFIX = $ENV{'ROOT_PREFIX'} || '';
$ROOT_PREFIX = $ENV{'DESTDIR'} if $ENV{'DESTDIR'};
my $datadir = "${prefix}/share";

# "system variables"
my $version = '0.7.0';
my $fvwmVersion = '2.7.0';
my $scriptName = ($0 =~ m:([^/]+)$:, $1);
# user
my $rcFile = "themes-rc";
my $rcFile2 = "$rcFile-2";
my $userHome = $ENV{'HOME'} || "./.";
my $userDir = $ENV{'FVWM_USERDIR'} || "$userHome/.fvwm";
my $persThemeDir = "$userDir/themes/personal";
# site
my $ftDir = "/usr/pkg/share/fvwm2";
my $siteMenusUser="$ftDir/themes/default/menus-programs";
#my $siteDataDir = "$ftDir/data";
#my @siteDataFiles = ("$siteDataDir/applications.db");
# general
my @imagePath = ();
my @pathDirs = split(':',$ENV{PATH});
my $lang =	$ENV{'LANG'};

# options variables
my $removePopup = 0;
my $buildMenus = 0;
my $fvwmMenu = 1;
my $block = 0;
my $comName = "menuapp"; # via a script "must" be menuapp-pid_of_the_script
my $comPid = 0;
my $menuFiles = "";
my $site = 0;

GetOptions(
	"help"            => \&showHelp,
	"version"         => \&showVersion,
	"build-menus"     => \$buildMenus,
	"site"            => \$site,
	"remove-popup"    => \$removePopup,
	"menu-files=s"    => \$menuFiles,
	"com-mode"        => \$block,
	"com-name=s"      => \$comName
) || wrongUsage();

wrongUsage() unless ($block || $buildMenus);

$site || chdir($userDir) || die "No FvwmConfigHome $userDir";

my %filesLines = ();    # for storing the contents of the files
my %lastLine = ();      # last line of the files
my %menuInfo = ();      # General info on a menu
my %strMenu = ();       # The menus in a "structured form" (need %menuInfo)
my @menuPipeRead = ();  # Some info on the piperead command
my @selMenu = ();       # The selection for FvwmScript-Menus
my $lastSel = -1;       # Nbr of items in %selMenu -2
my @menuFilesList = (); # List of files with menu to look for
my @menuFilesListIn = ();

if ($buildMenus && $fvwmMenu) {

	my $change = 1;

	buildFilesLists();
	buildstrMenuFromFvwmMenu();
	checkActionInstrMenu();

	while ($change != 0) {
		$change = 0;
		$change = checkMenuInstrMenu();
		$change += checkPopupInstrMenu();
	}

	checkTitleAndNopInstrMenu();
	updateLinesInstrMenu();
	writeFiles();
	exit(0);

}

if ($fvwmMenu) {
	buildFilesLists();
	buildstrMenuFromFvwmMenu();
}

if ($block) {

	$comPid = $comName;
	$comPid =~ s/menuapp-//;
	$comPid = 0 if ($comPid !~ /^\d+$/);
	getImagePath();
	comLoop();

}

#-----------------------------------------------------------------------------
# For killing FvwmScript-Menus if an error happen in this script!

END {
	if ($block) {
		if ($?) {
			my $lockFifo = ".tmp-com-lock-" . $comName;
			my $inFifo = ".tmp-com-in-" . $comName;
			my $message = "fvwm-themes-menuapp: internal error $?\n";
			# actually $@ is never defined in END
			$message .= "\teval error: $@\n" if $@;
			$message .= "\tOS error: $!\n" if $!;
			# actually the following is never executed on unix
			$message .= "\tOS error 2: $^E\n" if $^E && !($! && $^E eq $!);
			
			unlink($lockFifo);
			unlink($inFifo);
		  	if ($comPid) {
				kill(9,$comPid);
				$message .= "\tkilling FvwmScript-Menus";
			}
			print STDERR "$message\n"; 
		}
	}
}

#-----------------------------------------------------------------------------
#
# communication loop
#
#-----------------------------------------------------------------------------

sub comLoop {
	
	my $outFifo = ".tmp-com-out-" . $comName;
	my $inFifo = ".tmp-com-in-"  . $comName;
	my $lockFifo = ".tmp-com-lock-" . $comName;
	my $command = "";
	my $return = "";
	my $index = 0;
	my $menu = "";
	my $selIndex = 0;
	my $a = 0;
	
	unlink($lockFifo);
	unlink($inFifo);
	myMakeFifo($lockFifo);
	
	while(1) {
		
		eval {
			local $SIG{ALRM} = \&checkScript;
			alarm(10);
			# block until com want to communicate
			open(LOCK,">$lockFifo") || die "cannot write fifo $lockFifo";
			alarm(0);
			close(LOCK);
		};
		if ($@ =~ /^cannot/) {
			print STDERR "$comName: cannot write fifo $lockFifo\n";
			unlink("$lockFifo");
			exit(1);
		}
		if ($@ =~ /^NoScript/) {
			print STDERR "$comName: No more FvwmScript-Menu: exit!\n";
			unlink("$lockFifo");
			exit(0);
		}
		if ($@ =~ /^Script/) {
			next;
		}
		
		# read the command.
		eval {
			local $SIG{ALRM} = sub { die "Timeout" };
			alarm(10);
			# block unless com is ready to write on $outFifo
			open(IN,"$outFifo") || die "cannot open $outFifo";
			alarm(0);
			($command)=(<IN>);
			close(IN);
		};
		if ($@ =~ /^cannot/) {
			print STDERR "$comName: cannot open $outFifo\n";
			unlink("$lockFifo");
			exit(1);
		}
		if ($@ =~ /^Timeout/) {
			print STDERR "$comName: com gave an invalid unlock!\n";
			next;
		}
		
		chomp($command);
		if ($command =~ /^menu-items\s+/) {
			$command =~ s/^menu-items\s+//;
			$return = itemsList($command,-1);
		}
		elsif ($command =~ /^item\s+/) {
			$command =~ s/^item\s+//;
			($menu,$index)=split(':',$command);
			$return =  itemsList($menu,$index);
		}
		elsif ($command =~ /^menus-list$/) {
			$return = menusList(-1);
		}
		elsif ($command =~ /^nbr-of-items\s+/) {
			$command =~ s/^nbr-of-items\s+//;
			$return = itemsList($command,-2);
		}
		elsif ($command =~ /^root-menu\s+/) {
			$command =~ s/^root-menu\s+//;
			$return = rootMenusList($command);
		}
		elsif ($command =~ /^root-menus-list$/) {
			$return = rootMenusList(-1);
		}
		elsif ($command =~ /^add-or-edit-item\s+/) {
			$command =~ s/^add-or-edit-item\s+//;
			@ARGV=split(' --',$command);
			$a = 1;
			while(defined $ARGV[$a]) {	
				$ARGV[$a] =~ s/^(\w\w=)\"/$1/g;
				$ARGV[$a] = "--". $ARGV[$a];
				$ARGV[$a] =~ s/--^\s*\"//g;
				$ARGV[$a] =~ s/\"\s*$//g;
				$a++;
			}
			$return = addOrEditItem();
		}
		elsif ($command =~ /^remove-item\s+/) {
			$command =~ s/^remove-item\s+//;
			($menu,$index)=split(':',$command);
			$return = removeItem($menu,$index);
		}
		elsif ($command =~ /^move-up\s+/) {
			$command =~ s/^move-up\s+//;
			($menu,$index)=split(':',$command);
			$return = moveUpDown($menu,$index,"Up");
		}
		elsif ($command =~ /^move-down\s+/) {
			$command =~ s/^move-down\s+//;
			($menu,$index)=split(':',$command);
			$return = moveUpDown($menu,$index,"Down");
		}
		elsif ($command =~ /^selection-items$/) {
			$return = selMenuItemsList();
		}		
		elsif ($command =~ /^add-item-to-selection\s+/) {
			$command =~ s/^add-item-to-selection\s+//;
			($menu,$index)=split(':',$command);
			$return = addToselMenu($menu,$index);
		}
		elsif ($command =~ /^remove-sel-item\s+/) {
			$command =~ s/^remove-sel-item\s+//;
			$return = removeItemInselMenu($command);
		}
		elsif ($command =~ /^remove-all-sel-item/) {
			$return = freeselMenu();
		}
		elsif ($command =~ /^copy-sel-item\s+/) {
			$command =~ s/^copy-sel-item\s+//;
			($menu,$index,$selIndex)=split(':',$command);
			$return = insertOneselItem($menu,$index,$selIndex);
		}
		elsif ($command =~ /^copy-all-sel\s+/) {
			$command =~ s/^copy-all-sel\s+//;
			($menu,$index)=split(':',$command);
			$return = insertAllselItem($menu,$index);
		}
		elsif ($command =~ /^try/) {
			$return = outPutfilesLines();
		}
		elsif ($command =~ /^save/) {
			writeFiles();
			next;
		}
		elsif ($command eq "exit") {
			unlink($lockFifo);
			exit(0);
		}
		else {
			print STDERR "$comName: unknown command $command\n";
			$return = "0";
		}
		
		myMakeFifo($inFifo);

		eval {
			local $SIG{ALRM} = sub { die "Timeout" };
			alarm(10);
			# this line block until com take the answer
			open(OUT,">$inFifo") || die "cannot write fifo $inFifo";
			alarm(0);
			print OUT "ok\n" . $return;
			close(OUT);
			unlink($inFifo);
		};
		if ($@ =~ /cannot/) {
			print STDERR "$comName: cannot write on fifo $inFifo\n";
			unlink($lockFifo);
			unlink($inFifo);
			exit(1);
		}
		if ($@ =~ /Timeout/) {
			print STDERR "$comName: com do not read my answer!\n";
		}
		
	}
}

#----------------------------------------------------------------------------
# An alarm handler (called from eval block):
sub checkScript {

	die "Script" unless ($comPid);

	my $test = 0;
	my $lockFifo = ".tmp-com-lock-" . $comName;
	
	$test = 1 if kill 0 => $comPid;
	
	if ($test) { die "Script"; }
	else { unlink($lockFifo);die "NoScript"; }
}

# ----------------------------------------------------------------------------
#
# ImagePath utils
#
# ----------------------------------------------------------------------------

sub getImagePath {
	# there are more ... (Gnome, kde, ... ?)
	my @imagePathFiles = ("$userDir/$rcFile2");
	my ($file,$multiline,$tmp,$im,$line) = ("","","","",""); 

	for $file (@imagePathFiles) {
		open(IN,"$file") || die "Cannot read $file";
		$multiline = "";
		while(<IN>) {
			if ( /\\$/ && ! /\\\\$/) {
				s/\\$//;
				chomp;
				$multiline = $multiline . $_;
				next;
			} else {
				$line= $multiline . $_;
				$multiline = "";
			}
			chomp($line);
			$line =~ s/\s+//;
			if ($line =~ /^imagepath/i) {
				$line =~ s/^imagepath//i;
				$tmp = getNextQuotedToken(\$line);
				$tmp = `echo $tmp`;
				chomp($tmp);
				if ($tmp =~ /^\+/) {
					$tmp =~ s/\+//g;
					$tmp =~ s/^://;
					$tmp =~ s/:$//;
					if ($im ne "") { $im = $im .":". $tmp; }
					else  { $im = $tmp; }
				}
				elsif ($tmp =~ /\+$/) {
					$tmp =~ s/\+//g;
					$tmp =~ s/^://;
					$tmp =~ s/:$//;
					if ($im ne "") { $im = $tmp .":". $im; }
					else  { $im = $tmp; }
				}
				elsif ($tmp =~ /\+/) {
					my ($tmp1,$tmp2) = split(/\+/,$tmp);
					$tmp1 =~ s/\+//g; $tmp2 =~ s/\+//g;
					$im = $tmp1 .":". $im . ":" . $tmp2;
					$im =~ s/^://; $im =~ s/^:$//;
				}
				else {
					$tmp =~ s/^://;
					$tmp =~ s/:$//;
					$im = $tmp;
				}
			}
		}
	}
	@imagePath = split(':',$im);
}

sub getPrettyIconName {
	my ($icon) = @_;
	my $path;
	foreach $path (@imagePath) {
		next if $path eq "";
		return $1 if $icon =~ m!^$path/*(.*)$!;
	}
	return $icon;
}

# ----------------------------------------------------------------------------
#
# Files buildings
#
# ----------------------------------------------------------------------------

sub buildFilesLists {

	if ($menuFiles ne "") {
		@menuFilesList = @menuFilesListIn = split(',',$menuFiles);
	} else {
		if ($site) {
			@menuFilesList = ("$siteMenusUser");
			@menuFilesListIn = ("$siteMenusUser");
			return;
		}
		@menuFilesList = ("$persThemeDir/menus-programs");
		if (! -f "$persThemeDir/menus-programs") {
			@menuFilesListIn = ("$siteMenusUser");
		} else {
			@menuFilesListIn = ("$persThemeDir/menus-programs");
		}
		push @menuFilesList, "$persThemeDir/menus-extra";
		push @menuFilesListIn,"$persThemeDir/menus-extra";
	}
}



# ----------------------------------------------------------------------------
#
# build a structured menu "strMenu" from a fvwm menu
#
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# Main of the building

sub buildstrMenuFromFvwmMenu {
	
	my ($file,$line,$multiline,$tmpline,$currentMenu,$side,$destroy,$provide)= 
		("","","","","","","");
	my $outFile;
	my ($nbr,$index,$utils,$active,$i,$j) = (0,0,0,0,0,0);

	foreach $file (@menuFilesListIn) {
		next if -f $file || "$file" eq "$persThemeDir/menus-extra";
		print STDERR "$comName: <<Warning>> No file $file";
	}

	for $file (@menuFilesListIn) {

		$outFile = $menuFilesList[$j];
		$j++;

		if (! -f "$file" && "$file" eq "$persThemeDir/menus-extra") {
			# creating virtual menus-extra:
			# DestroyMenu MenuFvwmPersonal
			# AddToMenu MenuFvwmPersonal "Personal" Title
			$filesLines{$outFile}[0]{line} = "DestroyMenu MenuFvwmPersonal\n";
			$filesLines{$outFile}[1]{line} = 
				"AddToMenu   MenuFvwmPersonal \"Personal\" Title\n";
			$menuInfo{"MenuFvwmPersonal"}{destroyline}=
					\$filesLines{$outFile}[0]{line};
			addItemMenuInfo("MenuFvwmPersonal","");
			$strMenu{"MenuFvwmPersonal"}[0]{line} =
					\$filesLines{$outFile}[1]{line};
			addItemstrMenu("MenuFvwmPersonal","Title", "Personal",
				"","","", "", "", "", "",$outFile,"1");
			$filesLines{$outFile}[1]{menu} = "MenuFvwmPersonal";
			$filesLines{$outFile}[1]{index} = 0;
			$lastLine{"$outFile"}= 1;
			next;
		}

		open(IN,"$file") || die "Cannot read $file";
		$nbr = 0;
		$multiline = "";

		while(<IN>) {

			if ( /\\$/ && ! /\\\\$/) {
				s/\\$//;
				chomp;
				$multiline = $multiline . $_;
				next;
			} else {
				$line= $multiline . $_;
				$multiline = "";
			}

			$filesLines{$outFile}[$nbr]{line} = $line;

			$tmpline = $line;
			$tmpline =~ s/^\s*//;

			$side ="";
			chomp($tmpline);

			if (isMenuLine(\$currentMenu,\$tmpline,\$side)) {

				addItemMenuInfo($currentMenu,$side);
				$index=$menuInfo{$currentMenu}{itemsnbr};
				$strMenu{$currentMenu}[$index]{line} =
					\$filesLines{$outFile}[$nbr]{line};
				addItemstrMenu($currentMenu, analyzeMenuLine($tmpline),
					$outFile, $nbr);
				$filesLines{$outFile}[$nbr]{menu} = $currentMenu;
				$filesLines{$outFile}[$nbr]{index} = $index;
 
			} elsif ($tmpline =~ /^DestroyMenu/i) {

				$tmpline =~ s/^DestroyMenu//i;
				$destroy = getNextQuotedToken(\$tmpline);
				if (defined $menuInfo{$destroy}{itemsnbr}) {
					# need to do somthing here ... but what ?
					#undef($menuInfo{$destroy});
					#undef($strMenu{$destroy});
					print STDERR "Warning: Destroying a menu after creation is" . 
						" not yet supported\n";
				}
				$menuInfo{$destroy}{destroyline}=
					\$filesLines{$outFile}[$nbr]{line};
				$currentMenu = "";

			} elsif ($tmpline =~ /^PipeRead/i) {
				$tmpline =~ s/^PipeRead\s+//i;
				($utils,$i,$provide,$active)=analyzePipeRead($tmpline);
				if ($utils) {
					$menuPipeRead[$i]{line} = \$filesLines{$outFile}[$nbr]{line};
					$menuPipeRead[$i]{provide} = $provide;
					$menuPipeRead[$i]{active} = $active;
				}
				# fixe me!
				$currentMenu = "";

			} elsif ($tmpline =~ /^AddTo/i) {
				$currentMenu = "";
			}

			$nbr++;

		}
		$lastLine{"$outFile"}=$nbr-1;
	}
}

#-----------------------------------------------------------------------------
# Check if we have a menu line and set the current menu, the side pics and
# color and clear some part of the "line".

sub isMenuLine {
	my ($curMenu, $line, $side) = @_;
	my $s="";

	$$line =~ s/^\s*//;

	if ($$curMenu ne "" && $$line =~ /^\+/) {
		$$line =~ s/^\+\s*//;
		return 1;
	}
	if ($$line =~ /^AddToMenu/i) {
		$$line =~ s/^AddToMenu\s*//i;
		$$curMenu = getNextQuotedToken($line);
		$s = $$curMenu;
		$s =~ s/@@//g;
		if ( $s =~ /@/) {
			$s = substr($s,index($s,'@'));
			$$side = $s;
			$s =~ s/\^/\\^/g;
			$$curMenu =~ s/$s//;
		}
		return 1;
	}
	return 0;
}

#-----------------------------------------------------------------------------
# Analyze a fvwm menu line
 
sub analyzeMenuLine {

	my ($line) =  @_;
	my $trueaction = "";
	my $useraction = "";
	my $checkaction = "";
	my $workingdir = "";
	my $cdaction = "";
	my $type = "";
	my $name = "";
	my $lefticon = "";
	my $upicon = "";
	my $windowtitle = "";
	my $tmp = "";

	$line =~ s/^\s*//;
	return ("empty","","","","","","","","") if ($line eq "");

	$name = getNextQuotedToken(\$line);

	# left icon if any
	$tmp = $name;
	$tmp =~ s/\%\%//g;
	if ($tmp =~ /\%/) {
		
		$lefticon= substr($tmp,index($tmp,'%'));
		$lefticon =~ s/^\%//;
		$lefticon= substr($lefticon,0,rindex($lefticon,'%'))
			if ($lefticon =~ /\%/);
		$name =~ s/\%$lefticon\%*//;
		$lefticon =~ s/\%//g;
	}

	# up icon if any, Need some fixes
	$tmp = $name;
	$tmp =~ s/\*\*//;
	if ($tmp =~ /\*/) {
		$upicon=
			substr($tmp,index($tmp,'*'),rindex($tmp,'*'));
		$name =~ s/$upicon//;
		$upicon =~ s/\*//g;
	}

	if ($line =~ /^Exec\s+/i) {
		$type = "Exec";
		$line =~ s/^Exec\s+//i;
		$trueaction = $line;
		$useraction = $trueaction;
		if ($line =~ /^exec/) {
			$useraction =~ s/^exec\s+//i;
			$checkaction = $useraction;
			$checkaction =~ s/^killall\s+//;
			$checkaction = substr($checkaction,0,index($checkaction,' '))
				if ($checkaction =~ / /);
			$checkaction =~ s/\s//g;
		}
		elsif ($line =~ /^cd/) {
			$useraction =~ s/^cd\s+//i;
			$workingdir = substr($useraction,0,index($useraction,';'));
			$workingdir =~ s/\s//g;
			$workingdir =~ s/\/$//;
			$useraction = substr($useraction,index($useraction,"exec")+5);
			$useraction =~ s/^\.\///;
			$checkaction = $useraction;
			$checkaction = substr($checkaction,0,index($checkaction,' '))
				if ($checkaction =~ / /);
			$checkaction = "$workingdir/$checkaction";
		} else {
			$checkaction = $useraction;
			$checkaction =~ s/^killall\s+//;
			$checkaction = substr($checkaction,0,index($checkaction,' '))
				if ($checkaction =~ / /);
			$checkaction =~ s/\s//g;
		}
	}
	elsif ($line =~ /^Popup\s+/i) {
		$type = "Popup";
		$trueaction = $line;
		$line =~ s/^Popup\s+//i;
		$useraction = getNextQuotedToken(\$line);
	}
	elsif ($line =~ /^Menu\s+/i) {
		$type = "Menu";
		$trueaction = $line;
		$line =~ s/^Menu\s+//i;
		$useraction = getNextQuotedToken(\$line);
	}
	elsif ($line =~ /^Nop/i) {
		$type = "Nop";
	}		
	elsif ($line =~ /^Title/i) {
		$type = "Title";
	}
	elsif ($line =~ /^FuncFvwmRunInXterm/i) {
		$type = "ExecInTerm";
		$trueaction = $line;
		$line =~ s/^FuncFvwmRunInXterm//i;
		$windowtitle = getNextQuotedToken(\$line);
		$useraction = getNextQuotedToken(\$line);
		$checkaction = $useraction;
		$checkaction = substr($checkaction,0,index($checkaction,' '))
			if ($checkaction =~ / /);
	}
	elsif ($line =~ /^FuncFvwmKillEventAndRun/i) {
		$type = "ExecNoSound";
		$trueaction = $line;
		$line =~ s/^FuncFvwmKillEventAndRun//i;
		$useraction = getNextQuotedToken(\$line);
		$checkaction = $useraction;
		$checkaction = substr($checkaction,0,index($checkaction,' '))
			if ($checkaction =~ / /);
		}
		elsif ($line =~ /^FuncFvwmViewFileTail/i) {
		$type = "TailFile";
		$trueaction = $line;
		$line =~ s/^FuncFvwmViewFileTail//i;
		$windowtitle = getNextQuotedToken(\$line);
		$useraction = getNextQuotedToken(\$line);
		$checkaction = $useraction;
		$checkaction = substr($checkaction,0,index($checkaction,' '))
			if ($checkaction =~ / /);
	}
	elsif ($line =~ /^Restart/i) {
		$type = "FvwmCommand";
		$trueaction = $line;
		$useraction = $trueaction;
		$line =~ s/^Restart//i;
		$checkaction = getNextQuotedToken(\$line);
	}
	else {
		$type = "FvwmCommand";
		$trueaction = $line;
		$useraction = $trueaction;
	}

	return ("$type","$name","$lefticon","$upicon","$trueaction","$useraction","$workingdir","$checkaction","$windowtitle");
}

#-----------------------------------------------------------------------------
# Quite weak ...
 
sub analyzePipeRead {

	my ($line) = @_;
	my $index = "0";
	my $utils = 0;
	my $provide = "";
	my $active = "";

	if ($line =~ /fvwm-menu-xlock/) {
		$utils = 1;
		$active = checkApp("xlock");
		$line = substr($line,index($line,'name')+4);
		$provide = getNextQuotedToken(\$line);
	}

	while (defined $menuPipeRead[$index]) { $index++; }
			
	return ($utils,$index,$provide,$active);
}

#----------------------------------------------------------------------------
#
BEGIN {
	$menuGroupByName = {
		"System" => "System",
		"Shells" => "Shells",
		"ScreenSaver" => "ScreenSaver",
		"ScreenSaverModes" => "ScreenSaver::XlockSaver",
		"ScreenLockModes" => "ScreenSaver::XlockLock",
		"Applications" => "Applications",
		"Science" => "Science",
		"Editors" => "Editors",
		"Internet" => "Internet",
		"Graphics" => "Graphics",
		"Multimedia" => "Multimedia",
		"Games" => "Games",
		"Amusements" => "Amusements",
		"Xsnow" => "Amusement::Xsnow",
		"MesaDemos" => "Amusement::MesaDemos",
		"Quit" => "Quit",
		"Help" => "Help",
		"ManPages" => "ManPages",
		"CDE" => "CDE",
	};
}

sub getGroupByMenuName {
	my ($menu) = @_;
	$menu =~ s/^MenuFvwm//;
	return $menuGroupByName->{$menu};
}

#----------------------------------------------------------------------------
# strMenu Clean up

sub checkActionInstrMenu {
	my $key;
	my $i;
	
	foreach $key (keys(%strMenu)) {
		for ($i = 0; $i <= $menuInfo{"$key"}{itemsnbr}; $i++) {
			if ($strMenu{"$key"}[$i]{checkaction} eq "") {
				$strMenu{"$key"}[$i]{active} = 1 
					if ($strMenu{"$key"}[$i]{type} eq "FvwmCommand");
			} else {
				$strMenu{"$key"}[$i]{active} =
					checkApp($strMenu{"$key"}[$i]{checkaction});
			}
		}
	}
}

sub checkPopupInstrMenu {
	my $change = 0;
	my $i = 0;
	my $j = 0;
	my $key;
	my $test;
	my $menu;

	foreach $key (keys(%strMenu)) {
		for ($i = 0; $i <= $menuInfo{"$key"}{itemsnbr}; $i++) {
			if ($strMenu{"$key"}[$i]{type} eq "Popup" || 
				 $strMenu{"$key"}[$i]{type} eq "Menu") {

				$menu =  $strMenu{"$key"}[$i]{useraction};

				$j = 0; $test = 0;
				while(defined $menuPipeRead[$j]) {
					$test = 1 if ($menu eq "$menuPipeRead[$j]{provide}"
						&& $menuPipeRead[$j]{active});
					$j = $j + 1;
				}

				if (!$strMenu{"$key"}[$i]{active} && 
						($menuInfo{$menu}{active} || $test)) {
					$strMenu{"$key"}[$i]{active}=1;
					$menuInfo{"$key"}{submenus} .= ","
						if ($menuInfo{"$key"}{submenus} ne "");
					$menuInfo{"$key"}{submenus} .= $menu;
					$change = 1;
				}

			} # if
		} # for
	} # foreach

	return $change;
}

sub checkMenuInstrMenu {
	my $test = 0;
	my $change = 0;
	my $key;

	foreach $key (keys(%strMenu)) {
		$test = 0;
		for ($i = 0; $i <= $menuInfo{"$key"}{itemsnbr}; $i++) {
			if ($strMenu{"$key"}[$i]{active} == 1) {
			 	$test = 1;
		  	}
	  	}
		if ($test && !$menuInfo{"$key"}{active}) {
			$menuInfo{"$key"}{active} = 1;
			$change = 1;	
		}
	}
	return $change;
}

sub checkTitleAndNopInstrMenu {
	my $hasActiveAfter = 0;
	my $ok = 1;
	my $hasActiveBefore = 0;
	my $key;

	foreach $key (keys(%strMenu)) {
		for ($i = 0; $i <= $menuInfo{"$key"}{itemsnbr}; $i++) {
			$hasActiveBefore = 1 if ($strMenu{"$key"}[$i]{active});
			if ($strMenu{"$key"}[$i]{type} =~ /^(Nop|Title|empty)$/) {
				$ok = 1; $j = $i + 1; $hasActiveAfter = 0;
				while($ok && $j <= $menuInfo{"$key"}{itemsnbr}) {
					$hasActiveAfter = 1 if ($ok && $strMenu{"$key"}[$j]{active});
					$ok = 0
						if $strMenu{"$key"}[$j]{type} =~ /^(Nop|Title)$/;
					$j++;
				}
				if (($i == 0 && ($strMenu{"$key"}[$i]{type} eq "Title" ||
						$strMenu{"$key"}[$i]{type} eq "empty") && 
						$menuInfo{"$key"}{active}) ||
						($hasActiveBefore && $hasActiveAfter)) {
					$strMenu{"$key"}[$i]{active} = 1;
					$hasActiveBefore = 0;
				}
			}
		}
	}
}

sub updateLinesInstrMenu {
	my $i=0;
	my $key;
	
	foreach $key (keys(%strMenu)) {

		if (defined $menuInfo{"$key"}{destroyline}) {
			if ($menuInfo{"$key"}{active} == 1) {
				${$menuInfo{"$key"}{destroyline}} =~ s/^\#//;
		  	} else {
			 	${$menuInfo{"$key"}{destroyline}} = "#". 
					${$menuInfo{"$key"}{destroyline}}
			  		unless (${$menuInfo{"$key"}{destroyline}} =~ /^\#/);
			}
		}
		 
		for ($i = 0; $i <= $menuInfo{"$key"}{itemsnbr}; $i++) {
			if ($strMenu{"$key"}[$i]{active} == 1) {
				${$strMenu{"$key"}[$i]{line}} =~ s/^\#//;
		  	} else {
			 	${$strMenu{"$key"}[$i]{line}} = "#". ${$strMenu{"$key"}[$i]{line}}
			  		unless (${$strMenu{"$key"}[$i]{line}} =~ /^\#/);
			}
		}

	}

	$i=0;
	while(defined $menuPipeRead[$i]) {
		if ($menuPipeRead[$i]{active} == 1) {
			${$menuPipeRead[$i]{line}} =~ s/^\#//;
		} else {
			${$menuPipeRead[$i]{line}} = "#". ${$menuPipeRead[$i]{line}}
				unless (${$menuPipeRead[$i]{line}} =~ /^\#/);
	  	}
		$i++;
	}

}

# ----------------------------------------------------------------------------
#
# strMenu and filesLines functions
#
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# add item to menuInfo 

sub addItemMenuInfo {
	my ($menu,$side) = @_;
	my $key;
	my $mIsDef = 0;
	
	# need to do that since menu names are not case sensitive
	foreach $key (keys(%menuInfo)) {
		if (eqi($key,$menu) && defined $menuInfo{$menu}{itemsnbr}) {
			$mIsDef = 1;
			$menu = $key;
		}
	}

	if ($mIsDef) {
		$menuInfo{$menu}{itemsnbr} = $menuInfo{$menu}{itemsnbr}+1;
		$menuInfo{$menu}{side} = $side unless ($side eq ""); 
	} else {
		$menuInfo{$menu}{itemsnbr} = 0;
		$menuInfo{$menu}{side} = $side;
		$menuInfo{$menu}{submenus} = "";
		$menuInfo{$menu}{group} = getGroupByMenuName($menu);
		$menuInfo{$menu}{active} = 0;
	}
}

# ----------------------------------------------------------------------------
# add an item to strMenu

sub addItemstrMenu {
	
	my ($menu,$type,$name,$lefticon,$upicon,$trueaction,$useraction,$workingdir,
		$checkaction,$windowtitle,$file,$linenbr) = @_;

	$index = $menuInfo{$menu}{itemsnbr};
	$strMenu{$menu}[$index]{type} = $type;
	$strMenu{$menu}[$index]{name} = $name;
	$strMenu{$menu}[$index]{lefticon} = $lefticon;
	$strMenu{$menu}[$index]{upicon} = $upicon;
	$strMenu{$menu}[$index]{trueaction} = $trueaction;
	$strMenu{$menu}[$index]{useraction} = $useraction;
	$strMenu{$menu}[$index]{workingdir} = $workingdir;
	$strMenu{$menu}[$index]{checkaction} = $checkaction;
	$strMenu{$menu}[$index]{windowtitle} = $windowtitle;
	$strMenu{$menu}[$index]{group} =  getGroupByMenuName($menu);
	if ($buildMenus && 
		(!($type eq "Popup" || $type eq "Menu") ||
		 (defined(getGroupByMenuName($useraction)) && $removePopup))) {
		$strMenu{$menu}[$index]{active} = 0;
	} else {
		$strMenu{$menu}[$index]{active} = 1;
	}
	$strMenu{$menu}[$index]{file} = $file;
	$strMenu{$menu}[$index]{linenbr} = $linenbr;
  
}
	
# ----------------------------------------------------------------------------
# remove an item (comLoop)

sub removeItem {
	my ($menu,$item) = @_;
	my $line = "";
	my $index=0;
	my $i;
	
	return 0 if (! defined $menuInfo{$menu}{itemsnbr});
	for ($i = 0; $i <= $menuInfo{$menu}{itemsnbr}; $i++) {
		next if ($strMenu{$menu}[$i]{type} eq "empty");
		$index++;
		if ($item == $index) {
			$strMenu{$menu}[$i]{type} = "empty";
			#$line =~ s/^\s*//;
			if (${$strMenu{$menu}[$i]{line}} =~ /^AddToMenu\s+/i) {
				${$strMenu{$menu}[$i]{line}} = "AddToMenu " . $menu . "\n";
			} else {
				${$strMenu{$menu}[$i]{line}} = "";
			}
			return 1;
		}
	}
	return 0;
}

# ----------------------------------------------------------------------------
# edit or add an item (comLoop)
 	
sub addOrEditItem {
	my $main = shift(@ARGV);  
	my $menu = "";
	my $i;
	my $index;
	my $line = "";
	my $myItem = "";
	my $wdOrwt = "";
	my %newItem = (type => "", name => "", lefticon => "", 
		useraction => "", workingdir => "", windowtitle => "");

	GetOptions(
		"it:s"  => \$myItem,
		"ti:s"   => \$newItem{type},
		"na:s"   => \$newItem{name},
		"mi:s"  => \$newItem{lefticon},
		"ac:s"   => \$newItem{useraction},
		"wd:s"  => \$wdOrwt,
	) || return 0;

	#print STDERR "EorA: $myItem, $newItem{type},$newItem{name}," .
	#	"$newItem{lefticon},$newItem{useraction},$wdOrwt,END\n";
	($menu,$index) = split(':',$myItem);
	$newItem{type} = getTypeFromPretty($newItem{type});
	$newItem{lefticon}=getPrettyIconName($newItem{lefticon})
		if $newItem{lefticon} ne "";
	
	if (! defined $menuInfo{$menu}{itemsnbr}) {
		$menuInfo{$menu}{itemsnbr} = 0;
		$menuInfo{$menu}{side} = "";
		$menuInfo{$menu}{submenus} = "";
		$menuInfo{$menu}{group} = getGroupByMenuName($menu);
		$menuInfo{$menu}{active} = 1;
		$i = -1;
	} else {
		if ($index == 0) { $i = $menuInfo{$menu}{itemsnbr}; }
		else { $i = trueIndex($menu,$index); }
	}

	if ($newItem{type} eq "Exec") {
			$newItem{workingdir} = $wdOrwt;
		if ($newItem{workingdir} eq "") {
			$newItem{trueaction} = "Exec exec " . $newItem{useraction};
		} else {
			# may have to do more check here ...
			$strMenu{$menu}[$i]{trueaction} = "Exec cd " . 
				$newItem{workingdir} . "; " . "exec ./" . $newItem{useraction};
		}
	}
	elsif ($newItem{type} eq "ExecInTerm") {
		$newItem{windowtitle} = $wdOrwt;
		$newItem{trueaction} = "FuncFvwmRunInXterm " . 
			"\"$newItem{windowtitle}\" \"" . $newItem{useraction} . "\"";
	}
	elsif ($newItem{type} eq "TailFile") {
		$newItem{windowtitle} = $wdOrwt;
		$newItem{trueaction} = "FuncFvwmViewFileTail " . 
			"\"$newItem{windowtitle}\" \"" . $newItem{useraction} . "\"";
	}
	elsif ($newItem{type} eq "ExecNoSound") {
		$newItem{trueaction} = "FuncFvwmKillEventAndRun " 
			. $newItem{useraction};
	}
	elsif ($newItem{type} eq "Popup") {
		$newItem{trueaction} = "Popup ". $newItem{useraction};
	}
	elsif ($newItem{type} eq "Menu") {
		$newItem{trueaction} = "Menu ". $newItem{useraction};
	}
	elsif ($newItem{type} eq "Nop") {
		$newItem{trueaction} = "Nop";
		$newItem{lefticon} = "";
		$newItem{name} = "";
	}
	elsif ($newItem{type} eq "Title") {
		$newItem{trueaction} = Title;
	}
	else {
		$newItem{trueaction} = $newItem{useraction};
	}

	if ($i <= 0) {
		$line = "AddToMenu " . $menu . " ";
	}
	elsif ($main ne "A" && 
			${$strMenu{$menu}[$i]{line}} =~ /^\s*addtomenu/i) { 
		$line = "AddToMenu " . $menu . " ";
	} else {
		$line = "+ ";
	}

	$line .= "\"" . $newItem{name};
	$line .= "\%" . $newItem{lefticon} . "\%" if ($newItem{lefticon} ne "");
	$line .=  "\" " .  $newItem{trueaction} . "\n";

	if ($main eq "A") {
		insertItem($menu,$i,$line,\%newItem);
	} else {		
		foreach $key (keys(%newItem)) {
			$strMenu{$menu}[$i]{$key} = $newItem{$key}
		}
		${$strMenu{$menu}[$i]{line}} = $line;
	}

	return 1;
}

# ----------------------------------------------------------------------------
# 	insert a new item and line: this is tricky :o) (comLoop)

sub insertItem {
	my ($menu,$i,$line,$refHashItem) = @_;
	my ($j,$k,$l) = ("0","0","0");
	my $tmpLine;
	my $tmpFile;
	my $tmpMenu;
	my $tmpIndex;
	my $lineNbr;
	my $file;
	my $key;

	if ($i == -1) {
		# new menu!
		#try to found the good file ...
		foreach $menu (keys(%strMenu)) {
		 	foreach $key (keys(%strMenu)) {
	  			for ($l = 0; $l <= $menuInfo{"$key"}{itemsnbr}; $l++) {
					$file = $strMenu{"$key"}[$l]{file} 
						if (eqi($menu,$strMenu{$key}[$l]{useraction}) &&
						($strMenu{"$key"}[$l]{type} eq "Menu" || 
						$strMenu{"$key"}[$l]{type} eq "Popup"));
				}
			}
			$file = $menuFilesList[0] if (! defined $file);
		}
		#print STDERR "new menu in file $file\n";
		$lineNbr = $lastLine{"$file"}+1;
		$filesLines{"$file"}[$lineNbr]{line} = "\n";
		$lineNbr = $lastLine{"$file"}+2;
		$filesLines{"$file"}[$lineNbr]{line} = "DestroyMenu $menu\n";
		$lineNbr = $lastLine{"$file"} = $lastLine{"$file"}+3;
	} else {
		# where we want to insert the new line
		$lineNbr =  $strMenu{$menu}[$i]{linenbr} + 1;
		$file = $strMenu{$menu}[$i]{file};

		# "shift" the menu
		for ($j = $menuInfo{$menu}{itemsnbr}; $j >= ($i+1); $j--) {
	 		$k=$j+1;
			foreach $key (keys(%{$strMenu{$menu}[$j]})) {
				if ($key eq "line") {
					$tmpLine = $strMenu{$menu}[$j]{linenbr};
					$tmpFile = $strMenu{$menu}[$j]{file};
					if ("$tmpFile" eq "$file") {
		  				$strMenu{$menu}[$k]{line} = 
							\$filesLines{"$file"}[$tmpLine+1]{line};
					} else {
						$strMenu{$menu}[$k]{line}= 
							\$filesLines{"$tmpFile"}[$tmpLine]{line};
					}
					$filesLines{"$tmpFile"}[$tmpLine]{index}++;
				} else {
					$strMenu{$menu}[$k]{$key} = $strMenu{$menu}[$j]{$key};
				}
	  		}
		}
		$menuInfo{$menu}{itemsnbr}++;

		# rearrange the menu lines ...
		for ($j = $lastLine{"$file"}; $j >= $lineNbr; $j--) {
			$k = $j+1;
			$filesLines{"$file"}[$k]{line} = $filesLines{"$file"}[$j]{line};
			if (defined $filesLines{"$file"}[$j]{menu}) {
				$tmpMenu = $filesLines{"$file"}[$k]{menu} = 
					$filesLines{"$file"}[$j]{menu};
				$tmpIndex = $filesLines{"$file"}[$k]{index} = 
					$filesLines{"$file"}[$j]{index};
				undef($filesLines{"$file"}[$j]{menu});
				undef($filesLines{"$file"}[$j]{index});
				$strMenu{$tmpMenu}[$tmpIndex]{linenbr}++;
			}
		}
		$lastLine{"$file"}++;
	}

	# add the item
	$filesLines{"$file"}[$lineNbr]{line} = $line;
	$filesLines{"$file"}[$lineNbr]{menu} = $menu;
	$filesLines{"$file"}[$lineNbr]{index} = $i+1;
	foreach $key (keys(%{$refHashItem})) {
		$strMenu{$menu}[$i+1]{$key} = $$refHashItem{$key} if ($key ne "line");
	}
  	$strMenu{$menu}[$i+1]{line} = \$filesLines{"$file"}[$lineNbr]{line};
	$strMenu{$menu}[$i+1]{file} = $file;
	$strMenu{$menu}[$i+1]{linenbr} = $lineNbr;	
}

#-----------------------------------------------------------------------------
# Move up or Down a menu item (comLoop)

sub moveUpDown {
	my ($menu,$item,$d) = @_;
	my $line = "";
	my $index=0;
	my $i;
	my $j;
	my $key;
	my $done;
	my $tmp;
	my $tmp2;
	
	return 0 if ( !defined $menuInfo{$menu}{itemsnbr} || 
		($item <2 && $d eq "Up"));
	for ($i = 0; $i <= $menuInfo{$menu}{itemsnbr}; $i++) {
		next if ($strMenu{$menu}[$i]{type} eq "empty");
		$index++;
		if ($item == $index) {
			if ($d eq "Down") { 
				$done = 0; $j = $i; 
				while(!$done) {
					$j++;
					$done = 1 if (!defined $strMenu{$menu}[$j]{type} ||
						($strMenu{$menu}[$j]{type} ne "empty"));
				}
			}
			return 0 if (!defined $strMenu{$menu}[$j]{type});
			foreach $key (keys(%{$strMenu{$menu}[$i]})) {
				if ($key eq "line") {
					$tmp = ${$strMenu{$menu}[$i]{line}};
					$tmp2 = ${$strMenu{$menu}[$j]{$key}};
					# we can do better but I am litle bit tied!
					if ($tmp =~ /^addtomenu/i && $tmp2 !~ /^addtomenu/i) {
						$tmp2 =~ s/^\+/AddToMenu $menu/;
						#$tmp =~ s/^addtomenu\s+$menu/\+/i;
					}
					if ($tmp2 =~ /^addtomenu/i && $tmp !~ /^addtomenu/i) {
						$tmp =~ s/^\+/AddToMenu $menu/;
						#$tmp2 =~ s/^addtomenu\s+$menu/\+/i;
					}					
					${$strMenu{$menu}[$i]{$key}} = $tmp2;
					${$strMenu{$menu}[$j]{$key}} = $tmp;
				} else {
					$tmp = $strMenu{$menu}[$i]{$key};
					$strMenu{$menu}[$i]{$key} = $strMenu{$menu}[$j]{$key};
					$strMenu{$menu}[$j]{$key} = $tmp;
				}
			}
			return 1;
		}
		if ($d eq "Up") { $j = $i; }
	}
	return 0;
}

# ----------------------------------------------------------------------------
# return the true index from the user one (for comLoop func) 

sub trueIndex {
	my ($menu,$item) = @_;
	my ($done,$index,$i) = ("0","0","0");

	while(!$done && defined $strMenu{$menu}[$i]{type}) {
		if ($strMenu{$menu}[$i]{type} eq "empty") { 
			$i++;
			next;
		}
		$index++;
		if ($item == $index) {
			$done = 1;
		} else {
			$i++;
		}
	}
	# Oops not found ... bad hack
	$i = $menuInfo{$menu}{itemsnbr} if (! defined $strMenu{$menu}[$i]{type});

	return $i;
}

# ----------------------------------------------------------------------------
#
# selMenu function (comLoop)
#
# ----------------------------------------------------------------------------

# ----------------------------------------------------------------------------
# add an strMenu item to the selection (comLoop) 

sub addToselMenu {
	my ($menu,$item) = @_;
	my $i;
	my $index=0;

	return 0 if ( !defined $menuInfo{$menu}{itemsnbr});

	for ($i = 0; $i <= $menuInfo{$menu}{itemsnbr}; $i++) {
		next if ($strMenu{$menu}[$i]{type} eq "empty");
		$index++;
	
		if ($index == $item) {
			$lastSel++;
			foreach $key (keys(%{$strMenu{$menu}[$i]})) {
				next if ($key eq "file" || $key eq "linenbr");
				if ($key eq "line") {
					$selMenu[$lastSel]{line} = ${$strMenu{$menu}[$i]{line}};
		  		} else {
					$selMenu[$lastSel]{$key} = $strMenu{$menu}[$i]{$key};
		  		}
	 		}
			return 1;
		}

	}
	return 0;

}

# ----------------------------------------------------------------------------
# remove a selected item (comLoop)
 	
sub removeItemInselMenu {
	my ($item) = @_;
	$item--;
	return 0 if (! defined $selMenu[$item]{type} );
	removeItemInlist($item,$lastSel,\@{@selMenu});
	$lastSel--;
	return $lastSel+2;
}

# ----------------------------------------------------------------------------
# destroy the selection (comLoop)

sub freeselMenu {
	my $return = 0;
	my $i;

	for ($i = $lastSel; $i >= 0; $i--) {
		$return = removeItemInselMenu($i+1);
		return 0 unless ($return);
	}
	return $return;
}

# ----------------------------------------------------------------------------
# insert a selItem into strMenu and filesLines (comLoop)

sub insertOneselItem {
	my ($menu,$item,$selItem) = @_;
	$selItem--;
	
	return 0 if ($selItem < 0);

	if (!defined $menuInfo{$menu}{itemsnbr}) {
		# new menu!
		$menuInfo{$menu}{itemsnbr} = 0;
		$menuInfo{$menu}{side} = "";
		$menuInfo{$menu}{submenus} = "";
		$menuInfo{$menu}{group} = getGroupByMenuName($menu);
		$menuInfo{$menu}{active} = 1;
		$i = -1;
	} else {
		# find the true index: i
		if ($item == 0) { $i = $menuInfo{$menu}{itemsnbr}; }
		else { $i = trueIndex($menu,$item); }	
	}

	# build the new line
	$line = $selMenu[$selItem]{line};
	chomp($line);
	print STDERR "1:".$line . "\n";
	if ($line =~ /^AddToMenu\s+/i) {
		$line =~ s/^AddToMenu\s+//i;
		getNextQuotedToken(\$line);
	} elsif ($line =~ /^\+\s+/) {
		$line =~ s/^\+\s+//;
	}
	if ($i == -1) {
	 	$line = "AddToMenu " . $menu . " " . $line;
	} else {
		$line = "+ " . $line;
	}
	$line .= "\n";

	insertItem($menu,$i,$line,$selMenu[$selItem]);

	return 1;
}

# ----------------------------------------------------------------------------
# insert all selItem into strMenu and filesLines (comLoop)

sub insertAllselItem {
	my ($menu,$item) = @_;
	my $i;

	return 0 if ($lastSel == -1);

	for ($i = $lastSel; $i >= 0; $i--) {
		insertOneselItem($menu,$item,$i+1);
	}
	return 1;
}

#----------------------------------------------------------------------------
#
# Output functions (for the comLoop)
#
#----------------------------------------------------------------------------

#----------------------------------------------------------------------------
# Find all the menus or the menus of "index" index in this list (comLoop)

sub menusList() {
	my ($index) = @_; 
	my $return = "";
	my $menu;
	my $i = 1;

	foreach $menu (keys(%strMenu)) {
		if ($i == $index) { return $menu ."\n"; }
		$i++;		
		$return .= "|" unless ($return eq "");
		$return .= $menu;
	}

	return $return . "\n";

}
#----------------------------------------------------------------------------
# Find the "root" menus (comLoop)

sub rootMenusList() {
	my ($index) = @_;
	my $j = 1;
	my $i = 0;
	my $isRoot = 1;
	my $return = "";

	foreach $menu (keys(%strMenu)) {
		$isRoot = 1;
		foreach $key (keys(%strMenu)) {
	  		for ($i = 0; $i <= $menuInfo{"$key"}{itemsnbr}; $i++) {
				$isRoot = 0 if (eqi($menu,$strMenu{$key}[$i]{useraction}) &&
					($strMenu{"$key"}[$i]{type} eq "Menu" || 
						$strMenu{"$key"}[$i]{type} eq "Popup"));
			}
		}
	   if ($isRoot) {
			if ($j == $index) { return $menu . "\n";}
			$j++;
			$return .= "|" if ( $return ne "");
			$return .= $menu;
		}
	}
	return $return . "\n";
}

#----------------------------------------------------------------------------
# selMenu items list (comLoop)

sub selMenuItemsList {
	my $return = "";
	my $i;

	return $return if ($lastSel < 0);

	for ($i=0; $i <= $lastSel; $i++) {
		$return .= getTypeForItemsList($selMenu[$i]{type}) . $selMenu[$i]{name};
		$return .= "|" unless ($i == $lastSel);
	}

	return $return . "\n";
}
		 
#----------------------------------------------------------------------------
# items list (-1), nbr of items (-2) or info on an item (comLoop)

sub itemsList {
	my ($menu,$itemIndex) = @_;
	my @list =();
	my @itemInfo;
	my $i;
	my $index=0;
	my $return = "";
	
	return 0 if (! defined $menuInfo{$menu}{itemsnbr});

	for ($i = 0; $i <= $menuInfo{$menu}{itemsnbr}; $i++) {
		next if ($strMenu{$menu}[$i]{type} eq "empty");
		$index++;
		if ($index == $itemIndex) {
			$return = $strMenu{$menu}[$i]{name}."\n".
					getPrettyType($strMenu{$menu}[$i]{type})."\n".
					$strMenu{$menu}[$i]{useraction}."\n".
					$strMenu{$menu}[$i]{workingdir}."\n".
					$strMenu{$menu}[$i]{windowtitle}."\n".
					$strMenu{$menu}[$i]{lefticon}."\n";
			return $return;
		}
		push(@list, getTypeForItemsList($strMenu{$menu}[$i]{type}) . 
			$strMenu{$menu}[$i]{name});
	}
	if ($itemIndex == -1 && defined $list[0]) {
		$i=0;
		$return = "";
		while(defined $list[$i]) {
			$return .= "|" if ($i);
			$return .= "$list[$i]";
			$i++;
		}
		$return .= "\n";
	}
	elsif ($itemIndex == -2) {
		$return = $index  ."\n";
	}

	return $return;
}

#----------------------------------------------------------------------------
# out put the menu in a variable (comLoop)

sub outPutfilesLines {
	my $return = "";
	my $i;

	foreach $key (keys(%filesLines)) {
		for ($i=0; $i <= $lastLine{$key}; $i++) {
			$return .= $filesLines{"$key"}[$i]{line};
		}
	}
	return $return;

}


# ----------------------------------------------------------------------------
# some useful function for the comLoop functions ...

sub getPrettyType {
	my ($type) = @_;
	if ($type eq "Nop") { $type = "Separator"; }
	elsif ($type eq "Exec") {$type = "X Application";}
	elsif ($type eq "ExecInTerm") {$type = "Run in a Terminal";}
	elsif ($type eq "TailFile") {$type = "Tail File";}
	elsif ($type eq "FvwmCommand") {$type = "Fvwm Command";}
	elsif ($type eq "Popup") {$type = "Sub Menu";}
	return $type;
}

sub getTypeFromPretty {
	my ($type) = @_;
	if ($type eq "Separator") { $type = "Nop"; }
	elsif ($type eq "X Application") {$type = "Exec";}
	elsif ($type eq "Run in a Terminal") {$type = "ExecInTerm";}
	elsif ($type eq "Tail File") {$type = "TailFile";}
	elsif ($type eq "Fvwm Command") {$type = "FvwmCommand";}
	elsif ($type eq "Sub Menu") {$type = "Popup";}
	return $type;
}

sub getTypeForItemsList {
	my ($type) = @_;
	if ($type eq "Nop")      { $type = "--------------"; }
	elsif ($type eq "Popup") { $type = "Sub Menu: ";}
	elsif ($type eq "Menu")  { $type = "Menu:    ";}
	elsif ($type eq "Title") { $type = "Title:   ";}
	else                     { $type = "Action:  "; }
	return $type;
}

#----------------------------------------------------------------------------
#
# write the menus in the files (comLoop and building)
#
#----------------------------------------------------------------------------

sub writeFiles {

	if (!$site) {
		-d $persThemeDir || mkdir($persThemeDir, 0775) ||
			die "cannot create directory $persThemeDir\n";
	}
	foreach $key (keys(%filesLines)) {
		open(OUT,">$key") || die "cannot write on $key";
		for ($i=0; $i <= $lastLine{$key}; $i++) {
			print OUT $filesLines{"$key"}[$i]{line};
		}
		close(OUT);
	}
	return 1;
}

#----------------------------------------------------------------------------
#
# for debuging:
#
#----------------------------------------------------------------------------

sub debugfilesLines {

	foreach $key (keys(%filesLines)) {
		for ($i=0; $i <= $lastLine{$key}; $i++) {
			print $filesLines{"$key"}[$i]{line};
		}
	}
}

# ----------------------------------------------------------------------------
#
# some useful functions
#
# ----------------------------------------------------------------------------

sub removeItemInlist {
	my ($item, $last, $reflist) = @_;
	
	if ($item == $last) {
		undef ($$reflist[$last]); 
		return 1;
	}
	for ($i=$item; $i < $last; $i++) {
		$$reflist[$i] = $$reflist[$i+1];
	}
	undef ($$reflist[$last]);
	return 1;
} 	
		 
sub checkApp {
	my($app) = @_;
	my $dir ="";
	if ($app =~ /^\//) {
		if (-f $app ) { return 1 } # need a fix!
	} else {
		foreach $dir (@pathDirs) {
			if ( -x "$dir/$app" ) { return 1 }
		}
	}
	return 0;
}

sub myCopyFile {
	my ($in,$out)=@_;
	open(IN,"$in") || die "cannot open $in";
	open(OUT,">$out") || die "cannot create $out";
	while(<IN>) { print OUT $_; }
	close(IN);
	close(OUT);
}

# returns the next quoted token, modifies the parameter (ref to line)
sub getNextQuotedToken {
	my ($line) = @_;

	$$line =~ s/^\s+//;
	my $quote = '\s';
	$quote = substr($$line, 0, 1) if $$line =~ /^["'`]/;

	$$line =~ s/^$quote//;
	if ($$line =~ /^(.*?)$quote\s*(.*)$/) {
		$$line = $2;
		return $1;
	} else {
		my $token = $$line;
		$$line = "";
		return $token;
	}
}

sub eqi {
	my ($a,$b) = @_;
	$a=lc($a);
	$b=lc($b);
	if ("$a" eq "$b") {return 1;}
	else {return 0;}
}

sub nei {
	my ($a,$b) = @_;
	$a=lc($a);
	$b=lc($b);
	if ($a ne $b) {return 1;}
	else {return 0;}
}

sub myMakeFifo {
	my ($fifo) = @_;
	system("mkfifo '$fifo'");  # not portable: mknod '$fifo' p
}

#-----------------------------------------------------------------------------
# 
# The starndard functions
#
# ----------------------------------------------------------------------------

sub showHelp {
	print "The fvwm-themes menus and more utility.\n";
	print "Usage: $scriptName --build-menus or --com-mode or --help or --version [OPTIONS]\n";
	print "Options:\n";
	print "\t--help            show this help and exit\n";
	print "\t--version         show the version and exit\n";
	print "\t--build-menu      build clean menus\n"; 
	print "\t--com-mode        run under communication mode\n";
	print "\t--site            use site files\n";
	print "\t--menu-files f,f2 files to read and wrote\n";
	print "\t--remove-popup    remove certain popup in the building case\n";
	print "\t--com-name name   name for communication\n";
	exit 0;
}

sub showVersion {
	print "$version\n";
	exit 0;
}

sub wrongUsage {
	print STDERR "Try '$scriptName --help' for more information.\n";
	exit -1;
}


__END__


# ----------------------------------------------------------------------------

=head1 NAME

fvwm-themes-menuapp - fvwm-themes menus utility

=head1 SYNOPSIS

B<fvwm-themes-menuapp>
B<--build-menu> or B<--com-mode> orB<--help> or B<--version>
[ B<--site>]
[ B<--menu-files> [file1,file2,...] ]
[ B<--remove-popup>]
[ B<--com-name> name]

=head1 DESCRIPTION

This script is not a user script. It is used by Fvwm-Themes to do differrent
task concerning menu and other related things.
This scripts can do the following:

Parse and rebuild FVWM configuration files to build a clean menu from them:
application not in the path are removed, certain unuseful sub menus and 
separator are removed (--build-menus option).

It can work in a locked way and communicate (via fvwm-themes-com) with
FvwmScript-Menus or any other programs (--com-mode option). See below 
for the list of query that you can ask to fvwm-themes-menuapp via
fvwm-themes-com. 

Plan for the future: use an application data base 
and parse and build (Mini)Icon Styles.

=head1 OPTIONS

You must use (only) one of the following four options:

B<--help>    - show the help and exit

B<--version> - show the version and exit

B<--build-menus> - Cause fvwm-themes-menuapp to build clean menus.

B<--com-mode> Run fvwm-themes-menuapp under the "communication mode".

B<--menu-files> [file1,file2,...] - files which are read and written by 
fvwm-themes-menuapp. By default, fvwm-themes-menuapp will 
read $FT_DATADIR/themes/default/menus-programs with the --site option.
Without the --site option fvwm-themes-menuapp will read menus-programs
and menus-extra  (if found) in the fvwm-themes personal dir:
$FVWM_USERDIR/themes/personal. 
If menus-programs is not found in this directory the site's menus-programs
file is used (the one under $FT_DATADIR). Moreover, if menus-extra is not
found, a virtual such file is created so that the "personal" menus can
be created (MenuFvwmPersonal) in it.

B<--site> - Change the default for the --menu-files option.
 
B<--remove-popup> - Remove the sub menu popup that popup empty menu (only 
useful with --build-menus). In fact only certain sub menu popup are 
removed.

B<--com-name> name - use name as name for communication with fvwm-themes-com.
By default, "appmenu" is used, but you should use  "appmenu-pid" as name
where pid is the pid of the program that 
want to talk to fvwm-themes-menuapp so that fvwm-themes-menuapp can 
exit if this program exit and so that fvwm-themes-menuapp can kill the program
if an internal error happen in fvwm-themes-menuapp. On the other hand,
if you want to talk with fvwm-themes-menuapp in, say, a terminal you must
not give an name as "menuapp-an_integer" as name.

=head1 COMMUNICATION COMMANDS

Start fvwm-themes-menuapp as:

	fvwm-themes-menuapp --com-mode [--com-name=menuapp-pid ...]

Then use fvwm-themes-com as:

	fvwm-themes-com --name menuapp[-pid] [--lock-and-get] --message="Command"

where Command is one of the following. All this commands are "lock and
get" but the exit command. A return value of 0 indicate an error.

B<menus-list> - List of the menus.

B<root-menus-list> - List of the "root" menus.

B<root-menus> i - ith root menu.

B<menu-items> menu_name - List of the items of the menu menu_name.

B<item> menu_name:i - Information on the ith item of the menu menu_name.

B<remove> menu_name:i - Remove the ith item of the menu menu_name.

B<move-up> menu_name:i - Move up the ith item of the menu menu_name.

B<move-down> menu_name:i - Move down the ith item of the menu menu_name.

B<add-or-edit-item> X --it='menu_name:i' --t='type' --n='name' --mi='menu-icon'
	--wd='working dir or window title'
where X is either A for add or E for edit, see the code for details :)

B<selection-items> - List of the items in the selection.

B<add-item-to-selection> menu_name:i - Add the ith item of the menu menu_name 
to the selection.

B<remove-sel-item> i - Remove the ith item of the selection.

B<remove-all-sel-item> - Remove all the selection items.

B<copy-sel-item> menu_name:i:j - Copy the jth item of the selection after the
ith item of the menu menu_name.

B<copy-all-sel> menu_name:i - Copy all the selection after the
ith item of the menu menu_name.

B<try> - Out put the contents of the files (as soon as possible will run
the good fvwm-themes-config command).

B<save> - wrote back the files

B<exit> - Stop fvwm-themes-menuapp.

=head1 USAGE

When fvwm-themes is installed, fvwm-themes-menuapp is run as:

  fvwm-themes-menuapp --site --build-menus --remove-popup

FvwmScript-Menus used (and in fact is based on) fvwm-themes-menuapp. See
this FvwmScript script for examples with the com-mode option.

=head1 AUTHORS

Olivier Chapuis <olivier.chapuis@free.fr>, 5 May 2000.

=head1 COPYING

The script is distributed by the same terms as fvwm itself.
See GNU General Public License for details.

=head1 BUGS

Report bugs to fvwm-themes-devel@lists.sourceforge.net.

=cut

# ----------------------------------------------------------------------------
