#!/usr/bin/perl
## xex - A very simple launcher for X programs
## by Ben Wheeler, 2005. 
## $Id: xex,v 1.3 2005/03/04 15:57:54 jammin Exp $
## This is my first Gtk2 app, born of necessity...

use strict;
use warnings;
use constant TRUE => 1;
use constant FALSE => 0;
use FileHandle;
use Gtk2 '-init';
use Gtk2::SimpleList;

my $history_file = $ENV{HOME} . "/.xex_history";

## Window
my $window = Gtk2::Window->new;
$window->set_title('xex');
$window->set_border_width(1);
## Allow exiting :)
$window->signal_connect(destroy => sub { Gtk2->main_quit; });

## Horizontal container
my $hbox = Gtk2::HBox->new;
$window->add($hbox);

## History list model
my $model = Gtk2::ListStore->new('Glib::String');
if (-e $history_file) {
  my @history = &read_history_file();
  foreach my $h (@history) {
    $model->set($model->append, 0, $h);
  }
} else {
  &create_history_file();
}

## Text field & history list
my $cbent = Gtk2::ComboBoxEntry->new($model,0);
## Enter activates default button
## The Entry field is the child of the ComboBoxEntry
my $entry = $cbent->get_child;
$entry->set_activates_default(TRUE);
$entry->set_width_chars(10);
## TODO: Connect to the child's 'changed' signal, and search the
## history list for an incremental match. This will mean creating a
## separate model each time I think.
$entry->signal_connect(changed => \&retrieve_value, $cbent);

## Button (default widget)
my $button = Gtk2::Button->new('Run');
$button->signal_connect(clicked => \&runprog, $cbent);
$button->can_default(TRUE);
$button->set_focus_on_click(FALSE); ## Leave focus in the entry box
## TODO: Make font (and button) as small as possible
## requires reading up on Pango
# $button->modify_font(...)

$window->set_default($button);

## Pack it up
$hbox->pack_start($cbent, FALSE, FALSE, 0);
$hbox->pack_start($button, FALSE, FALSE, 0);

## Show and loop
$window->show_all;
Gtk2->main;
exit(0);


sub runprog 
{
  my ($what, $cbent) = @_;
  my $commandline = &retrieve_value($what, $cbent);
  ## Better not be empty
  return unless (defined $commandline && $commandline =~ /\S/); 
  ## TODO - sanity checks? Lazy for now, and we may as well trust 
  ## the user to run their own choice of command safely...
  system("$commandline &") and warn "Failed: $!\n";
  ## TODO - report error?
  ## Add command to history file (TODO: only if it started ok)
  &append_history_file($commandline);
  ## Clear entry
  $cbent->get_child->set_text('');
  ## Add to history list
  $cbent->append_text($commandline);
  ## TODO: Save to disk history list
  1;
}

sub retrieve_value
{
  my ($what, $cbent) = @_;
  my $text;
  if (my $preset = $cbent->get_active_iter) {
    $text = $cbent->get_model->get($preset, 0);
  } else {
    $text = $cbent->get_child->get_text();
  }
  return $text;
}

sub create_history_file
{
  ## If it doesn't exist, create it and chmod it before putting
  ## anything in it.
  my $fh = new FileHandle(">$history_file") or warn "Can't create history file: $!\n" and return;
  $fh->close() or warn "Error creating history file: $!\n" and return;
  chmod(0600,$history_file) or die "Can't set permissions on history file: $!\nNot safe to continue, exiting.\n";
  1;
}


sub append_history_file
{
  my $commandline = shift;

  my $fh = new FileHandle(">>$history_file") or warn "Can't append to history file: $!\n";
  $fh->print("$commandline\n");
  $fh->close() or warn "Error appending to history file: $!\n" and return;
}

sub read_history_file
{
  my $fh = new FileHandle("<$history_file") or warn "Can't read history file: $!\n" and return;
  my @history;
  while (<$fh>) {
    chomp;
    push @history, $_;
  }
  $fh->close;
  return @history;
}

