#!/usr/bin/perl -w
################################################
# fbconfig.pl
#	--Configuration program fro blackbox, fluxbox, etc.
#
#TODO:
#	--code add/update btns for men editor
#	--save menu file
#	--
#	--code init file editor
#	--
#	--code style file editor
#	--
#	--code status bar
#	--
#	--split this file into smaller files
##################################################

use Gtk2;
#use strict;
use Gtk2::Gdk::Keysyms;
use Data::Dumper;

init Gtk2;

use constant FALSE => 0;
use constant TRUE => 1;
use constant TEST =>1;

### Global Variables ###

my $menufile="~/.fluxbox/menu";
my $initfile="~/.fluxbox/init";
my $stylesdir="~/.fluxbox/styles";
my $stylefile="";
my $context_id;
my $current_doc_isTainted = FALSE;
my $statusText="";
##################

### GUI Elements###

my $mw = new Gtk2::Window( 'toplevel' );
	my $main_box = new Gtk2::VBox( 0, 0 );
		my $menu_box = new Gtk2::HBox( 1, 0 );
			my $menubar = new Gtk2::MenuBar();
		my $toolbar_box = new Gtk2::HBox( 0, 0 );
			my $toolbar = new Gtk2::Toolbar;
		my $notebook_box = new Gtk2::HBox( 0, 0 );
			my $notebook = new Gtk2::Notebook();
				my $menuEdit_box= new Gtk2::HPaned();
					my $menuTree_view = new Gtk2::ScrolledWindow( ); #0,0 );
						my $menuTree = new Gtk2::TreeView();
							my $menu_selection;
							my $menuTreeModel=Gtk2::TreeStore->new(qw/ Glib::String Glib::String Glib::String /);
							my $selected; #current selection
							use constant NAMECOL=>0;
							use constant FXNCOL=>1;
							use constant CMDCOL=>2;
							use constant ICNCOL=>3;
							use constant NUMCOLS=>3;
					my $menuEditor_view=new Gtk2::Table( 10, 3, 1 );
					my $menueditor_frame=new Gtk2::Frame( "Options" );
				my $initEdit_box= new Gtk2::HPaned();
				my $styleEdit_box= new Gtk2::HPaned();
		my $status_box = new Gtk2::HBox( 0, 0 );
			my $status = new Gtk2::Statusbar(); 
		
my $filesel = new Gtk2::FileSelection( "Select File" );


######################################
###         Editing Panes          ###
######################################

######################################
###         Menu Edit              ###


#Title column
my $renderer = Gtk2::CellRendererText->new;
$renderer->set (xalign => 0.0);

my $col_offset = $menuTree->insert_column_with_attributes 
   					(-1, "Title", $renderer,
					 text => NAMECOL);
my $column = $menuTree->get_column ($col_offset - 1);
$column->set_clickable (TRUE);

#Type  column
$renderer = Gtk2::CellRendererText->new;
$renderer->set (xalign => 0.0);

$col_offset = $menuTree->insert_column_with_attributes 
   					(-1, "Type", $renderer,
					 text => FXNCOL);
$column = $menuTree->get_column ($col_offset - 1);
$column->set_clickable (TRUE);

#Command column
$renderer = Gtk2::CellRendererText->new;
$renderer->set (xalign => 0.0);

$col_offset = $menuTree->insert_column_with_attributes 
   					(-1, "Command", $renderer,
					 text => CMDCOL);
$column = $menuTree->get_column ($col_offset - 1);
$column->set_clickable (TRUE);


$menuTree->set_rules_hint (TRUE);
#$menuTree->signal_connect( "select_cursor_row", \&select_row ); 
$menuTree->show;

my $function_entry = new Gtk2::Entry;
my $function_sel = new Gtk2::OptionMenu;
my $title_entry = new Gtk2::Entry();
my $cmd_entry = new Gtk2::Entry();

my $browse_btn = new Gtk2::Button('Browse');
my $add_btn = new Gtk2::Button('Add');
my $update_btn = new Gtk2::Button('Update');

#build popup function menu
my @Fxn_Menu=('Submenu', 
						'Exec', 
						'Include', 
						'Nop', 
						'Style',
						'StylesDir',
						'StylesMenu',
						'Reconfig',
						'Restart',
						'Workspaces');
$function_sel->set_menu(&getMenu(@Fxn_Menu));
$function_sel->signal_connect( "changed",  sub {$function_entry->set_text( $Fxn_Menu[$function_sel->get_history])}); 
$function_sel->show;
$function_entry->show;
$add_btn->signal_connect( "clicked", sub{update_tree("add");});
$update_btn->signal_connect( "clicked", sub{update_tree("update");});

$menuEditor_view->attach( (new Gtk2::Label("Function:")), 0, 1, 1, 2, [], [],  0, 0 );
$menuEditor_view->attach( $function_entry, 1, 3, 1, 2, ['fill','expand','shrink'], [], 0, 0 );
$menuEditor_view->attach( $function_sel, 3, 4, 1, 2, ['fill','expand','shrink'], [], 0, 0 );

$menuEditor_view->attach( (new Gtk2::Label("Menu Title:")), 0, 1, 2, 3, [], [],  0, 0 );
$menuEditor_view->attach_defaults( $title_entry, 1, 4, 2, 3);

$menuEditor_view->attach( (new Gtk2::Label("Command:")), 0, 1, 3, 4, [], [],  0, 0 );
$menuEditor_view->attach( $cmd_entry, 1, 3, 3, 4, ['fill','expand','shrink'], [], 0, 0 );
$menuEditor_view->attach( $browse_btn, 3, 4, 3, 4, [], [], 0, 0 );

$menuEditor_view->attach( $add_btn, 2, 3, 5, 6, [], [], 0, 0 );
$menuEditor_view->attach( $update_btn, 3, 4, 5, 6, [], [], 0, 0 );

$menuEditor_view->show_all();


$menuTree_view->add_with_viewport($menuTree);
$menuTree_view->show_all();

$menueditor_frame->add($menuEditor_view);
$menueditor_frame->set_label_align(.5,0);
$menueditor_frame->set_shadow_type('etched_out');
$menueditor_frame->show;

$menuEdit_box->pack1( $menuTree_view, 1, 1 );
$menuEdit_box->pack2( $menueditor_frame, 1, 1 );

$menuEdit_box->show;

###        Menu Edit               ###
######################################

######################################
###        Init  Edit              ###


$initEdit_box->show();
###        Init Edit               ###
######################################
######################################
###        Style Edit              ###


$styleEdit_box->show();
###        Style Edit              ###
######################################




######################################
###            Widows              ###
######################################

######################################
###         Main Window            ###

#set window props
$mw->set_title( "FBConfig" );
$mw->set_default_size (600, 400);
$mw->signal_connect( "destroy"=> \&exit_app );
$mw->realize();

###############
### menubar ###

my $file_mnu_root = new Gtk2::MenuItem( "File" );
my $file_mnu = new Gtk2::Menu();
	my $open_item = new Gtk2::MenuItem( "Open" );
		$file_mnu->append( $open_item );
		$open_item->signal_connect( 'activate', sub{$filesel->show();} );
		$open_item->show();
	my $save_item = new Gtk2::MenuItem( "Save" );
		$file_mnu->append( $save_item );
		$save_item->signal_connect( 'activate', \&file_save );
		$save_item->show();
	my $quit_item = new Gtk2::MenuItem( "Quit" );
		$file_mnu->append( $quit_item );
		$quit_item->signal_connect( 'activate', \&exit_app );
		$quit_item->show();
$file_mnu_root->set_submenu( $file_mnu );
$file_mnu_root->show();
my $help_mnu_root = new Gtk2::MenuItem( "Help" );
my $help_mnu = new Gtk2::Menu();
	my $help_item = new Gtk2::MenuItem( "Help" );
		$help_mnu->append( $help_item );
		$help_item->signal_connect( 'activate', \&show_help );
		$help_item->show();
	my $about_item = new Gtk2::MenuItem( "About" );
		$help_mnu->append( $about_item );
		$about_item->signal_connect( 'activate', \&show_about );
		$about_item->show();

$help_mnu_root->set_submenu( $help_mnu );
$help_mnu_root->show();

$menubar->append( $file_mnu_root );
$menubar->append( $help_mnu_root );

$menubar->show();

$menu_box->pack_start( $menubar, 1, 1, 0 ); 

$menu_box->show();

### menubar ###
###############

###############
### toolbar ###
	$toolbar->insert_stock ('gtk-open',
                              "Opens File for editing.",
                              undef,
                              sub{$filesel->show();},
                              $mw, # user data for callback
                              -1);  # -1 means "append"

	$toolbar->insert_stock ('gtk-quit',
                              "Quits",
                              undef,
                              \&exit_app,
                              $mw, # user data for callback
                              -1);  # -1 means "append"

	$toolbar->append_space;


#$toolbar->border_width( 0 );
#$toolbar->set_space_size( 0 );

#my $open_button = $toolbar->append_item( "Open",
#				       "Open a file",
#					   "Private",
#				       &getImage("open") );
#	$open_button->signal_connect( "clicked", sub{$filesel->show();} );
#my $close_button = $toolbar->append_item( "Close",
#				       "Close this app",
#				       "Private",
#				       &getImage("exit") );
#	$close_button->signal_connect( "clicked", \&exit_app );

$toolbar->show();
$toolbar_box->pack_start( $toolbar, 1, 1, 0 ); 
$toolbar_box->show();

### toolbar ###
############


##############
### NoteBook ###


$notebook->set_tab_pos( 'top' );
$notebook->append_page($menuEdit_box, (new Gtk2::Label( "Menu" )));
$notebook->append_page($initEdit_box, (new Gtk2::Label( "Init" )));
$notebook->append_page($styleEdit_box, (new Gtk2::Label( "Style" )) );

$notebook->show();
$notebook_box->pack_start( $notebook, 1, 1, 1 ); 
$notebook_box->show();

### NoteBook ###
##############


##############
### Status Bar ###

my @context_id;
showStatus("Welcome to $0");
$status->show();

$status_box->pack_start( $status, 1, 1, 0 ); 
$status_box->show();

### Status Bar ###
##############





$main_box->pack_start($menu_box , 0, 0, 0);
$main_box->pack_start( $toolbar_box , 0, 0, 0);
$main_box->pack_start( $notebook_box , 1, 1, 0);
$main_box->pack_start( $status_box , 0, 0, 0);
$main_box->show;
$mw->add( $main_box );


###          Main Window           ###
######################################





######################################
###     File Selection Dialog      ###

# Connect the ok_button to file_ok_sel function
$filesel->ok_button->signal_connect( "clicked",
					 \&file_ok_sel,
					 $filesel );

# Connect the cancel_button to destroy the widget
$filesel->cancel_button->signal_connect( "clicked",
					     sub { $filesel->destroy; } );

# Set the filename, 
$filesel->set_filename( $menufile );

###    File Selection Dialog       ###
######################################


#$mw->add( $mainlayout );

$mw->show;

Gtk2->main;
#Gtk2->main_quit;
exit 0 ;

######################################
###       Global Routines          ###
######################################

sub file_ok_sel
{
   my ( $widget, $file_selection ) = @_;
   $menufile = $file_selection->get_filename();

   #print( "$menufile\n" );
   &makemodel();
   $file_selection->destroy;
}


#this just manipulates the status ba. Pass in the string to display as a parameter.
sub showStatus{
	my ( $statusText) = @_;
	if ($context_id[0]){
		pop @context_id;
	}
	push (@context_id , $status->get_context_id( "$statusText" ) );

	$status->push( $context_id[-1], $statusText );
}

#
# getImage() is used to display the toolbar images.
# The parameter is the name of the file in pixmaps/, without the .xpm extension.
#
sub getImage {
	my $file = "pixmaps/".$_[0].".xpm";
	
	if (-e $file){
		my ( $icon, $mask ) = Gtk2::Gdk::Pixmap->create_from_xpm( $mw->window,
							$mw->style->white,
							$file );
	
		return ( new Gtk2::Pixmap( $icon, $mask ) );
	} else {
		return undef;
	}
}

#
# Place all closing code here.
# If a doc hasn't been saved, then ask to save it.
sub exit_app {
	if ($current_doc_isTainted) {
		#prompt user to save
	}
	#$mw->destroy;
	Gtk2->main_quit;
	exit 0;
}


# this function actually creates the menu tree. 
sub makemodel {
	my @nodes;
	
	open (MENUFILE, $menufile) || return;
	
	foreach $thistr (<MENUFILE>){
		#chomp;
		#my $thistr=$_;
				
		if ($thistr=~m/^\s*#/){
			#do nothing
		} elsif ($thistr=~m/\[(\s*)begin(\s*)\]/){				#begins menu
			$thistr=~m/\(.*\)/;									#matches name of menu
			$thistr=$&;											#thistr= name of menu
			$thistr=~s/\(//;$thistr=~s/\)//;					#delete parens
			push(@nodes, $menuTreeModel->append(undef) );		#add to tree as node
			$menuTreeModel->set($nodes[-1],NAMECOL,"$thistr" );	#fill in vals in row
		} elsif ($thistr=~m/\[(\s)*submenu(\s)*\]/){
			$thistr=~m/\(.*\)/;
			$thistr=$&;
			$thistr=~s/\(//;$thistr=~s/\)//;
			push(@nodes, $menuTreeModel->append( $nodes[-1] ) ) ;
			$menuTreeModel->set($nodes[-1],NAMECOL,"$thistr" );
		} elsif ($thistr=~m/\[(\s)*end(\s)*\]/){
			pop (@nodes);
		} else {
			my $temp=$thistr;
			$temp=~m/\[.*\]/;
			my $fxn=$&;$fxn=~s/\[//;$fxn=~s/\]//;
			
			$temp=$thistr;$temp=~m/\(.*\)/;
			my $title=$&;$title=~s/\(//;$title=~s/\)//;
			
			$temp=$thistr;$temp=~m/\{.*\}/;
			my $cmd=$&;
			$cmd=~s/\{//;$cmd=~s/\}//;
			#showStatus( "Number of Iters in \@parentIters is $nodes[-1] \n");
			if($title){
				my $childIter=$menuTreeModel-> append($nodes[-1]);
				$menuTreeModel-> set( $childIter, NAMECOL,"$title", FXNCOL,"$fxn",CMDCOL,"$cmd" );
			}
		}
	}
	close (MENUFILE);
	$menuTree->set_model($menuTreeModel);
	$menuTree->set_expander_column (NAMECOL);
	$menu_selection = $menuTree->get_selection;
	$menu_selection->set_mode ('browse');
	$menu_selection->signal_connect (changed => \&select_row, $menuTreeModel);
	#$menuTree->signal_connect (row_activated => \&row_activated, $menuTreeModel);
}

#this is just here so select_row can work. don't ask me why, but it needs to be here.
sub row_activated {

}
#
# Callback when a row is selected
#
sub select_row   {
	my ($selection, $model) = @_;
	$selected = $selection->get_selected;
	showStatus("Could not get selection.\n") if !($selected);
	return unless $selected;
	
	my $title = $model->get ($selected, NAMECOL);
	my $fxn = $model->get ($selected, FXNCOL);
	my $cmd = $model->get ($selected, CMDCOL);
	#my $icn = $model->get ($selected, ICNCOL);
	
	$title_entry->set_text( $title ) if $title; 
	$cmd_entry->set_text( $cmd ) if $cmd;$cmd_entry->set_text("") if !($cmd);
	$function_entry->set_text( $fxn ) if $fxn;$function_entry->set_text("") if !($fxn);
	$fxn="Submenu" if !($fxn);
	$function_sel->set_history( &which_index($fxn,@Fxn_Menu) );
}

#which_index takes a string and an array and returns the index of that string in the array
#	For my purposes, it is only used to change the value of the option menu for the
#		menu editor when a row is selected.
sub which_index{
	local ($str,@ompare)=@_;
	local ($cnt,$ret)=0;
	
	foreach $i (@ompare){ 
		$ret=$cnt if ( lc($str) eq lc($i) );
		$cnt++;
	}
	return $ret;
}




#getMenu() returns a menu widget based on an array. Used for the menueditor->popupmenu
sub getMenu {
	my @list = @_;
	my $menu =  Gtk2::Menu->new;
	my $menuitem;
	#$menu->set_title($list[0]);
	my $i;
	foreach $i (@list){	
		$menuitem = Gtk2::MenuItem->new_with_label ($i);
		$menu->append($menuitem);
		$menuitem->show;		
	}
	return $menu;

}

#updates item to menutree
#to update:
#	$menuTreeModel->set_value($iter, $column,$value)
#	$menuTreeModel->set($iter, COL1, $value1, COL2, $value2, ... ,-1)
#to add:
#	$iter=$menu_selection->get_selection()
#	$menuTreeModel->insert_after($iter)
sub update_tree {
	my ($type)=@_;
	local $iter;
	$menu_selection = $menuTree->get_selection;
	$iter = $menu_selection->get_selected;
	$path=$menuTreeModel->get_path($iter)->to_string;
	if ($type eq 'add'){
		$iter=$menuTreeModel->insert_after('',$iter);
		$path=$menuTreeModel->get_path($iter)->to_string;
		showStatus("Added to $path.");
	}
	
	if ($type eq 'update'){
		showStatus("Updated $path.");
	}
		$menuTreeModel-> set( $iter, 
								NAMECOL,$title_entry->get_text, 
								CMDCOL,$cmd_entry->get_text,
								FXNCOL,lc($function_entry->get_text) );
}


#
#this function saves the file
#
sub file_save {
	my $out="#this file generated by fbConfig by Skaman Sam . Copyleft 2003.\n";
	my @spaceholder;
	#$menuTreeModel->foreach(\&printrow,\$out);
	
	#my $iter=$menuTreeModel->get_iter_first;
	$path=new_first Gtk2::TreePath;
	get_child($menuTreeModel,$path,\$out);
	warn ($out);
	
}


sub get_child{

	local ($model,$path,$out)=@_;
	
	local $iter=$model->get_iter($path);
	
	#warn "iter has ".$model->iter_n_children($iter)." children.\n";
	for (my $cnt=0; $cnt<$model->iter_n_children($iter);$cnt++){
		#local $parent=$model->get_iter($path);
		#$model->iter_parent($parent);
		
		$iter=$model->iter_nth_child($model->iter_parent($parent),$cnt);
		
		local $name=$model->get ($iter, NAMECOL) if ($model->get ($iter, NAMECOL));
		local $fxn=$model->get ($iter, FXNCOL) if ($model->get ($iter, FXNCOL));
		local $cmd=$model->get ($iter, CMDCOL) if ($model->get ($iter, CMDCOL));

		#$$out.=join(@spaceholder,"  ");
		$$out.="[".$fxn."] " if $fxn;
		$$out.="(".$name.") " if $name;
		$$out.="{".$cmd."}" if $cmd;
		$$out.="\n";
		warn $$out;
		
		#$model->iter_next($iter);
		
		if ($model->iter_has_child($iter)){
			$path->down;
			get_child($model,$path,$out);
		} else {
		}
	}
		$$out.="[end]\n";

}


#
#this function is used to traverse a Gtk2::TreeModel and output all data in the tree to a variable
#printrow() takes a few variables
#	$treeStore   => the Gtk2::Treestore you want to read from (passed in auto. by foreach())
#	$TreePath   => the Gtk2::TreePath you want to read from (passed in auto. by foreach())
#	$CurIter      => the Gtk2::TreeModel->get_iter you want to read from (passed in auto. by foreach())
#	\$in                 => the data you want to add to
#
sub printrow{
	local ($model,$treePath,$iter,$out) = @_;
	
	#if no iter, then return and end traversal
	return TRUE if (!$iter);
	
	#prints the path of the current iter
	#$$out.=$treePath->to_string if TEST;
	
	#adds the submenu and begin parts to the output
	if($model->iter_has_child($iter)){
		if ($treePath->to_string eq '0'){
			$$out.="[begin] " ;
		}  else {
			$$out.="[submenu] ";
		}
		push(@spaceholder,"  ");
	}
	
	
	local $name=$model->get ($iter, NAMECOL) if ($model->get ($iter, NAMECOL));
	local $fxn=$model->get ($iter, FXNCOL) if ($model->get ($iter, FXNCOL));
	local $cmd=$model->get ($iter, CMDCOL) if ($model->get ($iter, CMDCOL));

	$$out.=join(@spaceholder,"  ");
	$$out.="[".$fxn."] " if $fxn;
	$$out.="(".$name.") " if $name;
	$$out.="{".$cmd."}" if $cmd;
	$$out.="\n";
	
	#adds the '[end]' to the end of the submenu
	#local $tmpPath= new Gtk2::TreePath;
	$tmpIter=$model->get_iter_first;
	$tmpIter=$iter;
	
	if (! $model->iter_next($tmpIter)){
		if ($treePath->to_string eq '0'){
		
		}  else {
			$$out.=join(@spaceholder,"  ");
			$$out.="[end]\n";
			pop(@spaceholder);
			return FALSE;
		}
	}
	$model->iter_prev($iter);
	
	#$in.=$out;
	#warn $out;
	return FALSE;
	
}
