#!/usr/bin/perl -w

#
# This program and all associated files are copyright (C) 1998
# Richard Hestilow and are available for use under the terms of the
# GNU GPL. Details of the GPL are in the file COPYING which should
# be included with the distribution.
#
# This program has NO WARRANTY; see COPYING for details.

 
use Gtk;

$VERSION="0.1.2";

$home = $ENV{'HOME'};
@locations = ("/etc/X11/icewm", "/usr/X11R6/lib/X11/icewm",
"/usr/local/lib/X11/icewm");

$id = 1; # for menu read. could not put in function else overwritten.

%tree_items = ();

$nests = -1; #must be declared globally. 
foreach $place (@locations) { #find system icewm dir 
	if (-r "$place" and -r "$place/menu") {
		$sys_loc = $place;
		last;
	}
}

parse_menu_file();

Gtk->init;
$window = new Gtk::Window -toplevel;
$window->signal_connect("destroy", \&quit_cb, NULL);
$window->set_title("IceWM Menu Editor $VERSION");

$main_vbox = new Gtk::VBox(0, 5);
$vbox = new Gtk::VBox(0, 5); 
$vbox2 = new Gtk::VBox(0, 5);
$hbox = new Gtk::HBox(1, 5);
$menu_tree = recursive_tree_add(@MenuList);
$scrolled_win = new Gtk::ScrolledWindow(undef, undef);
$scrolled_win->set_policy(-automatic, -automatic);
$scrolled_win->add($menu_tree);
$scrolled_win->set_usize(200, 200);
$vbox2->pack_start($scrolled_win, 1, 0, 0);
$button = new Gtk::Button("Add");
$button->signal_connect("clicked", \&menu_add_cb, NULL);
$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Properties");
$button->signal_connect("clicked", \&menu_prop_cb, NULL);
$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Delete");
$button->signal_connect("clicked", \&menu_delete_cb, NULL);
$hbox->pack_start($button, 1, 1, 5);
$vbox2->pack_start($hbox, 1, 0, 0);
$vbox->pack_start($vbox2, 1, 0, 0);
$main_vbox->pack_start($vbox, 1, 0, 5);
$label = new Gtk::Label(
" Note: You will have to restart IceWM for the changes to take effect ");
$main_vbox->pack_start($label, 1, 0, 5);
$separator = new Gtk::HSeparator;
$main_vbox->pack_start($separator, 1, 0, 0);

$hbox = new Gtk::HBox(1, 5);
$button = new Gtk::Button("Ok");
$button->signal_connect("clicked", sub {save_cb(); quit_cb()});
$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Save");
$button->signal_connect("clicked", \&save_cb, NULL);
$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Cancel");
$button->signal_connect("clicked", \&quit_cb, NULL);
$hbox->pack_start($button, 1, 1, 5);
$main_vbox->pack_start($hbox, 1, 0, 5);
$window->add($main_vbox);
$window->show_all;
Gtk->main;

sub quit_cb {
Gtk->main_quit;
}

sub parse_menu_file {

$nofile = "Fatal error: could not load config files. This should not usually happen.\nIf it did, you probably don't have icewm, or have a really messed up\ninstallation. Please contact the author at <hestgray\@ionet.net>.\n";

# WARNING! This code is naive and lazy. It probably should not be run as root.
# I take no responsibility for things it does.


if (!(-d "$home/.icewm")) {
	print ".icewm directory not found. creating...\n";
	system("mkdir $home/.icewm");
}
if (!(-e "$home/.icewm/menu")) {
	print "menu file not found ,creating...\n";
	system("cp $sys_loc/menu $home/.icewm/");
	system("chmod 644 $home/.icewm/menu");
}
 
open(MENU, "$home/.icewm/menu") or die $nofile;

my($temp)= recursive_menu_read();
@MenuList= @$temp;

close MENU;
}

sub destroy_window {
	my($widget, $windowref) = @_;
	destroy $$windowref;
}

sub save_cb {
open (MENU, ">$home/.icewm/menu");
recursive_menu_print(@MenuList);
close MENU;
}

sub recursive_menu_print {
my(@list) = @_;
foreach $item (@list) {
if (defined($item)) {
	if ($$item{"type"} eq "prog") {
		print MENU "prog \"" . $$item{"name"} . "\" " .  $$item{"icon"} . " " . $$item{"comm"} . "\n";
	}
	elsif ($$item{"type"} eq "separator") {
		print MENU "separator\n";
	}
	elsif ($$item{"type"} eq "menu") {
		print MENU "menu \"" . $$item{"name"} . "\" " . $$item{"icon"} .  " " . "\{\n";
		my($foo) = $$item{"list"};
		recursive_menu_print(@$foo);
		print MENU "\}\n";
	}
}
}
}

sub flip {
my($widget, $data) = @_;
$$data = 1 - $$data;
}

sub recursive_menu_read {
my(@tempmenu, $line, $foo);
my($i) = -1;
$nests++;
while ($line = <MENU>) {
	$i++;
	chomp $line;
	if ($line =~ /\s*prog\s+\"(.*?)\"\s+(.*?)\s+(.*)$/) {
		push @tempmenu, {"type" => "prog", "name" => $1, "icon" => $2,
				"comm" => $3, "id" => $id++,
				"num_deep" => $nests};
	}
	elsif ($line =~ /\s*prog\s+(.*?)\s+(.*?)\s+(.*)$/) {
		push @tempmenu, {"type" => "prog", "name" => $1, "icon" => $2,
				"comm" => $3, "id" => $id++,
				"num_deep" => $nests};
	}
	elsif ($line =~ /\s*separator/) {
		push @tempmenu, {"type" => "separator", "id" => $id++,
		"num_deep" => $nests};
	}
	elsif ($line =~ /\s*menu\s+\"(.*?)\"\s+(.*?)\s+\{.*$/) {
		my($name) = $1; #this next thing overwrites it
		my($icon) = $2;
		my($templist)= recursive_menu_read();
		$nests--;
		push @tempmenu, {"type" => "menu", "name" => $name, 
			"list" => $templist, "icon" => $2, "id" => $id++,
			"num_deep" => $nests};
	}
	elsif ($line =~ /\s*menu\s+(.*?)\s+(.*?)\s+\{.*$/) {
		my($name) = $1;
		my($icon) = $2;
		my($templist)= recursive_menu_read();
		$nests--;
		push @tempmenu, {"type" => "menu", "name" => $name,
			"list" => $templist, "icon" => $2, "id" => $id++,
			"num_deep" => $nests};
	}
	elsif ($line =~ /\s*\}/) {
		my($thing, $idee);
		foreach $thing (@tempmenu) {
			$idee = $$thing{"id"};
			$tree_items{$idee} = $thing;
		}
					
		return \@tempmenu;
	}
#	$foo = $tempmenu[$i];
#	$tree_items{$$foo{"id"}} = $foo;
}
my($thing, $idee);
foreach $thing (@tempmenu) {
	$idee = $$thing{"id"};
	$tree_items{$idee} = $thing;
}
return \@tempmenu;
}

sub recursive_tree_add {
my($item, $tree_item, $submenu, @templist, $foo);
@templist = @_;
$submenu = new Gtk::Tree;
foreach $item (@templist) {
	
	$$item{"parent"} = \$submenu;
	if ($$item{"type"} eq "prog") {
		$tree_item = new Gtk::TreeItem($$item{"name"});
		$tree_item->set_user_data($$item{"id"});
		$submenu->append($tree_item);
	}
	if ($$item{"type"} eq "menu") {
		$tree_item = new Gtk::TreeItem($$item{"name"});
		$tree_item->set_user_data($$item{"id"});
		$submenu->append($tree_item);
		$foo= $$item{"list"};
		$tree_item->set_subtree(recursive_tree_add(@$foo));
	}
	if ($$item{"type"} eq "separator") {
		$tree_item = new Gtk::TreeItem("separator");
		$tree_item->set_user_data($$item{"id"});
		$submenu->append($tree_item);
	}
}
return $submenu;
}

sub menu_add_cb {
$menu_win = new Gtk::Window -toplevel;
$menu_win->signal_connect("delete_event", \&destroy_window, \$menu_win);
$vbox = new Gtk::VBox(0, 5);
$hbox = new Gtk::HBox(1, 5);
$label = new Gtk::Label("Type: ");
$option_menu = new Gtk::OptionMenu;
$menu = new Gtk::Menu;

$name_entry = new Gtk::Entry; 			# put these here for ref;
$name_label = new Gtk::Label("Name: ");
$com_label = new Gtk::Label("Command: ");
$com_entry = new Gtk::Entry;
$icon_label = new Gtk::Label("Icon: ");
$icon_button = new Gtk::Button();
foreach $name ("Program", "Folder", "Separator") {
	$menu_item = new Gtk::MenuItem($name);
	$menu_item->signal_connect("activate", \&add_option_cb, {"type" => $name, "name_label" => \$name_label, "name_entry" => \$name_entry, "com_label" => \$com_label, "com_entry" => \$com_entry, "icon_label" => \$icon_label, "icon_button" => \$icon_button, "option_menu" => \$option_menu});
	$menu->append($menu_item);
	$menu_item->show();
}

$option_menu->set_user_data("Program");
$option_menu->set_menu($menu);
$option_menu->set_history(0);
$hbox->pack_start($label, 1, 0, 0);
$hbox->pack_start($option_menu, 1, 0, 0);
$vbox->pack_start($hbox, 1, 0, 5);
$hbox = new Gtk::HBox(1, 5);
$hbox->pack_start($name_label, 1, 0, 5);
$hbox->pack_start($name_entry, 1, 0, 5);
$vbox->pack_start($hbox, 1, 0, 5);
$hbox = new Gtk::HBox(1, 5);
$hbox->pack_start($icon_label, 1, 0, 5);
draw_icon_button(\$icon_button, \$window, "app");
$icon_button->signal_connect("clicked", \&create_icon_sel, undef);
$hbox->pack_start($icon_button, 1, 0, 5);
$vbox->pack_start($hbox, 1, 0, 5);
$hbox = new Gtk::HBox(1, 5);
$hbox->pack_start($com_label, 1, 0, 0);
$hbox->pack_start($com_entry, 1, 0, 0);
$vbox->pack_start($hbox, 1, 0, 0);
$sep = new Gtk::HSeparator;
$vbox->pack_start($sep, 1, 0, 0);
$hbox = new Gtk::HBox(1, 5);
$button = new Gtk::Button("Ok");
$button->signal_connect("clicked", \&add_ok_cb, {"option_menu" => \$option_menu,"name_entry" => \$name_entry, "icon_button" => \$icon_button, "com_entry" => \$com_entry, "window" => \$menu_win});
$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Cancel");
$button->signal_connect("clicked", \&destroy_window, \$menu_win);
$hbox->pack_start($button, 1, 1, 5);
$vbox->pack_start($hbox, 1, 0, 5);
$menu_win->add($vbox);
$menu_win->show_all();
}

sub add_ok_cb {
my($widget, $data) = @_;
my($widref);
my($type, $name, $icon, $command) = (0, 0, 0, 0);
$widref = $$data{"option_menu"};
$type = $$widref->get_user_data();
if (($type eq "Program") or ($type eq "Folder")) {
	$widref = $$data{"name_entry"};
	$name = $$widref->get_text();
	$widref = $$data{"icon_button"};
	$icon = $$widref->get_user_data();
	if (not defined $icon) {$icon = "app"};
}
if ($type eq "Program") {
	$widref = $$data{"com_entry"};
	$command = $$widref->get_text();
}
$widref = $$data{"window"};
destroy $$widref;
add_new_item($type, $name, $icon, $command);
}

sub add_new_item {
my($long_type, $name, $icon, $command) = @_;
my($item);
my($sel_id);
my(@list);
my($num) = 0;
my($tree);
my($anon);
my($type);
my($deep);
my($new_tree);
my($i);
my($temp_num);
my($ids);
my($thing);
$item = $menu_tree->selection;
$sel_id = $item->get_user_data();
$item = $tree_items{$sel_id};
$deep = $$item{"num_deep"};
$parent = $$item{"parent"};
$parent = $$parent;

print "$icon\n";
if ($$item{"type"} eq "menu") {
	$tree = $item->subtree;
	$deep++;
}
else {
	$tree = $parent;
}

if ($long_type eq "Program") { $type = "prog";}
elsif ($long_type eq "Separator") { $type = "separator";}
elsif ($long_type eq "Folder") { $type = "menu";}

$anon = {"type" => $type, "name" => $name, "icon" => $icon, "comm" => $command,
	"num_deep" => $deep, "id" => $id++, "parent" => \$tree};
	
if ($type eq "menu") {
	$$anon{"list"} = \@list;
}

foreach $item ($tree->children) {
	if ($item->get_user_data() eq $sel_id) {
		last;
	}
	else { $num++ }
}


$item = new Gtk::TreeItem "$name";
$item->set_user_data($id);
if ($type eq "menu") {
	$new_tree = new Gtk::Tree;
	$item->set_subtree($new_tree);
}

$item->show();
$tree->append($item);

@list = @MenuList;

recursive_splice_thing($num, 0, $anon, \@list, $sel_id);

}

sub recursive_splice_thing {
my($num, $num_called, $anon, $listref, $sel_id) = @_;
my($item, $i, @templist);
@list = @$listref;	

# have to do this ugly slow way, because gtk-perl don't have tree_owner

for($i=0;$i<scalar(@list);$i++) {
	$item = $list[$i];
	if ($$item{"type"} eq "menu") {
		$thing = $$item{"list"};
		
		if (recursive_splice_thing($num, $num_called+1,
						$anon, \@$thing, $sel_id)) {
			return 1;
		}
	}
	if ($$item{"id"} == $sel_id) { 
		splice(@$listref, $num, 0, $anon);
		return 1;
	}
}

return 0;
}

sub add_option_cb {
my($widget, $data) = @_;
my($type) = $$data{"type"};
delete $$data{"type"};
my($widref, $key);
$widref = $$data{"option_menu"};
$$widref->set_user_data($type);
if (($type eq "Separator") or ($type eq "Folder")) {
	$widref = $$data{"com_label"};
	$$widref->set_sensitive(0);
	$widref = $$data{"com_entry"};
	$$widref->set_sensitive(0);
	$widref = $$data{"icon_label"};
	$$widref->set_sensitive(1);
	$widref = $$data{"icon_button"};
	$$widref->set_sensitive(1);
}
if ($type eq "Separator") {
	$widref = $$data{"icon_label"};
	$$widref->set_sensitive(0);
	$widref = $$data{"icon_button"};
	$$widref->set_sensitive(0);
}

if ($type eq "Program") {
	foreach $key (sort keys %$data) {
		$widref = $$data{$key};
		$$widref->set_sensitive(1);
	}
}
}
sub menu_delete_cb {
my($widget, $data) = @_;

$selection = $menu_tree->selection;
if (defined($selection)) {
	$id = $selection->get_user_data();
	my($foo) = $tree_items{$id};
	undef %$foo; # will leave "hole" in list. need to ignore.
	$menu_tree->remove_items($selection);
}
}

sub menu_prop_cb {
my($widget, $data) = @_;

$selection = $menu_tree->selection;
$id = $selection->get_user_data();
my($foo) = $tree_items{$id};
$menu_win = new Gtk::Window -toplevel;
my(%info) = ();
$info{"win"} = \$menu_win;
$menu_win->signal_connect("delete_event", sub {destroy $menu_win});

$info{"id"} = $id;

$vbox = new Gtk::VBox(0, 5);
$hbox = new Gtk::HBox(1, 5);
$label = new Gtk::Label("Type: ");
if ($$foo{"type"} eq "prog") {
	$label2 = new Gtk::Label("Program");
	$info{"type"} = "prog";
}
elsif ($$foo{"type"} eq "menu") {
	$label2 = new Gtk::Label("Menu");
	$info{"type"} = "menu";
}
elsif ($$foo{"type"} eq "separator") {
	$label2 = new Gtk::Label("Separator");
	$info{"type"} = "separator";
}
$hbox->pack_start($label, 1, 0, 0);
$hbox->pack_start($label2, 1, 0, 0);
$vbox->pack_start($hbox, 1, 0, 0);

if (($$foo{"type"} eq "prog") or ($$foo{"type"} eq "menu")) {
	$hbox = new Gtk::HBox(1, 5);
	$label = new Gtk::Label("Name: ");
	$entry = new Gtk::Entry;
	my($name) = $$foo{"name"};
	$entry->set_text($name);
	$hbox->pack_start($label, 1, 0, 5);
	$hbox->pack_start($entry, 1, 0, 5);
	$vbox->pack_start($hbox, 1, 0, 5);
	$info{"name_en"} = \$entry;
	$info{"icon"} = $$foo{"icon"};
	$hbox = new Gtk::HBox(1, 5);
	$label = new Gtk::Label("Icon: ");
	my($icon) = $$foo{"icon"};
	
	$hbox->pack_start($label, 1, 0, 5);
	$button = new Gtk::Button;
	draw_icon_button(\$button, \$window, $icon);
	$button->signal_connect("clicked", \&create_icon_sel, \$foo);
	$hbox->pack_start($button, 1, 0, 5);
	$vbox->pack_start($hbox, 1, 0, 5);
}
if ($$foo{"type"} eq "prog") {
	$hbox = new Gtk::HBox(1, 5);
	$label = new Gtk::Label("Command: ");
	$entry2 = new Gtk::Entry;
	my($comm) = $$foo{"comm"};
	$entry2->set_text($comm);
	$hbox->pack_start($label, 1, 0, 0);
	$hbox->pack_start($entry2, 1, 0, 0);
	$vbox->pack_start($hbox, 1, 0, 0);
	$info{"comm_en"} = \$entry2;
}

$sep = new Gtk::HSeparator;
$vbox->pack_start($sep, 1, 0, 0);
$hbox = new Gtk::HBox(1, 5);
$button = new Gtk::Button("Ok");
$button->signal_connect("clicked", \&prop_win_ok, \%info);
$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Cancel");
$button->signal_connect("clicked", sub {destroy $menu_win});
$hbox->pack_start($button, 1, 1, 5);
$vbox->pack_start($hbox, 1, 0, 5);
$menu_win->add($vbox);
$menu_win->show_all();
}

sub draw_icon_button {

	my($butref, $winref, $icon) = @_;
	my($button) = $$butref;
	my($window) = $$winref;
	my($style) = Gtk::Widget->get_default_style;
	
	my($iconfile);
	if (-r "$sys_loc/icons/$icon\_16x16.xpm") {
		$iconfile = "$sys_loc/icons/$icon\_16x16.xpm";
	}
	elsif (-r "$home/.icewm/icons/$icon\_16x16.xpm") {
		$iconfile = "$home/.icewm/icons/$icon\_16x16.xpm";
	}
	my($gdk_pixmap, $gdk_pixmap_mask) = Gtk::Gdk::Pixmap->create_from_xpm($window->window, $style->bg("normal"), $iconfile);
	my($pixmap) = new Gtk::Pixmap($gdk_pixmap, $gdk_pixmap_mask);

if (defined($button->children)) {
	$button->remove($button->children);
}
	$button->add($pixmap);
	show $pixmap;
}

sub create_icon_sel {
my($widget, $data) = @_;
my($foo);
if (defined($data)) {
$foo = $$data;
}

my(@icons);
my($win) = new Gtk::Window -toplevel;
$win->signal_connect("delete_event", \&destroy_window, \$win);
$win->signal_connect("destroy", \&destroy_window, \$win);

my($vbox) = new Gtk::VBox(0, 5);
$win->add($vbox);

my($scrolled_win) = new Gtk::ScrolledWindow(undef, undef);

$scrolled_win->set_policy(-automatic, -automatic);
$vbox->pack_start($scrolled_win, 1, 1, 5);
$list = new Gtk::List;
$list->set_selection_mode(-single);
$scrolled_win->add($list);

foreach $place ("$sys_loc/icons/", "$home/.icewm/icons/") {
	opendir ICONS, $place;
	foreach $file (readdir(ICONS)) {
		if ($file =~ /(.+)\_16x16\.xpm/) {
			push @icons, $1;
		}	
	}
	closedir ICONS;
}

foreach $icon (@icons) {
	$list_item = new Gtk::ListItem($icon);
	$list->add($list_item);
}

$hbox = new Gtk::HBox(1, 5);
$button = new Gtk::Button("Ok");

if (defined($data)) {
my($sel_id) = $$$data{"id"};
$item = $tree_items{$sel_id}; #this should be my, but strangely it don't appear in
				# anon sub
#my(%item) = %$itemref;
}

$button->signal_connect("clicked", sub { my($icon)=$list->selection->children->get();draw_icon_button(\$widget, \$window, $icon); if (defined($data)) {$$item{"icon"} = $icon;} $widget->set_user_data($icon);destroy $win;});


$hbox->pack_start($button, 1, 1, 5);
$button = new Gtk::Button("Cancel");
$button->signal_connect("clicked", \&destroy_window, \$win);
$hbox->pack_start($button, 1, 1, 5);
$vbox->pack_start($hbox, 1, 1, 5);
$win->show_all();
}

sub prop_win_ok {
my($widget, $data) = @_;
my(%info) = %$data;
my($id) = $info{"id"};
my($foo) = $tree_items{$id};
my($selection) = $menu_tree->selection;

if ($info{"type"} eq "prog") {
	my($entry) = $info{"comm_en"};
	my($comm) = $$entry->get_text();	
	$$foo{"comm"} = $comm;
	$entry = $info{"name_en"};
	my($name) = $$entry->get_text();
	$$foo{"name"} = $name;
	my($child) = $selection->children;
	$child->set($name);
}
elsif ($info{"type"} eq "menu") {
	my($entry) = $info{"name_en"};
	my($name) = $$entry->get_text();
	$$foo{"name"} = $name;
	my($child) = $selection->children;
	$child->set($name);	
}

my($win) = $info{"win"};
destroy $$win;
}

