/* xsane -- a graphical (X11, gtk) scanner-oriented SANE frontend

   xsane-multipage-project.c

   Oliver Rauch <Oliver.Rauch@rauch-domain.de>
   Copyright (C) 2005 Oliver Rauch
   This file is part of the XSANE package.

   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., 675 Mass Ave, Cambridge, MA 02139, USA.  */ 

/* ---------------------------------------------------------------------------------------------------------------------- */


#include "stdio.h"
#include "xsane.h"
#include "xsane-back-gtk.h"
#include "xsane-front-gtk.h"
#include "xsane-preview.h"
#include "xsane-save.h"
#include "xsane-gamma.h"
#include "xsane-setup.h"
#include "xsane-scan.h"
#include "xsane-rc-io.h"
#include "xsane-device-preferences.h"
#include "xsane-preferences.h"
#include "xsane-icons.h"
#include "xsane-batch-scan.h"

#ifdef HAVE_LIBTIFF
# include "tiffio.h"
#endif

#include <sys/wait.h>

/* ---------------------------------------------------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------------------------------------------------- */

/* forward declarations: */


void xsane_multipage_dialog(void);
void xsane_multipage_dialog_close(void);
void xsane_multipage_project_save(void);
static gint xsane_multipage_dialog_delete();
static void xsane_multipage_filetype_callback(GtkWidget *filetype_option_menu, char *filetype);
static void xsane_multipage_project_changed_callback(GtkWidget *widget, gpointer data);
static void xsane_multipage_project_load(void);
static void xsane_multipage_project_delete(void);
static void xsane_multipage_project_create(void);
static void xsane_multipage_entry_move_up_callback(GtkWidget *widget, gpointer list);
static void xsane_multipage_entry_move_down_callback(GtkWidget *widget, gpointer list);
static void xsane_multipage_entry_delete_callback(GtkWidget *widget, gpointer list);
static void xsane_multipage_show_callback(GtkWidget *widget, gpointer data);
static void xsane_multipage_edit_callback(GtkWidget *widget, gpointer data);
static void xsane_multipage_save_file(void);

/* ---------------------------------------------------------------------------------------------------------------------- */

static gint xsane_multipage_dialog_delete()
{
 return TRUE;
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_filetype_callback(GtkWidget *filetype_option_menu, char *filetype)
{
  DBG(DBG_proc, "xsane_multipage_filetype_callback(%s)\n", filetype);

  if (preferences.multipage_filetype)
  {
    free(preferences.multipage_filetype);
  }
  preferences.multipage_filetype = strdup(filetype);
}

/* ---------------------------------------------------------------------------------------------------------------------- */

void xsane_multipage_dialog()
{
 GtkWidget *multipage_dialog, *multipage_scan_vbox, *multipage_project_vbox;
 GtkWidget *multipage_project_exists_hbox, *button;
 GtkWidget *hbox;
 GtkWidget *scrolled_window, *list;
 GtkWidget *pixmapwidget, *text;
 GtkWidget *pages_frame;
 GtkWidget *label;
 GtkWidget *filetype_menu, *filetype_item;
 GtkWidget *filetype_option_menu;
 GdkPixmap *pixmap;
 GdkBitmap *mask;
 char buf[64];
 int filetype_nr;
 int select_item;

  DBG(DBG_proc, "xsane_multipage_dialog\n");

  if (xsane.project_dialog) 
  {
    return; /* window already is open */
  }

  /* GTK_WINDOW_TOPLEVEL looks better but does not place it nice*/
  multipage_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL);

  snprintf(buf, sizeof(buf), "%s %s", xsane.prog_name, WINDOW_MULTIPAGE_PROJECT);
  gtk_window_set_title(GTK_WINDOW(multipage_dialog), buf);
  g_signal_connect(GTK_OBJECT(multipage_dialog), "delete_event", (GtkSignalFunc) xsane_multipage_dialog_delete, NULL);
  xsane_set_window_icon(multipage_dialog, 0);
  gtk_window_add_accel_group(GTK_WINDOW(multipage_dialog), xsane.accelerator_group); 

  /* set the main vbox */
  multipage_scan_vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(multipage_scan_vbox), 0);
  gtk_container_add(GTK_CONTAINER(multipage_dialog), multipage_scan_vbox);
  gtk_widget_show(multipage_scan_vbox);


  /* multipage project */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
  gtk_box_pack_start(GTK_BOX(multipage_scan_vbox), hbox, FALSE, FALSE, 1);

  pixmap = gdk_pixmap_create_from_xpm_d(xsane.dialog->window, &mask, xsane.bg_trans, (gchar **) multipage_xpm);
  pixmapwidget = gtk_image_new_from_pixmap(pixmap, mask);
  gtk_box_pack_start(GTK_BOX(hbox), pixmapwidget, FALSE, FALSE, 2);
  gdk_drawable_unref(pixmap);

  text = gtk_entry_new();
  xsane_back_gtk_set_tooltip(xsane.tooltips, text, DESC_MULTIPAGE_PROJECT);
  gtk_entry_set_max_length(GTK_ENTRY(text), 128);
  gtk_entry_set_text(GTK_ENTRY(text), (char *) preferences.multipage_project);
  gtk_box_pack_start(GTK_BOX(hbox), text, TRUE, TRUE, 4);
  g_signal_connect(GTK_OBJECT(text), "changed", (GtkSignalFunc) xsane_multipage_project_changed_callback, NULL);

  xsane.project_entry = text;
  xsane.project_entry_box = hbox;

  gtk_widget_show(pixmapwidget);
  gtk_widget_show(text);
  gtk_widget_show(hbox);

  multipage_project_vbox = gtk_vbox_new(/* homogeneous */ FALSE, 0);
  gtk_box_pack_start(GTK_BOX(multipage_scan_vbox), multipage_project_vbox, TRUE, TRUE, 0);
  gtk_widget_show(multipage_project_vbox);


  /* FILETYPE MENU */
  /* button box, active when project exists */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
  gtk_box_pack_start(GTK_BOX(multipage_project_vbox), hbox, FALSE, FALSE, 1);
  gtk_widget_show(hbox);

  filetype_menu = gtk_menu_new();

  filetype_nr = -1;
  select_item = 0;


  filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PDF);
  gtk_container_add(GTK_CONTAINER(filetype_menu), filetype_item);
  g_signal_connect(GTK_OBJECT(filetype_item), "activate", (GtkSignalFunc) xsane_multipage_filetype_callback, (void *) XSANE_FILETYPE_PDF);
  gtk_widget_show(filetype_item);
  filetype_nr++;
  if ( (preferences.multipage_filetype) && (!strcasecmp(preferences.multipage_filetype, XSANE_FILETYPE_PDF)) )
  {
    select_item = filetype_nr;
  }


  filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_PS);
  gtk_container_add(GTK_CONTAINER(filetype_menu), filetype_item);
  g_signal_connect(GTK_OBJECT(filetype_item), "activate", (GtkSignalFunc) xsane_multipage_filetype_callback, (void *) XSANE_FILETYPE_PS);
  gtk_widget_show(filetype_item);
  filetype_nr++;
  if ( (preferences.multipage_filetype) && (!strcasecmp(preferences.multipage_filetype, XSANE_FILETYPE_PS)) )
  {
    select_item = filetype_nr;
  }


#ifdef HAVE_LIBTIFF
  filetype_item = gtk_menu_item_new_with_label(MENU_ITEM_FILETYPE_TIFF);
  gtk_container_add(GTK_CONTAINER(filetype_menu), filetype_item);
  g_signal_connect(GTK_OBJECT(filetype_item), "activate", (GtkSignalFunc) xsane_multipage_filetype_callback, (void *) XSANE_FILETYPE_TIFF);
  gtk_widget_show(filetype_item);
  filetype_nr++;
  if ( (preferences.multipage_filetype) && (!strcasecmp(preferences.multipage_filetype, XSANE_FILETYPE_TIFF)) )
  {
    select_item = filetype_nr;
  }
#endif
                                                                                                              
  label = gtk_label_new(TEXT_MULTIPAGE_FILETYPE);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
  gtk_widget_show(label);

  filetype_option_menu = gtk_option_menu_new();
  xsane_back_gtk_set_tooltip(xsane.tooltips, filetype_option_menu, DESC_MULTIPAGE_FILETYPE);
  gtk_option_menu_set_menu(GTK_OPTION_MENU(filetype_option_menu), filetype_menu);
  if (select_item >= 0)
  {
    gtk_option_menu_set_history(GTK_OPTION_MENU(filetype_option_menu), select_item);
  }
  gtk_box_pack_end(GTK_BOX(hbox), filetype_option_menu, FALSE, FALSE, 2);
  gtk_widget_show(filetype_menu);
  gtk_widget_show(filetype_option_menu);


  /* pages frame */
  pages_frame = gtk_frame_new(TEXT_PAGES);
  gtk_box_pack_start(GTK_BOX(multipage_project_vbox), pages_frame, TRUE, TRUE, 2);
  gtk_widget_show(pages_frame);

  /* pages list */
  scrolled_window = gtk_scrolled_window_new(0, 0);
  gtk_widget_set_size_request(scrolled_window, 200, 100);
  gtk_container_add(GTK_CONTAINER(pages_frame), scrolled_window);
  gtk_widget_show(scrolled_window);

  list = gtk_list_new();
/*  gtk_list_set_selection_mode(list, GTK_SELECTION_BROWSE); */

  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrolled_window), list);
  gtk_widget_show(list);
  xsane.project_list = list;


  /* button box, active when project exists */
  hbox = gtk_hbox_new(FALSE, 2);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 2);
  gtk_box_pack_start(GTK_BOX(multipage_project_vbox), hbox, FALSE, FALSE, 1);

  button = gtk_button_new_with_label(BUTTON_IMAGE_SHOW);
  g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_multipage_show_callback, list);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label(BUTTON_IMAGE_EDIT);
  g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_multipage_edit_callback, list);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label(BUTTON_IMAGE_DELETE);
  g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_multipage_entry_delete_callback, list);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  xsane_button_new_with_pixmap(multipage_dialog->window, hbox, move_up_xpm,   0, (GtkSignalFunc) xsane_multipage_entry_move_up_callback,   list);
  xsane_button_new_with_pixmap(multipage_dialog->window, hbox, move_down_xpm, 0, (GtkSignalFunc) xsane_multipage_entry_move_down_callback, list);

  gtk_widget_show(hbox);

  xsane.project_box = multipage_project_vbox;

  /* set the main hbox */
  hbox = gtk_hbox_new(FALSE, 0);
  xsane_separator_new(multipage_scan_vbox, 2);
  gtk_box_pack_end(GTK_BOX(multipage_scan_vbox), hbox, FALSE, FALSE, 5);
  gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
  gtk_widget_show(hbox);     


  multipage_project_exists_hbox = gtk_hbox_new(FALSE, 2);
  gtk_box_pack_start(GTK_BOX(hbox), multipage_project_exists_hbox, TRUE, TRUE, 0);

  button = gtk_button_new_with_label(BUTTON_SAVE_MULTIPAGE);
  g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_multipage_save_file, NULL);
  gtk_box_pack_start(GTK_BOX(multipage_project_exists_hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  button = gtk_button_new_with_label(BUTTON_DELETE_PROJECT);
  g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_multipage_project_delete, NULL);
  gtk_box_pack_start(GTK_BOX(multipage_project_exists_hbox), button, TRUE, TRUE, 0);
  gtk_widget_show(button);

  gtk_widget_show(multipage_project_exists_hbox);
  xsane.project_exists = multipage_project_exists_hbox;

  button = gtk_button_new_with_label(BUTTON_CREATE_PROJECT);
  g_signal_connect(GTK_OBJECT(button), "clicked", (GtkSignalFunc) xsane_multipage_project_create, NULL);
  gtk_box_pack_start(GTK_BOX(hbox), button, TRUE, TRUE, 0);
  xsane.project_not_exists = button;

  /* progress bar */
  xsane.project_progress_bar = (GtkProgressBar *) gtk_progress_bar_new();
  gtk_box_pack_start(GTK_BOX(multipage_scan_vbox), (GtkWidget *) xsane.project_progress_bar, FALSE, FALSE, 0);
  gtk_progress_set_show_text(GTK_PROGRESS(xsane.project_progress_bar), TRUE);
  gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), "");
  gtk_widget_show(GTK_WIDGET(xsane.project_progress_bar));


  xsane.project_dialog = multipage_dialog;

  xsane_multipage_project_load();

  gtk_window_move(GTK_WINDOW(xsane.project_dialog), xsane.project_dialog_posx, xsane.project_dialog_posy);
  gtk_widget_show(multipage_dialog);
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_project_set_sensitive(int sensitive)
{
  gtk_widget_set_sensitive(xsane.project_box, sensitive);
  gtk_widget_set_sensitive(xsane.project_exists, sensitive);
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_project_load()
{
 FILE *projectfile;
 char page[TEXTBUFSIZE];
 char filename[PATH_MAX];
 GtkWidget *list_item;
 int i;
 int c;

  DBG(DBG_proc, "xsane_multipage_project_load\n");

  if (xsane.multipage_status)
  {
    free(xsane.multipage_status);
    xsane.multipage_status = NULL;
  }

  if (xsane.multipage_filename)
  {
    free(xsane.multipage_filename);
    xsane.multipage_filename = NULL;
  }

  gtk_list_remove_items(GTK_LIST(xsane.project_list), GTK_LIST(xsane.project_list)->children);

  snprintf(filename, sizeof(filename), "%s/xsane-multipage-list", preferences.multipage_project);
  projectfile = fopen(filename, "rb"); /* read binary (b for win32) */

  if ((!projectfile) || (feof(projectfile)))
  {
    snprintf(filename, sizeof(filename), "%s/image-1.pnm", preferences.multipage_project);
    xsane.multipage_filename=strdup(filename);
    xsane_update_counter_in_filename(&xsane.multipage_filename, FALSE, 0, preferences.filename_counter_len); /* correct counter len */

    xsane.multipage_status=strdup(TEXT_PROJECT_STATUS_NOT_CREATED);

    gtk_widget_hide(xsane.project_exists);
    gtk_widget_show(xsane.project_not_exists);

    gtk_widget_set_sensitive(xsane.project_box, FALSE);
    gtk_widget_set_sensitive(xsane.project_exists, FALSE);
    /* do not change sensitivity of multipage_project_entry_box here !!! */
    gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), FALSE); 
  }
  else
  {
    i=0;
    c=0;
    while ((i<255) && (c != 10) && (c != EOF)) /* first line is multipage status */
    {
      c = fgetc(projectfile);
      page[i++] = c;
    }
    page[i-1] = 0;
    if (strchr(page, '@'))
    {
      *strchr(page, '@') = 0;
    }

    if (xsane.multipage_status)
    {
      free(xsane.multipage_status);
    }
    xsane.multipage_status = strdup(page);


    i=0;
    c=0;
    while ((i<255) && (c != 10) && (c != EOF)) /* second line is next multipage filename */
    {
      c = fgetc(projectfile);
      page[i++] = c;
    }
    page[i-1] = 0;

    snprintf(filename, sizeof(filename), "%s/%s", preferences.multipage_project, page);
    xsane.multipage_filename=strdup(filename);

    while (!feof(projectfile))
    {
      i=0;
      c=0;
                                                                                                                                
      while ((i<255) && (c != 10) && (c != EOF))
      {
        c = fgetc(projectfile);
        page[i++] = c;
      }
      page[i-1]=0;
                                                                                                                                
      if (c > 1)
      {
       char *type;
       char *extension;
                                                                                                                                
        extension = strrchr(page, '.');
        if (extension)
        {
          type = strdup(extension);
          *extension = 0;
        }
        else
        {
          type = strdup("");
        }
                                                                                                                                
        list_item = gtk_list_item_new_with_label(page);
        gtk_object_set_data(GTK_OBJECT(list_item), "list_item_data", strdup(page));
        gtk_object_set_data(GTK_OBJECT(list_item), "list_item_type", strdup(type));
        gtk_container_add(GTK_CONTAINER(xsane.project_list), list_item);
        gtk_widget_show(list_item);
      }
    }

    if (!strcmp(xsane.multipage_status, TEXT_PROJECT_STATUS_FILE_SAVING)) 
    {
      xsane_multipage_project_set_sensitive(FALSE);
      gtk_widget_set_sensitive(xsane.project_entry_box, TRUE);
      gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), FALSE); 
    }
    else
    {
      xsane_multipage_project_set_sensitive(TRUE);
      gtk_widget_set_sensitive(xsane.project_entry_box, TRUE);
      gtk_widget_set_sensitive(GTK_WIDGET(xsane.start_button), TRUE); 
    }

    gtk_widget_show(xsane.project_exists);
    gtk_widget_hide(xsane.project_not_exists);
  }

  if (projectfile)
  {
    fclose(projectfile);
  }

  gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
  gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_project_delete()
{
 char *page;
 char *type;
 char file[PATH_MAX];
 GList *list = (GList *) GTK_LIST(xsane.project_list)->children;
 GtkObject *list_item;

  DBG(DBG_proc, "xsane_multipage_project_delete\n");

  while (list)
  {
    list_item = GTK_OBJECT(list->data);
    page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
    type = strdup((char *) gtk_object_get_data(list_item, "list_item_type"));
    xsane_convert_text_to_filename(&page);
    snprintf(file, sizeof(file), "%s/%s%s", preferences.multipage_project, page, type);
    free(page);
    free(type);
    remove(file);
    list = list->next;
  }
  snprintf(file, sizeof(file), "%s/xsane-multipage-list", preferences.multipage_project);
  remove(file);
  snprintf(file, sizeof(file), "%s", preferences.multipage_project);
  rmdir(file);

  xsane_multipage_project_load();
}

/* ---------------------------------------------------------------------------------------------------------------------- */

void xsane_multipage_project_save()
{
 FILE *projectfile;
 GList *list = (GList *) GTK_LIST(xsane.project_list)->children;
 GtkObject *list_item;
 char *page;
 char *type;
 char filename[PATH_MAX];

  DBG(DBG_proc, "xsane_multipage_project_save\n");

  umask((mode_t) preferences.directory_umask); /* define new file permissions */    
  mkdir(preferences.multipage_project, 0777); /* make sure directory exists */

  snprintf(filename, sizeof(filename), "%s/xsane-multipage-list", preferences.multipage_project);

  if (xsane_create_secure_file(filename)) /* remove possibly existing symbolic links for security */
  {
   char buf[TEXTBUFSIZE];

    snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, filename);
    xsane_back_gtk_error(buf, TRUE);
   return; /* error */
  }

  projectfile = fopen(filename, "wb"); /* write binary (b for win32) */

  if (xsane.multipage_status)
  {
   char buf[TEXTBUFSIZE];

    snprintf(buf, 32, "%s@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", xsane.multipage_status); /* fill 32 characters status line */
    fprintf(projectfile, "%s\n", buf); /* first line is status of multipage */
    gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
    gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
  }
  else
  {
    fprintf(projectfile, "                                \n"); /* no multipage status */
    gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), "");
    gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
  }

  if (xsane.multipage_filename)
  {
    fprintf(projectfile, "%s\n", strrchr(xsane.multipage_filename, '/')+1); /* third line is next multipage filename */
  }
  else
  {
    fprintf(projectfile, "\n");
  }

  while (list)
  {
    list_item = GTK_OBJECT(list->data);
    page = (char *) gtk_object_get_data(list_item, "list_item_data");
    type = (char *) gtk_object_get_data(list_item, "list_item_type");
    fprintf(projectfile, "%s%s\n", page, type);
    list = list->next;
  }

  fclose(projectfile);
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_project_create()
{
  DBG(DBG_proc, "xsane_multipage_project_create\n");

  if (strlen(preferences.multipage_project))
  {
    if (xsane.multipage_status)
    {
      free(xsane.multipage_status);
    }
    xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_CREATED);
    xsane_multipage_project_save();
    xsane_multipage_project_load();
  }
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_project_changed_callback(GtkWidget *widget, gpointer data)
{
  DBG(DBG_proc, "xsane_multipage_project_changed_callback\n");

  if (preferences.multipage_project)
  {
    free((void *) preferences.multipage_project);
  }
  preferences.multipage_project = strdup(gtk_entry_get_text(GTK_ENTRY(widget)));

  xsane_multipage_project_load();
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_entry_move_up_callback(GtkWidget *widget, gpointer list)
{
 GList *select;
 GList *item = GTK_LIST(list)->children;
 GtkWidget *list_item_1;
 GtkWidget *list_item_2;
 int position;
 int newpos;

  DBG(DBG_proc, "xsane_multipage_entry_move_up\n");

  select = GTK_LIST(list)->selection;
  if (select)
  {
    list_item_1 = select->data;

    position = gtk_list_child_position(GTK_LIST(list), list_item_1);
    position--; /* move up */
    newpos = position;

    if (position >= 0)
    {
      while (position>0)
      {
        item = item->next;
        position--;
      }

      list_item_2 = item->data;
      if (list_item_2)
      {
        xsane_front_gtk_list_entries_swap(list_item_1, list_item_2);
        gtk_list_select_item(GTK_LIST(list), newpos);

        if (xsane.multipage_status)
        {
          free(xsane.multipage_status);
        }
        xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_CHANGED);
        xsane_multipage_project_save();
      }
    }
  }
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_entry_move_down_callback(GtkWidget *widget, gpointer list)
{
 GList *select;
 GList *item = GTK_LIST(list)->children;
 GtkWidget *list_item_1;
 GtkWidget *list_item_2;
 int position;
 int newpos;

  DBG(DBG_proc, "xsane_multipage_entry_move_down\n");

  select = GTK_LIST(list)->selection;
  if (select)
  {
    list_item_1 = select->data;

    position = gtk_list_child_position(GTK_LIST(list), list_item_1);
    position++; /* move down */
    newpos = position;

    while ((position>0) && (item))
    {
      item = item->next;
      position--;
    }

    if (item)
    {
      list_item_2 = item->data;
      if (list_item_2)
      {
        xsane_front_gtk_list_entries_swap(list_item_1, list_item_2);
        gtk_list_select_item(GTK_LIST(list), newpos);

        if (xsane.multipage_status)
        {
          free(xsane.multipage_status);
        }
        xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_CHANGED);
        xsane_multipage_project_save();
      }
    }
  }
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_entry_delete_callback(GtkWidget *widget, gpointer list)
{
 GtkObject *list_item;
 GList *select;
 char *page;
 char *type;
 char file[PATH_MAX];

  DBG(DBG_proc, "xsane_multipage_entry_delete_callback\n");

  select = GTK_LIST(list)->selection;
  if (select)
  {
    list_item = GTK_OBJECT(select->data);
    page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
    type = strdup((char *) gtk_object_get_data(list_item, "list_item_type"));
    xsane_convert_text_to_filename(&page);
    snprintf(file, sizeof(file), "%s/%s%s", preferences.multipage_project, page, type);
    free(page);
    free(type);
    remove(file);
    gtk_widget_destroy(GTK_WIDGET(list_item));

    if (xsane.multipage_status)
    {
      free(xsane.multipage_status);
    }
    xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_CHANGED);
    xsane_multipage_project_save();
  }
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_show_callback(GtkWidget *widget, gpointer list)
{
 GtkObject *list_item;
 GList *select;
 char *page;
 char *type;
 char filename[PATH_MAX];

  DBG(DBG_proc, "xsane_multipage_entry_show_callback\n");

  select = GTK_LIST(list)->selection;
  if (select)
  {
    list_item = GTK_OBJECT(select->data);
    page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
    type = strdup((char *) gtk_object_get_data(list_item, "list_item_type"));
    xsane_convert_text_to_filename(&page);
    snprintf(filename, sizeof(filename), "%s/%s%s", preferences.multipage_project, page, type);
    free(page);
    free(type);

    xsane_viewer_new(filename, NULL, FALSE, filename, VIEWER_NO_MODIFICATION, IMAGE_SAVED);
  }
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_edit_callback(GtkWidget *widget, gpointer list)
{
 GtkObject *list_item;
 GList *select;
 char *page;
 char *type;
 char filename[PATH_MAX];
 char outfilename[PATH_MAX];
 int cancel_save = 0;

  DBG(DBG_proc, "xsane_multipage_entry_show_callback\n");

  select = GTK_LIST(list)->selection;
  if (select)
  {
    list_item = GTK_OBJECT(select->data);
    page = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
    type = strdup((char *) gtk_object_get_data(list_item, "list_item_type"));
    xsane_convert_text_to_filename(&page);
    snprintf(filename, sizeof(filename), "%s/%s%s", preferences.multipage_project, page, type);
    free(page);
    free(type);

    xsane_back_gtk_make_path(sizeof(outfilename), outfilename, 0, 0, "xsane-viewer-", xsane.dev_name, ".pnm", XSANE_PATH_TMP);
    xsane_copy_file_by_name(outfilename, filename, xsane.project_progress_bar, &cancel_save);

    xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_CHANGED);
    xsane_multipage_project_save();

    gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
    gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);

    xsane_viewer_new(outfilename, NULL, FALSE, filename, VIEWER_NO_NAME_MODIFICATION, IMAGE_SAVED);
  }
}

/* ---------------------------------------------------------------------------------------------------------------------- */

static void xsane_multipage_save_file()
{
 char *image;
 char *type;
 GList *list = (GList *) GTK_LIST(xsane.project_list)->children;
 GtkObject *list_item;
 char source_filename[PATH_MAX];
 char multipage_filename[PATH_MAX];
 int output_format;
 int cancel_save = 0;
 int page, pages = 0;
 FILE *outfile = NULL, *imagefile = NULL;
#ifdef HAVE_LIBTIFF
 TIFF *tiffile = NULL;
#endif
 Image_info image_info;
 long int source_size = 0;
 float imagewidth, imageheight;
 char buf[TEXTBUFSIZE];
 struct pdf_xref xref;
 int remove_lineart_file = FALSE;

  DBG(DBG_proc, "xsane_multipage_save_file\n");

  xsane_set_sensitivity(FALSE); /* do not allow changing xsane mode */
  xsane_multipage_project_set_sensitive(FALSE);

  while (gtk_events_pending())
  {
    DBG(DBG_info, "calling gtk_main_iteration\n");
    gtk_main_iteration();
  }

  if (xsane.multipage_status)
  {
    free(xsane.multipage_status);
  }
  xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_FILE_SAVING);

  gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
  gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);


  snprintf(multipage_filename, sizeof(multipage_filename), "%s%s", preferences.multipage_project, preferences.multipage_filetype);
  output_format = xsane_identify_output_format(multipage_filename, NULL, NULL);

  while (list)
  {
    list_item = GTK_OBJECT(list->data);
    image = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
    type  = strdup((char *) gtk_object_get_data(list_item, "list_item_type"));
    xsane_convert_text_to_filename(&image);
    snprintf(source_filename, sizeof(source_filename), "%s/%s%s", preferences.multipage_project, image, type);
    free(image);
    free(type);
    list = list->next;
    pages++;
    source_size += xsane_get_filesize(source_filename);
  }


  if ( (preferences.overwrite_warning) ) /* test if filename already used */
  {
   FILE *testfile;
                                                                                                                             
    testfile = fopen(multipage_filename, "rb"); /* read binary (b for win32) */
    if (testfile) /* filename used: skip */
    {
     char buf[TEXTBUFSIZE];
                                                                                                                             
      fclose(testfile);

      snprintf(buf, sizeof(buf), WARN_FILE_EXISTS, multipage_filename);
      if (xsane_back_gtk_decision(ERR_HEADER_WARNING, (gchar **) warning_xpm, buf, BUTTON_OVERWRITE, BUTTON_CANCEL, TRUE /* wait */) == FALSE)
      {
        xsane_set_sensitivity(TRUE);
        xsane_multipage_project_set_sensitive(TRUE);
        if (xsane.multipage_status)
        {
          free(xsane.multipage_status);
        }
        xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_FILE_SAVING_ABORTED);

        gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
        gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
       return;
      }
    }
  }


  if (xsane_create_secure_file(multipage_filename)) /* remove possibly existing symbolic links for security */
  {
    snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, multipage_filename);
    xsane_back_gtk_error(buf, TRUE);
    xsane_multipage_project_set_sensitive(TRUE);
    xsane_set_sensitivity(TRUE); /* allow changing xsane mode */
   return;
  }

  DBG(DBG_info, "xsane_multipage_save_file: created %s\n", multipage_filename);


  if ((output_format == XSANE_PS) || (output_format == XSANE_PDF))
  {
    outfile = fopen(multipage_filename, "wb"); /* b = binary mode for win32 */
    if (!outfile)
    {
      snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_CREATE_SECURE_FILE, multipage_filename);
      xsane_back_gtk_error(buf, TRUE);
      xsane_multipage_project_set_sensitive(TRUE);
      xsane_set_sensitivity(TRUE); /* allow changing xsane mode */

      if (xsane.multipage_status)
      {
        free(xsane.multipage_status);
      }
      xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_FILE_SAVING_ERROR);

      gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
      gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
     return;
    }

    if (output_format == XSANE_PS)
    {
      xsane_save_ps_create_document_header(outfile, pages, preferences.save_ps_flatdecoded);
    }
    else if (output_format == XSANE_PDF)
    {
      xsane_save_pdf_create_document_header(outfile, &xref, pages, preferences.save_pdf_flatdecoded);
    }
  }
#ifdef HAVE_LIBTIFF
  else if (output_format == XSANE_TIFF)
  {
    tiffile = TIFFOpen(multipage_filename, "w");
    if (!tiffile)
    {
      snprintf(buf, sizeof(buf), "%s %s %s\n", ERR_DURING_SAVE, ERR_OPEN_FAILED, multipage_filename);
      xsane_back_gtk_error(buf, TRUE);
      xsane_multipage_project_set_sensitive(TRUE);
      xsane_set_sensitivity(TRUE); /* allow changing xsane mode */

      if (xsane.multipage_status)
      {
        free(xsane.multipage_status);
      }
      xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_FILE_SAVING_ERROR);

      gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
      gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
     return;
    }
  }
#endif
  else
  {
    snprintf(buf, sizeof(buf), "%s unsupported multipage fileformat\n", ERR_DURING_SAVE);
    xsane_back_gtk_error(buf, TRUE);
    xsane_multipage_project_set_sensitive(TRUE);
    xsane_set_sensitivity(TRUE); /* allow changing xsane mode */

    if (xsane.multipage_status)
    {
      free(xsane.multipage_status);
    }
    xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_FILE_SAVING_ERROR);

    gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
    gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
   return;
  }


  list = (GList *) GTK_LIST(xsane.project_list)->children;

  page = 1;
  while (list)
  {
    list_item = GTK_OBJECT(list->data);
    image = strdup((char *) gtk_object_get_data(list_item, "list_item_data"));
    type  = strdup((char *) gtk_object_get_data(list_item, "list_item_type"));
    xsane_convert_text_to_filename(&image);
    snprintf(source_filename, sizeof(source_filename), "%s/%s%s", preferences.multipage_project, image, type);

    imagefile = fopen(source_filename, "rb"); /* read binary (b for win32) */
    if (!imagefile)
    {
      DBG(DBG_error, "could not read imagefile %s\n", source_filename);
     return;
    }

    xsane_read_pnm_header(imagefile, &image_info);

    /* reduce lineart images to lineart before conversion */
    if (image_info.reduce_to_lineart)
    {
     char lineart_filename[PATH_MAX];

      DBG(DBG_info, "original image is a lineart => reduce to lineart\n");
      fclose(imagefile);
      xsane_back_gtk_make_path(sizeof(lineart_filename), lineart_filename, 0, 0, "xsane-conversion-", xsane.dev_name, ".pbm", XSANE_PATH_TMP);

      snprintf(buf, sizeof(buf), "%s", PROGRESS_PACKING_DATA);
                                                                                                                         
      gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), buf);
      gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);
                                                                                                                         
      while (gtk_events_pending())
      {
        gtk_main_iteration();
      }
                                                                                                                         
      xsane_save_image_as_lineart(lineart_filename, source_filename, xsane.project_progress_bar, &cancel_save);
                                                                                                                         
      strncpy(source_filename, lineart_filename, sizeof(source_filename));
      remove_lineart_file = TRUE;
                                                                                                                         
      imagefile = fopen(source_filename, "rb"); /* read binary (b for win32) */
      if (imagefile == 0)
      {
       char buf[TEXTBUFSIZE];
        snprintf(buf, sizeof(buf), "%s `%s': %s", ERR_OPEN_FAILED, source_filename, strerror(errno));
        xsane_back_gtk_error(buf, TRUE);
                                                                                                                          
       return;
      }
                                                                                                                         
      xsane_read_pnm_header(imagefile, &image_info);
    }


    snprintf(buf, sizeof(buf), "%s %s %d/%d", _(xsane.multipage_status), PROGRESS_PAGE, page, pages);
    gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), buf);


    if (output_format == XSANE_PS)
    {
      imagewidth  = 72.0 * image_info.image_width/image_info.resolution_x; /* width in 1/72 inch */
      imageheight = 72.0 * image_info.image_height/image_info.resolution_y; /* height in 1/72 inch */

      xsane_save_ps_page(outfile, page,
                         imagefile, &image_info, imagewidth, imageheight,
                         0, 0, imagewidth, imageheight, 0 /* portrait top left */,
                         preferences.save_ps_flatdecoded,
                         xsane.project_progress_bar, &cancel_save);
    }
    else if (output_format == XSANE_PDF)
    {
      imagewidth  = 72.0 * image_info.image_width/image_info.resolution_x; /* width in 1/72 inch */
      imageheight = 72.0 * image_info.image_height/image_info.resolution_y; /* height in 1/72 inch */

      xsane_save_pdf_page(outfile, &xref, page,
                         imagefile, &image_info, imagewidth, imageheight,
                         0, 0, imagewidth, imageheight, 0 /* portrait top left */,
                         preferences.save_pdf_flatdecoded,
                         xsane.project_progress_bar, &cancel_save);
    }
#ifdef HAVE_LIBTIFF
    else if (output_format == XSANE_TIFF)
    {
      xsane_save_tiff_page(tiffile, page, pages, imagefile, &image_info, preferences.jpeg_quality,
                           xsane.project_progress_bar, &cancel_save);
    }
#endif

    if (remove_lineart_file)
    {
      remove(source_filename); /* remove lineart pbm file  */
    }

    free(image);
    free(type);
    list = list->next;

    page++;
  }

  if (output_format == XSANE_PS)
  {
    xsane_save_ps_create_document_trailer(outfile, 0 /* we defined pages at beginning */);
                                                                                                                                
    if (ferror(outfile))
    {
     char buf[TEXTBUFSIZE];
                                                                                                                                
      snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
      DBG(DBG_error, "%s\n", buf);
      xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
      cancel_save = 1;
    } 

    fclose(outfile);
  }
  else if (output_format == XSANE_PDF)
  {
    xsane_save_pdf_create_document_trailer(outfile, &xref, pages);
                                                                                                                                
    if (ferror(outfile))
    {
     char buf[TEXTBUFSIZE];
                                                                                                                                
      snprintf(buf, sizeof(buf), "%s %s", ERR_DURING_SAVE, strerror(errno));
      DBG(DBG_error, "%s\n", buf);
      xsane_back_gtk_decision(ERR_HEADER_ERROR, (gchar **) error_xpm, buf, BUTTON_OK, NULL, TRUE /* wait */);
      cancel_save = 1;
    }

    fclose(outfile);
  }
#ifdef HAVE_LIBTIFF
  else if (output_format == XSANE_TIFF)
  {
    TIFFClose(tiffile);
  }
#endif

  if (xsane.multipage_status)
  {
    free(xsane.multipage_status);
    xsane.multipage_status = NULL;
  }

  if (cancel_save)
  {
    xsane.multipage_status = strdup(ERR_DURING_SAVE);
  }
  else
  {
    xsane.multipage_status = strdup(TEXT_PROJECT_STATUS_FILE_SAVED);
  }
  xsane_multipage_project_save();

  gtk_progress_set_format_string(GTK_PROGRESS(xsane.project_progress_bar), _(xsane.multipage_status));
  gtk_progress_bar_update(GTK_PROGRESS_BAR(xsane.project_progress_bar), 0.0);

  xsane_multipage_project_set_sensitive(TRUE);
  xsane_set_sensitivity(TRUE); /* allow changing xsane mode */
}


