#!/usr/local/bin/perl

# Eqe, LaTeX equation editor.
#     Ronan Le Hy, 2004 - 2006.

#     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; version 2 of the License only.

#     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


use strict;
use warnings;
use Carp qw/cluck/;

our $VERSION = '1.3.0';

###############################
# external modules
use Gtk2 qw/-init/;
use Gtk2::SimpleMenu;
use Cwd;
use Data::Dumper;

use File::Copy;
use File::Temp qw/tempfile/;
use File::Slurp;
#use IO::All;
use File::Spec;

use Getopt::Long;
use Sys::Hostname;

###############################
# help

sub usage
{
    "eqe: a LaTeX equation editor, version $VERSION
Ronan Le Hy, 2004-2006
usage: eqe <options>
  where options are:
    --help
    --version

    --eqedit=<path to eqedit binary> [eqedit]
    --magnification=<n> [3]

    --text-color=<color> [black]
    --page-color=<transparent> [transparent]
    --font=<font name> [times]
    --wrap-in-displaymath [off]

    --verbose [off]
    --keep-temp [off]
  All options can be abbreviated.
"
}

sub version
{
    $VERSION;
}

###############################
# hash as object
# synopsis:
#  my $h = SafeHash::new;
#  $h->{key} = $value;
#  print $h->key;
#  my $g = $h->with({'other_key' => 'other_value'});
package SafeHash;
use Carp;
sub new { bless(shift || {}, __PACKAGE__) }
sub with
{
    my ($h, $add) = @_;
    return new { %$h, %$add };
}

AUTOLOAD
{
    our $AUTOLOAD;
    my ($name) = $AUTOLOAD =~ /([^:]+)$/;
    croak "$AUTOLOAD: no such function" 
	unless defined $_[0] and defined $_[0]->{$name};
    return $_[0]->{$name};
};

DESTROY
{
};

1;

package main;

###############################
# options handling

my $options = SafeHash::new
{
 help => 0,
 version => 0,
 text_color => 'black',
 page_color => 'transparent',
 font => 'times',
 magnification => 3,
 eqedit => 'eqedit',
 displaymath => 0,
 verbose => 0,
 keep_temp => 0,
 no_redraw => 1,
 latex_template => 'template.tt.tex',
};

GetOptions(
	   help => \$options->{help},
	   version => \$options->{version},
	   verbose => \$options->{verbose},
	   'text-color=s' => \$options->{text_color},
	   'page-color=s' => \$options->{page_color},
	   'font=s' => \$options->{font},
	   'magnification=i' => \$options->{magnification},
	   'eqedit=s' => \$options->{eqedit},
	   'wrap-in-displaymath' => \$options->{displaymath},
	   'keep-temp' => \$options->{keep_temp},
	   'latex-template=s' => \$options->{latex_template}
	  )
    or die usage;

$options->help
    and do { print(usage); exit 0 };

$options->version
    and do { print(version); exit 0 };

sub verbose
{
    $options->verbose;
}

# global that holds all created temporary files
my @temporary_files;

###############################
# main code
main($options);

###############################
# helper functions

# runs the passed function, masking all its output on stdout and stderr
sub no_output
{
    my $fun = shift;

    open(OLDOUT, ">&STDOUT") or warn "Cannot duplicate STDOUT: $!";
    open(OLDERR, ">&STDERR") or warn "Cannot duplicate STDERR: $!";

    close STDERR or warn "Cannot close STDERR: $!";
    close STDOUT or warn "Cannot close STDOUT: $!";

    $fun->();

    # restore stdout and stderr
    open(STDERR, ">&OLDERR") or warn "Can't restore stderr: $!";
    open(STDOUT, ">&OLDOUT") or warn "Can't restore stdout: $!";

    # avoid leaks by closing the independent copies
    close(OLDOUT) or warn "Can't close OLDOUT: $!";
    close(OLDERR) or warn "Can't close OLDERR: $!";
}

# looks for a readable file
sub find_readable
{
    my ($name) = @_;
    my @path = ('/usr/share/eqe/', '/usr/local/share/eqe', "$ENV{HOME}/.eqe", '.');

    for (map {"$_/$name"} @path)
    {
	if (-r)
	{
	    verbose and warn "Found readable file '$_'.\n";
	    return $_;
	}
    }

    verbose and warn "Did not find readable file '$name'.\n";
    return undef;
}

################## temporary files handling ###########################

# returns the name of a fresh temporary file
sub make_temp
{
    my ($id, $suffix) = @_;

    my ($fh, $filename) = 
	$suffix ?
	    tempfile("eqe_temp_${id}_XXXXXX",
		     DIR => File::Spec->tmpdir(),
		     SUFFIX => $suffix) :
   	    tempfile("eqe_temp_${id}_XXXXXX",
		     DIR => File::Spec->tmpdir());

    push @temporary_files, $filename;
    #close $fh or warn "Cannot close file '$filename': $!\n";

    return $filename;
}

sub cleanup
{
    for my $temp (@temporary_files)
    {
	if ($options->keep_temp)
	{
	    verbose and warn "keeping temporary file '$temp'\n";
	    next;
	}
	verbose and warn "deleting temporary file '$temp'\n";
	unless (unlink $temp)
	{
	    verbose and warn "Cannot delete temporary file '$temp': $!\n";
	}
    }
}

$SIG{QUIT} = $SIG{KILL} = $SIG{TERM} = \&cleanup;

END
{
    cleanup;
}

#######################################################################

# wrapper for system() that checks for errors
sub sys
{
    my $command = join ' ', @_;
    verbose and warn "command: '$command'\n";
    my $ret = system @_;
    $ret and warn "command\n\t$command\nfailed with error code $?\n";
    return $ret;
}

# runs an external editor on the LaTeX template
sub edit_template
{
    my ($options) = @_;

    my $verbose = $options->verbose ? '--verbose' : '';
    my $template = quotemeta $options->{latex_template};
    sys "$options->{eqedit} $verbose --latex-template=$template --edit-template &";
}

# sets the image, from a file (typically, a PNG)
sub set_image
{
    my ($gui, $file) = @_;
    $gui->image_area->set_from_file($file);
    $gui->drag->drag_source_set_icon_pixbuf($gui->image_area->get_pixbuf);
    $gui->options->{tempimage} = $file;
}

# generates an image from LaTeX input
sub generate
{
    my ($gui, $latex, $options, $stop) = @_;

    return if $latex =~ /^\s*$/;

    my $text_color = $options->text_color;
    my $page_color = $options->page_color;
    my $magnification = $options->magnification;
    my $font = $options->font;
    my $tempimage = make_temp 'image', '.png'; #$options->tempimage;
    my $eqedit = $options->eqedit;
    my $displaymath = $options->displaymath;
    my $latex_template = $options->latex_template;
    my $verbose = $options->verbose ? '--verbose' : '';
    my $keep_temp = $options->keep_temp ? '--keep-temp' : '';

    cleanup();

    my $log = make_temp 'log', '.log';

    my $command =
	"$eqedit --latex-template=$latex_template \
                 --log=$log \
                 --text-color=$text_color \
                 --page-color=$page_color \
                 --font=$font \
                 --magnification=$magnification \
                 --displaymath=$displaymath \
                 $verbose \
                 $keep_temp \
                 -o $tempimage";
    $command =~ s/\s+/ /g;
    $options->verbose and
	warn "command: '$command'\n";

    my $error_code;
    no_output sub
    {
	open EQ, "| $command"
	    or do
	    {
		#warn "Cannot execute command:\n  $command\n";
		$stop or generate($gui, "cannot execute eqedit",
				     $options->with({
						     %$options,
						     text_color => 'red',
						     page_color => 'white',
						     tempimage => $tempimage,
						    }),
				     1);
	    };

	print EQ $latex;

	close EQ;
	$error_code = $?;
    };

    if ($error_code)
    {
	#warn "eqedit returned with an error";
	if ($stop)
	{
	    #warn "called with stop, not doing anything";
	}
	else
	{
	    fill_logw($gui, $log);
	    $gui->logw->show_all;

	    if (my $error_image = find_readable('error.png'))
	    {
		set_image($gui, $error_image);
	    }
	    else
	    {
		generate($gui,
			 "an error occurred, check your \\LaTeX{}", 
			 $options->with({
					 text_color => 'red',
					 page_color => 'white',
					 tempimage => $tempimage}),
			 1);
	    }
	}
    }
    else
    {
	if ($stop)
	{

	}
	else
	{
	    $gui->log_area->get_buffer()->set_text(scalar read_file $log);
	}

	set_image($gui, $tempimage);
    }
}

# regenerates the image from the LaTeX, and displays it
sub redraw
{
    my ($gui, $options) = @_;
    !$options->no_redraw
	and generate($gui, get_latex_area_contents($gui), $options);
}

# creates and initialises the log window
sub init_log_window
{
    my $gui = shift;

    $gui->{logw} = Gtk2::Window->new('toplevel');
    $gui->{log_scrollable} = Gtk2::ScrolledWindow->new();
    $gui->log_scrollable->set_policy(qw/automatic automatic/);
    $gui->logw->add($gui->log_scrollable);

    $gui->{log_area} = Gtk2::TextView->new();
    $gui->log_scrollable->add($gui->log_area);

    $gui->{log_buf} = $gui->log_area->get_buffer;
    $gui->{log_red_tag} = $gui->log_buf->create_tag("red_foreground",
						    foreground => "red");

    $gui->logw->set_title('error log');
    $gui->logw->resize(500, 700);

    # these return true to stop propagation of the event
    $gui->logw->signal_connect('destroy_event',
			       sub { $gui->logw->hide_all;1 });
    $gui->logw->signal_connect('delete_event',
			       sub { $gui->logw->hide_all;1 });

    $gui->logw->hide_all;
}

# creates the popup menu that appears over the image
sub init_popup_menu
{
    my $gui = shift;

    my $menu_desc =
    [
     _Main =>
     {
      item_type => '<Branch>',
      children =>
      [
       '_Save image as...' =>
       {
	item_type => '<StockItem>',
	extra_data => 'gtk-save-as',
	callback => sub { save_as($gui) }
       },
       '_Regenerate image' => 
       {
	item_type => '<StockItem>',
	extra_data => 'gtk-refresh',
	callback => sub { redraw($gui, $options) }
       },
      ]
     }
    ];

    $gui->{popup_menu} =
	Gtk2::SimpleMenu->
		new(menu_tree => $menu_desc)->
		    get_widget('/Main');
}

# pops up a context menu when you right click on the image
sub popup_image_menu
{
    my $gui = shift;

    return sub
    {
	my (undef, $event) = @_;
	return unless $event->button == 3;
	$gui->popup_menu->popup(undef, undef, undef, undef, 0, 0);
    }
}

# creates the image display area
sub init_image_area
{
    my $gui = shift;

    $gui->{image_scrollable} = Gtk2::ScrolledWindow->new();
    $gui->image_scrollable->set_policy(qw/automatic automatic/);

    $gui->{image_area} = Gtk2::Image->new();
    $gui->image_scrollable->add_with_viewport($gui->image_area);
}

# initialises drag & drop from the image to the outside
sub init_drag_and_drop
{
    my ($gui, $options) = @_;

    my $drag = Gtk2::Button->new;
    $gui->{drag} = $drag;
    $drag->set_relief('none');
    $gui->tooltips->set_tip($drag,
			    'Click me, drag me!',
			    'Click me to generate image, drag me to other apps');

    $drag->add($gui->image_scrollable);

    $drag->drag_source_set('button1_mask', ['copy'],
			   #{target=>'text/plain', flags => 0, info=>1},
			   #{target=>'STRING', flags => 0, info=>1},
			   {
			    target => 'image/png',
			    flags  => [],
			    info   => 0
			   },
			   {
			    target=>'image/x-png', flags => [], info=>0
			   },
			   {
			    target => 'application/octet-stream',
			    flags  => [],
			    info   => 0
			   },
			   {
			    target => 'text/uri-list',
			    flags  => [],
			    info   => 1
			   },
			   {
			    target => 'text/plain',
			    flags  => [],
			    info   => 2
			   },
			   #{target=>'image/bmp', flags => [], info=>666},
			   #{target=>'application/octet-stream', flags => [], info=>2},
			   #{target=>'image/x-png', flags => [], info=>0},
			  );
    $drag->signal_connect('drag_data_get',
			  \&drag_data_get_image,
			  $gui);
    $drag->signal_connect('clicked', sub { redraw($gui, $options) });

    $gui->vpaned->add($drag);

    $gui->drag->signal_connect('button-release-event', popup_image_menu($gui));
}

# creates the menus
sub init_menu
{
    my ($gui, $options) = @_;

    my $file_menu =
    {
     item_type => '<Branch>',
     children =>
     [
      '_Save image as...' =>
      {
       item_type => '<StockItem>',
       extra_data => 'gtk-save-as',
       callback => sub { save_as($gui) }
      },
      Separator => { item_type => '<Separator>'},
      '_Quit' =>
      {
       item_type => '<StockItem>',
       extra_data => 'gtk-quit',
       callback => sub { Gtk2->main_quit }
      },
     ]
    };

    my $view_menu =
    {
     item_type => '<Branch>',
     children =>
     [
      '_Regenerate image' => 
      {
       item_type => '<StockItem>',
       extra_data => 'gtk-refresh',
       callback => sub { redraw($gui, $options) } 
      },
      '_Clear LaTeX input' =>
      {
       item_type => '<StockItem>',
       extra_data => 'gtk-clear',
       callback =>
       sub { $gui->latex_area->get_buffer()->set_text(""); }
      },
      '_Show log' =>
      {
       callback => sub { $gui->logw->show_all; } 
      },
     ]
    };

    my $options_menu =
    {
     item_type => '<Branch>',
     children =>
     [
      '_Text Color' =>
      {
       item_type => '<Branch>',
       children =>
       [
	eqe_radio('text-color',
		  sub { $options->{text_color} = shift; redraw($gui, $options) })
       ]
      },
      '_Background Color' => 
      {
       item_type => '<Branch>',
       children =>
       [
	eqe_radio('text-color',
		  sub {$options->{page_color} = shift; redraw($gui, $options) })
       ]
      },
      '_Font' =>
      {
       item_type => '<Branch>',
       children =>
       [
	eqe_radio('font',
		  sub { $options->{font} = shift; redraw($gui, $options) })
       ]
      },
      '_Size' =>
      {
       item_type => '<Branch>',
       children =>
       [
	magnification_radio
	(sub { $options->{magnification} = shift; redraw($gui, $options) })
       ]
      },
      '_Wrap input in displaymath environment' =>
      {
       item_type => '<CheckItem>',
       callback => sub { $options->{displaymath} = $_[2]->get_active ? 1 : 0 }
      },
      '_Edit LaTeX template...' =>
      {
       item_type => '<StockItem>',
       extra_data => 'gtk-edit',
       callback => sub { edit_template($options) }
      },
     ]
    };

    my $help_menu =
    {
     item_type => '<Branch>',
     children =>
     [
      '_About' =>
      {
       item_type => '<StockItem>',
       extra_data => 'gtk-about',
       callback => sub { about($gui) }
      },
     ]
    };

    my $menu_desc =
	[
	 _File => $file_menu,
	 _View => $view_menu,
	 _Options => $options_menu,
	 _Help => $help_menu,
	];

    $gui->{menu} = Gtk2::SimpleMenu->new(menu_tree => $menu_desc);

    $gui->vbox->pack_start($gui->menu->{widget}, 0, 0, 0);

    my %opts = (font => 'Font',
		text_color => 'Text Color',
		page_color => 'Background Color',
		magnification => 'Size');

    for (keys %opts)
    {
	my $wid = "/Options/$opts{$_}/" . ucfirst $options->{$_};
	$gui->menu->get_widget($wid)->activate;
    }
}

# creates the LaTeX input area
sub init_latex_area
{
    my $gui = shift;

    $gui->{latex_scrollable} = Gtk2::ScrolledWindow->new();
    $gui->latex_scrollable->set_policy(qw/automatic automatic/);
    $gui->latex_scrollable->set_shadow_type ('etched-out');

    $gui->vpaned->add($gui->latex_scrollable);

    $gui->{latex_area} = Gtk2::TextView->new();

    $gui->latex_area->set_left_margin(5);
    $gui->latex_area->set_right_margin(5);

    my $tt = Gtk2::Pango::FontDescription->from_string ("Monospace");
    $gui->latex_area->modify_font($tt);

    my $initial_message = 'Type your \\LaTeX{} code here.

Then, to regenerate the image, you may:
* click it, or
* press Shift+Return or the keypad Enter key.
';
    $gui->latex_area->get_buffer()->set_text($initial_message);

    $gui->latex_scrollable->add($gui->latex_area);

    $gui->latex_area->grab_focus();
}

# returns a tester to see if a keypress event corresponds to a specification
# $key is the name of a key
# @modifiers is a list of modifiers ('shift', 'control', 'alt'), possibly empty
sub key_matcher
{
    my ($key, @modifiers) = @_;
    my $keyval = Gtk2::Gdk->keyval_from_name($key);

    return sub
    {
	my $pressed = shift;
	my $state = $pressed->state;
	for my $mod (@modifiers)
	{
	    return 0 unless $state & "$mod-mask";
	}
	return 0 unless $keyval == $pressed->keyval;
	return 1;
    };
}

# handles global key presses (mostly regenerating the image)
sub global_key_press
{
    my $gui = shift;

    my $regenerate = sub
    {
	$gui->drag->signal_emit('clicked');
    };

    my $regenerate_keys = [['Return', 'shift'], ['KP_Enter']];
    my $matchers = [map {key_matcher(@$_)} @$regenerate_keys];

    my $press = sub
    {
	my (undef, $pressed) = @_;
	for my $m (@$matchers)
	{
	    if ($m->($pressed))
	    {
		$regenerate->();
		return 1;
	    }
	}
	return 0;
    };

    return $press;
}

# main code
sub main
{
    my ($options) = @_;

    # don't trigger redraws during initialisation
    $options->{no_redraw} = 1;

    my $gui = SafeHash::new;

    $gui->{options} = $options;

    $gui->{tooltips} = Gtk2::Tooltips->new;
    $gui->{mw} = Gtk2::Window->new('toplevel');

    init_log_window($gui);

    $gui->{vbox} = Gtk2::VBox->new();

    #$options->{tempimage} = make_temp 'image', '.png';

    # check that eqedit can be run
    my $eqedit = $options->eqedit;
    sys "$eqedit --help >/dev/null 2>&1"
	and die "I could not execute '$eqedit'. Is eqedit in you PATH, and executable?\n";

    $gui->{alignment} = Gtk2::Alignment->new(0.5, 0.5, 1, 1);

    # this vpaned will hold the LaTeX input area (bottom)
    # and the image display area (top)
    $gui->{vpaned} = Gtk2::VPaned->new();
    $gui->alignment->add($gui->vpaned);

    init_image_area($gui);
    init_drag_and_drop($gui, $options);
    init_latex_area($gui);
    init_menu($gui, $options);
    init_popup_menu($gui);

    $gui->vpaned->set_border_width(5);

    # I have no idea what this '1' does
    $gui->latex_area->signal_emit('select-all', 1);

    # this cannot be in init_image_area as it needs to be done
    # after D&D init, and D&D init needs to be done after init_image_area
    if (my $initial_image = find_readable('initial_image.png'))
    {
	set_image($gui, $initial_image);
    }

    $gui->vbox->add($gui->alignment);

    # setup the main window
    $gui->mw->add($gui->vbox);
    $gui->mw->show_all;

    $gui->mw->signal_connect('destroy_event', sub { Gtk2->main_quit });
    $gui->mw->signal_connect('delete_event', sub { Gtk2->main_quit });

    $gui->mw->set_title("LaTeX Equation Editor");
    $gui->mw->resize(500, 400);

    my ($press) = global_key_press($gui);
    $gui->mw->signal_connect('key-press-event', $press);

    # you can start redrawing now
    $options->{no_redraw} = 0;

    Gtk2->main;
}

# returns the LaTeX input from the LaTeX input area
sub get_latex_area_contents
{
    my ($gui) = @_;

    my $buf = $gui->latex_area->get_buffer();
    my $contents = $buf->get_text($buf->get_start_iter,
				  $buf->get_end_iter,
				  1);
    return $contents;
}


my $group = 0;

# returns radio buttons for available colors
sub eqe_radio
{
    my ($option, $callback) = @_;
    my $eqedit = $options->eqedit;

    my ($cols) = `$eqedit --help 2>&1` =~ /--$option=<([^>]+)>/;
    my @colors = split /\s*\|\s*/, $cols;
    ++$group;

    return map {
	my $col = $_;
	ucfirst $col => {
			 item_type => '<RadioItem>',
			 callback => sub { $callback->($col) if $_[2]->get_active},
			 groupid => $group
			}
    } @colors;
}

# returns radio buttons for text sizes
sub magnification_radio
{
    my $callback = shift;
    ++$group;

    return
	map
	{
	    my $mag = $_;
	    $mag => {
		     item_type => '<RadioItem>',
		     callback => sub { $callback->($mag) if $_[2]->get_active},
		     groupid => $group
		    }
	}
	    (1,2,3,4,5,10,12,15,18,20,30,40,50);
}

# called when a drag&drop target asks for data
# http://www.freedesktop.org/wiki/Draganddropwarts
sub drag_data_get_image
{
    my ($widget, $context, $data, $info, $time, $gui) = @_;

    my $options = $gui->options;

    # uri-list
    if ($info == 1)
    {
	verbose and warn "D&D: sending uri-list for file '$options->{tempimage}'";
	# the correct way, that does not work (at least with the Gimp)
# 	my $uri =
# 	    'file://' .
# 		hostname() .
# 		$options->tempimage . "\r\n";

	# the non compliant way (without the hostname), that mostly works locally
	# warning: too many slashes at the beginning fail with Konqueror
	# ($options->tempimage has one slash at the beginning already)
	my $uri =
	    'file://' .
		$options->tempimage . "\r\n";

	$data->set($data->target, 8, $uri);
    }
    # text
    elsif ($info == 2)
    {
	verbose and warn "D&D: sending text from LaTeX input area";
	$data->set($data->target, 8, get_latex_area_contents($options->gui));
    }
    # png
    elsif ($info == 0)
    {
	verbose and warn "D&D: sending png from file '$options->{tempimage}'";
	$data->set($data->target, 8, scalar read_file $options->tempimage);
    }
    else
    {
	warn "Unknown drag info field: $info\n";
    }
}

# runs the About... popup
sub about
{
    my ($gui) = @_;

    my $name = "eqe: LaTeX Equation Editor, version $VERSION";
    my $copyright = "copyright (c) Ronan Le Hy, 2004-2006";
    my $description = "A small LaTeX editor that produces images, with drag and drop support.";
    my $licence = "
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; version 2 of the
License only.

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
";

    my $aboutw = Gtk2::MessageDialog->new
	($gui->mw,
	 'destroy-with-parent', 'info', 'close',
	 "%s\n%s\n_____\n\n%s\n\n%s",
	 $name, $copyright, $description, $licence);

    $aboutw->run;
    $aboutw->destroy;
}

# runs the Save as... dialog
sub save_as
{
    my ($gui) = @_;
    my $save = Gtk2::FileSelection->new
	("Save image as... (supported formats: all those ImageMagick support, ie most)");
    $save->ok_button->signal_connect('clicked', sub { dosave($gui, $save) });
    $save->cancel_button->signal_connect('clicked', sub {});
    $save->run;
    $save->destroy;
}

# does the real work of saving to a file
sub dosave
{
    my ($gui, $save) = @_;
    my $as = $save->get_filename;

    if (-e $as)
    {
	# The file already exists. Ask for confirmation to overwrite.
	my $dialog = Gtk2::MessageDialog->new ($gui->mw,
					       'destroy-with-parent',
					       'question', # message type
					       'yes-no', # which set of buttons?
					       "File '$as' already exists. Do you want to overwrite it?");
	my $response = $dialog->run;
	$dialog->destroy;
	return unless $response eq 'yes';
    }

    no_output sub {
	sys 'convert', $options->tempimage, $as
	    and do
	    {
		my $err = Gtk2::MessageDialog->new
		    ($gui->mw,
		     'modal', 'error', 'close',
		     "Cannot save image to $as.");
		$err->run;
		$err->destroy;
	    };
    };
}

# fills the log window with the current log, and colors errors in red
sub fill_logw
{
    my ($gui, $log) = @_;

    my $l = read_file $log;

    my @colored;
    my $from = 0;
    while (($from = index $l, "\n!", $from) != -1)
    {
	my $to = index $l, "\n", $from+1;
	$to = length($l)-1 if $to == -1;
	push @colored, [$from, $to];

	$from = $to;
    }

    $gui->log_buf->set_text($l);

    for my $r (@colored)
    {
	my $start = $gui->log_buf->get_iter_at_offset($r->[0]);
	my $end = $gui->log_buf->get_iter_at_offset($r->[1]);
	$gui->log_buf->apply_tag($gui->log_red_tag, $start, $end);
    }

    $gui->log_area->scroll_to_iter($gui->log_buf->get_end_iter, 0.2, 1, 0, 1);
}

