home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / fileops.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  60.3 KB  |  2,371 lines

  1. /* The GIMP -- an image manipulation program
  2.  * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis
  3.  * Copyright (C) 1997 Josh MacDonald
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  18.  */
  19.  
  20. #include "config.h"
  21.  
  22. #ifdef HAVE_SYS_PARAM_H
  23. #include <sys/param.h>
  24. #endif
  25.  
  26. #include <gtk/gtk.h>
  27.  
  28. #include <ctype.h>
  29. #include <errno.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <sys/types.h>
  33. #include <sys/stat.h>
  34. #ifdef HAVE_UNISTD_H
  35. #include <unistd.h>
  36. #endif
  37. #include <errno.h>
  38.  
  39. #ifdef G_OS_WIN32
  40. #include <direct.h>        /* For _mkdir() */
  41. #define mkdir(path,mode) _mkdir(path)
  42. #include <io.h>
  43. #ifndef S_IWUSR
  44. #define S_IWUSR _S_IWRITE
  45. #endif
  46. #ifndef S_IRUSR
  47. #define S_IRUSR _S_IREAD
  48. #endif
  49. #ifndef S_IWGRP
  50. #define S_IWGRP (_S_IWRITE>>3)
  51. #define S_IWOTH (_S_IWRITE>>6)
  52. #endif
  53. #ifndef S_IRGRP
  54. #define S_IRGRP (_S_IREAD>>3)
  55. #define S_IROTH (_S_IREAD>>6)
  56. #endif
  57. #define uid_t gint
  58. #define gid_t gint
  59. #define geteuid() 0
  60. #define getegid() 0
  61. #endif
  62.  
  63. #include "apptypes.h"
  64.  
  65. #include "appenv.h"
  66. #include "cursorutil.h"
  67. #include "dialog_handler.h"
  68. #include "gdisplay.h"
  69. #include "gimage.h"
  70. #include "gimpcontext.h"
  71. #include "gimpdrawableP.h"
  72. #include "gimpui.h"
  73. #include "fileops.h"
  74. #include "fileopsP.h"
  75. #include "menus.h"
  76. #include "layer.h"
  77. #include "channel.h"
  78. #include "plug_in.h"
  79. #include "procedural_db.h"
  80. #include "gimprc.h"
  81. #include "docindex.h"
  82. #include "undo.h"
  83.  
  84. #include "libgimp/gimpmath.h"
  85.  
  86. #include "libgimp/gimpintl.h"
  87.  
  88.  
  89. #define REVERT_DATA_KEY "revert_confirm_dialog"
  90.  
  91.  
  92. typedef struct _OverwriteData OverwriteData;
  93.  
  94. struct _OverwriteData
  95. {
  96.   gchar *full_filename;
  97.   gchar *raw_filename;
  98. };
  99.  
  100.  
  101. static void    file_overwrite               (gchar         *filename,
  102.                          gchar         *raw_filename);
  103. static void    file_overwrite_callback      (GtkWidget     *widget,
  104.                          gboolean       overwrite,
  105.                          gpointer       data);
  106. static void    file_revert_confirm_callback (GtkWidget     *widget,
  107.                          gboolean       revert,
  108.                          gpointer       data);
  109.  
  110. static GimpImage * file_open_image          (gchar         *filename,
  111.                          gchar         *raw_filename,
  112.                          gchar         *open_mode,
  113.                          RunModeType    run_mode,
  114.                          gint          *status);
  115.  
  116. static gint    file_save                    (GimpImage     *gimage,
  117.                          gchar         *filename,
  118.                          gchar         *raw_filename,
  119.                          RunModeType    run_mode);
  120.  
  121. static void    file_open_genbutton_callback (GtkWidget     *widget,
  122.                          gpointer       data);
  123.  
  124. static void    file_open_clistrow_callback  (GtkWidget     *widget,
  125.                          gint           row);
  126.  
  127. static void    file_open_ok_callback        (GtkWidget     *widget,
  128.                          gpointer       data);
  129.  
  130. static void    file_save_ok_callback        (GtkWidget     *widget,
  131.                          gpointer       data);
  132.  
  133. static void    file_dialog_show             (GtkWidget     *filesel);
  134. static gint    file_dialog_hide             (GtkWidget     *filesel);
  135. static void    file_update_name             (PlugInProcDef *proc,
  136.                          GtkWidget     *filesel);
  137.  
  138. static void    file_open_type_callback      (GtkWidget     *widget,
  139.                          gpointer       data);
  140. static void    file_save_type_callback      (GtkWidget     *widget,
  141.                          gpointer       data);
  142.  
  143. static void    file_convert_string          (gchar         *instr,
  144.                          gchar         *outmem,
  145.                          gint           maxmem,
  146.                          gint          *nmem);
  147.  
  148. static gchar * file_absolute_filename       (gchar         *name);
  149.  
  150. static gint    file_check_single_magic      (gchar         *offset,
  151.                          gchar         *type,
  152.                          gchar         *value,
  153.                          gint           headsize,
  154.                          guchar        *file_head,
  155.                          FILE          *ifp);
  156.  
  157. static gint    file_check_magic_list        (GSList        *magics_list,
  158.                          gint           headsize,
  159.                          guchar        *head,
  160.                          FILE          *ifp);
  161.  
  162. static void    file_update_menus            (GSList        *procs,
  163.                          gint           image_type);
  164.  
  165. static GSList* clist_to_slist               (GtkCList      *file_list);
  166.  
  167.  
  168.  
  169. static GtkWidget  *fileload     = NULL;
  170. static GtkWidget  *filesave     = NULL;
  171. static GtkWidget  *open_options = NULL;
  172. static GtkWidget  *save_options = NULL;
  173.  
  174. /* widgets for the open_options menu */
  175. static GtkPreview *open_options_preview        = NULL;
  176. static GtkWidget  *open_options_fixed          = NULL;
  177. static GtkWidget  *open_options_label          = NULL;
  178. static GtkWidget  *open_options_frame          = NULL;
  179. static GtkWidget  *open_options_genbuttonlabel = NULL;
  180.  
  181. /* Some state for the thumbnailer */
  182. static gchar *preview_fullname = NULL;
  183.  
  184. GSList *load_procs = NULL;
  185. GSList *save_procs = NULL;
  186.  
  187. static PlugInProcDef *load_file_proc = NULL;
  188. static PlugInProcDef *save_file_proc = NULL;
  189.  
  190. static GimpImage *the_gimage = NULL;
  191.  
  192. extern GSList *display_list; /* from gdisplay.c */
  193.  
  194.  
  195. void
  196. file_ops_pre_init (void)
  197. {
  198. }
  199.  
  200. void
  201. file_ops_post_init (void)
  202. {
  203.   GimpItemFactoryEntry entry;
  204.   PlugInProcDef *file_proc;
  205.   GSList *tmp;
  206.  
  207.   load_procs = g_slist_reverse (load_procs);
  208.   save_procs = g_slist_reverse (save_procs);
  209.  
  210.   for (tmp = load_procs; tmp;  tmp = g_slist_next (tmp))
  211.     {
  212.       gchar *help_page;
  213.  
  214.       file_proc = tmp->data;
  215.  
  216.       help_page = g_strconcat ("filters/",
  217.                    g_basename (file_proc->prog),
  218.                    ".html",
  219.                    NULL);
  220.       g_strdown (help_page);
  221.  
  222.       entry.entry.path            = file_proc->menu_path;
  223.       entry.entry.accelerator     = NULL;
  224.       entry.entry.callback        = file_open_type_callback;
  225.       entry.entry.callback_action = 0;
  226.       entry.entry.item_type       = NULL;
  227.       entry.help_page             = help_page;
  228.       entry.description           = NULL;
  229.  
  230.       menus_create_item_from_full_path (&entry, NULL, file_proc);
  231.     }
  232.  
  233.   for (tmp = save_procs; tmp; tmp = g_slist_next (tmp))
  234.     {
  235.       gchar *help_page;
  236.  
  237.       file_proc = tmp->data;
  238.  
  239.       help_page = g_strconcat ("filters/",
  240.                    g_basename (file_proc->prog),
  241.                    ".html",
  242.                    NULL);
  243.       g_strdown (help_page);
  244.  
  245.       entry.entry.path            = file_proc->menu_path;
  246.       entry.entry.accelerator     = NULL;
  247.       entry.entry.callback        = file_save_type_callback;
  248.       entry.entry.callback_action = 0;
  249.       entry.entry.item_type       = NULL;
  250.       entry.help_page             = help_page;
  251.       entry.description           = NULL;
  252.  
  253.       menus_create_item_from_full_path (&entry, NULL, file_proc);
  254.     }
  255. }
  256.  
  257. void
  258. file_open_callback (GtkWidget *widget,
  259.             gpointer   data)
  260. {
  261.   if (!fileload)
  262.     {
  263.       fileload = gtk_file_selection_new (_("Load Image"));
  264.       gtk_window_set_position (GTK_WINDOW (fileload), GTK_WIN_POS_MOUSE);
  265.       gtk_window_set_wmclass (GTK_WINDOW (fileload), "load_image", "Gimp");
  266.  
  267.       gtk_container_set_border_width (GTK_CONTAINER (fileload), 2);
  268.       gtk_container_set_border_width 
  269.     (GTK_CONTAINER (GTK_FILE_SELECTION (fileload)->button_area), 2);
  270.  
  271.       dialog_register_fileload (fileload);
  272.  
  273.       gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->cancel_button),
  274.                  "clicked",
  275.                  GTK_SIGNAL_FUNC (file_dialog_hide),
  276.                  GTK_OBJECT (fileload));
  277.       gtk_signal_connect (GTK_OBJECT (fileload), "delete_event",
  278.               GTK_SIGNAL_FUNC (file_dialog_hide),
  279.               NULL);
  280.       gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->ok_button),
  281.               "clicked",
  282.               GTK_SIGNAL_FUNC (file_open_ok_callback),
  283.               fileload);
  284.       gtk_quit_add_destroy (1, GTK_OBJECT (fileload));
  285.  
  286.       gtk_clist_set_selection_mode (GTK_CLIST
  287.                     (GTK_FILE_SELECTION (fileload)->file_list),
  288.                     GTK_SELECTION_EXTENDED);
  289.  
  290.       /* Catch file-clist clicks so we can update the preview thumbnail */
  291.       gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->file_list),
  292.               "select_row",
  293.               GTK_SIGNAL_FUNC (file_open_clistrow_callback),
  294.               fileload);
  295.  
  296.       /*  Connect the "F1" help key  */
  297.       gimp_help_connect_help_accel (fileload,
  298.                     gimp_standard_help_func,
  299.                     "open/dialogs/file_open.html");
  300.     }
  301.   else
  302.     {
  303.       gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE);
  304.       if (GTK_WIDGET_VISIBLE (fileload))
  305.     return;
  306.  
  307.       gtk_file_selection_set_filename (GTK_FILE_SELECTION (fileload),
  308.                        "." G_DIR_SEPARATOR_S);
  309.       gtk_window_set_title (GTK_WINDOW (fileload), _("Load Image"));
  310.     }
  311.  
  312.   if (!open_options)
  313.     {
  314.       GtkWidget *frame;
  315.       GtkWidget *vbox;
  316.       GtkWidget *hbox;
  317.       GtkWidget *option_menu;
  318.       GtkWidget *load_menu;
  319.       GtkWidget *open_options_genbutton;
  320.  
  321.       open_options = gtk_hbox_new (TRUE, 1);
  322.  
  323.       /* format-chooser frame */
  324.       frame = gtk_frame_new (_("Determine File Type"));
  325.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  326.       gtk_box_pack_start (GTK_BOX (open_options), frame, TRUE, TRUE, 4);
  327.  
  328.       vbox = gtk_vbox_new (FALSE, 2);
  329.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  330.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  331.  
  332.       hbox = gtk_hbox_new (FALSE, 0);
  333.       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  334.       gtk_widget_show (hbox);
  335.  
  336.       option_menu = gtk_option_menu_new ();
  337.       gtk_box_pack_start (GTK_BOX (hbox), option_menu, FALSE, FALSE, 0);
  338.       gtk_widget_show (option_menu);
  339.  
  340.       menus_get_load_menu (&load_menu, NULL);
  341.       gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), load_menu);
  342.  
  343.       gtk_widget_show (vbox);
  344.       gtk_widget_show (frame);
  345.  
  346.       /* Preview frame */
  347.       open_options_frame = frame = gtk_frame_new ("");
  348.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  349.       gtk_box_pack_end (GTK_BOX (open_options), frame, FALSE, TRUE, 4);
  350.  
  351.       vbox = gtk_vbox_new (FALSE, 2);
  352.       gtk_container_set_border_width (GTK_CONTAINER (vbox), 4);
  353.       gtk_container_add (GTK_CONTAINER (frame), vbox);
  354.  
  355.       hbox = gtk_hbox_new (TRUE, 0);
  356.       gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  357.       gtk_widget_show (hbox);
  358.  
  359.       open_options_genbutton = gtk_button_new ();
  360.       gtk_signal_connect (GTK_OBJECT (open_options_genbutton), "clicked",
  361.               GTK_SIGNAL_FUNC (file_open_genbutton_callback),
  362.               fileload);
  363.       gtk_box_pack_start (GTK_BOX (hbox), open_options_genbutton,
  364.               TRUE, FALSE, 0);
  365.       gtk_widget_show (open_options_genbutton);    
  366.  
  367.       open_options_fixed = gtk_fixed_new ();
  368.       gtk_widget_set_usize (open_options_fixed, 80, 60);
  369.       gtk_container_add (GTK_CONTAINER (GTK_BIN (open_options_genbutton)),
  370.              open_options_fixed);
  371.       gtk_widget_show (open_options_fixed);
  372.  
  373.       {
  374.     GtkWidget* abox;
  375.     GtkWidget* sbox;
  376.     GtkWidget* align;
  377.  
  378.     sbox = gtk_vbox_new (TRUE, 0);
  379.     gtk_container_add (GTK_CONTAINER (open_options_fixed), sbox);
  380.     gtk_widget_show (sbox);
  381.  
  382.     align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  383.     gtk_widget_set_usize (align, 80, 60);
  384.     gtk_box_pack_start (GTK_BOX (sbox), align, FALSE, TRUE, 0);
  385.     gtk_widget_show (align);
  386.  
  387.     abox = gtk_hbox_new (FALSE, 0);
  388.     gtk_container_add (GTK_CONTAINER (align), abox);
  389.     gtk_widget_show (abox);
  390.  
  391.     open_options_preview =
  392.       GTK_PREVIEW (gtk_preview_new (GTK_PREVIEW_COLOR));
  393.     gtk_box_pack_start (GTK_BOX (abox), GTK_WIDGET (open_options_preview),
  394.                 FALSE, TRUE, 0);
  395.     gtk_widget_show (GTK_WIDGET (open_options_preview));
  396.  
  397.     open_options_genbuttonlabel = gtk_label_new (_("Generate\nPreview"));
  398.     gtk_box_pack_start (GTK_BOX (abox), open_options_genbuttonlabel,
  399.                 FALSE, TRUE, 0);
  400.     gtk_widget_show (open_options_genbuttonlabel);
  401.       }
  402.  
  403.       open_options_label = gtk_label_new ("");
  404.       gtk_box_pack_start (GTK_BOX (vbox), open_options_label, FALSE, FALSE, 0);
  405.       gtk_widget_show (open_options_label); 
  406.  
  407.       gtk_widget_show (vbox);
  408.       gtk_widget_show (frame);
  409.  
  410.       /* pack the containing open_options hbox into the open-dialog */
  411.       gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (fileload)->main_vbox),
  412.             open_options, FALSE, FALSE, 0);
  413.     }
  414.  
  415.   gtk_frame_set_label (GTK_FRAME (open_options_frame), _("Preview"));
  416.   gtk_label_set_text (GTK_LABEL (open_options_label), _("No Selection."));
  417.  
  418.   gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel));
  419.   gtk_widget_hide (GTK_WIDGET (open_options_preview));
  420.   gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), FALSE);
  421.  
  422.   gtk_widget_show (open_options);
  423.  
  424.   file_dialog_show (fileload);
  425. }
  426.  
  427. void
  428. file_save_callback (GtkWidget *widget,
  429.             gpointer   data)
  430. {
  431.   GDisplay *gdisplay;
  432.  
  433.   gdisplay = gdisplay_active ();
  434.   if (! gdisplay)
  435.     return;
  436.  
  437.   if (! gimage_active_drawable (gdisplay->gimage))
  438.     return;
  439.  
  440.   /*  Only save if the gimage has been modified  */
  441.   if (!trust_dirty_flag || gdisplay->gimage->dirty != 0)
  442.     {
  443.       if (gdisplay->gimage->has_filename == FALSE)
  444.     {
  445.       file_save_as_callback (widget, data);
  446.     }
  447.       else
  448.     {
  449.       gchar *filename;
  450.       gchar *raw_filename;
  451.       gint   status;
  452.  
  453.       filename     = g_strdup (gimage_filename (gdisplay->gimage));
  454.       raw_filename = g_basename (filename);
  455.       
  456.       status = file_save (gdisplay->gimage,
  457.                   filename,
  458.                   raw_filename,
  459.                   RUN_WITH_LAST_VALS);
  460.  
  461.       if (status != PDB_SUCCESS &&
  462.           status != PDB_CANCEL)
  463.         {
  464.           g_message (_("Save failed.\n%s"), filename);
  465.         }
  466.  
  467.       g_free (filename);
  468.     }
  469.     }
  470. }
  471.  
  472. void
  473. file_save_as_callback (GtkWidget *widget,
  474.                gpointer   data)
  475. {
  476.   GDisplay *gdisplay;
  477.  
  478.   gdisplay = gdisplay_active ();
  479.   if (! gdisplay)
  480.     return;
  481.  
  482.   if (! gimage_active_drawable (gdisplay->gimage))
  483.     return;
  484.  
  485.   the_gimage = gdisplay->gimage;
  486.  
  487.   if (!filesave)
  488.     {
  489.       filesave = gtk_file_selection_new (_("Save Image"));
  490.       gtk_window_set_wmclass (GTK_WINDOW (filesave), "save_image", "Gimp");
  491.       gtk_window_set_position (GTK_WINDOW (filesave), GTK_WIN_POS_MOUSE);
  492.  
  493.       gtk_container_set_border_width (GTK_CONTAINER (filesave), 2);
  494.       gtk_container_set_border_width 
  495.     (GTK_CONTAINER (GTK_FILE_SELECTION (filesave)->button_area), 2);
  496.  
  497.       gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->cancel_button),
  498.                  "clicked",
  499.                  GTK_SIGNAL_FUNC (file_dialog_hide),
  500.                  GTK_OBJECT (filesave));
  501.       gtk_signal_connect (GTK_OBJECT (filesave), "delete_event",
  502.               GTK_SIGNAL_FUNC (file_dialog_hide),
  503.               NULL);
  504.       gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->ok_button),
  505.               "clicked",
  506.               GTK_SIGNAL_FUNC (file_save_ok_callback),
  507.               filesave);
  508.       gtk_quit_add_destroy (1, GTK_OBJECT (filesave));
  509.  
  510.       /*  Connect the "F1" help key  */
  511.       gimp_help_connect_help_accel (filesave,
  512.                     gimp_standard_help_func,
  513.                     "save/dialogs/file_save.html");
  514.     }
  515.   else
  516.     {
  517.       gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE);
  518.       if (GTK_WIDGET_VISIBLE (filesave))
  519.     return;
  520.  
  521.       gtk_window_set_title (GTK_WINDOW (filesave), _("Save Image"));
  522.     }
  523.  
  524.   gtk_file_selection_set_filename (GTK_FILE_SELECTION(filesave),
  525.                                    gdisplay->gimage->has_filename
  526.                                    ? gimage_filename(gdisplay->gimage)
  527.                                    : "." G_DIR_SEPARATOR_S);
  528.   if (!save_options)
  529.     {
  530.       GtkWidget *frame;
  531.       GtkWidget *hbox;
  532.       GtkWidget *label;
  533.       GtkWidget *option_menu;
  534.       GtkWidget *save_menu;
  535.  
  536.       save_options = gtk_hbox_new (TRUE, 1);
  537.  
  538.       frame = gtk_frame_new (_("Save Options"));
  539.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  540.       gtk_box_pack_start (GTK_BOX (save_options), frame, TRUE, TRUE, 4);
  541.  
  542.       hbox = gtk_hbox_new (FALSE, 4);
  543.       gtk_container_set_border_width (GTK_CONTAINER (hbox), 4);
  544.       gtk_container_add (GTK_CONTAINER (frame), hbox);
  545.       gtk_widget_show (hbox);
  546.  
  547.       label = gtk_label_new (_("Determine File Type:"));
  548.       gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
  549.       gtk_widget_show (label);
  550.  
  551.       option_menu = gtk_option_menu_new ();
  552.       gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 0);
  553.       gtk_widget_show (option_menu);
  554.  
  555.       menus_get_save_menu (&save_menu, NULL);
  556.       gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), save_menu);
  557.  
  558.       gtk_widget_show (frame);
  559.  
  560.       /* pack the containing save_options hbox into the save-dialog */
  561.       gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (filesave)->main_vbox),
  562.             save_options, FALSE, FALSE, 0);
  563.     }
  564.  
  565.   switch (drawable_type (gimage_active_drawable (gdisplay->gimage)))
  566.     {
  567.     case RGB_GIMAGE:
  568.       file_update_menus (save_procs, PLUG_IN_RGB_IMAGE);
  569.       break;
  570.     case RGBA_GIMAGE:
  571.       file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE);
  572.       break;
  573.     case GRAY_GIMAGE:
  574.       file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE);
  575.       break;
  576.     case GRAYA_GIMAGE:
  577.       file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE);
  578.       break;
  579.     case INDEXED_GIMAGE:
  580.       file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE);
  581.       break;
  582.     case INDEXEDA_GIMAGE:
  583.       file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE);
  584.       break;
  585.     }
  586.  
  587.   gtk_widget_show (save_options);
  588.  
  589.   file_dialog_show (filesave);
  590. }
  591.  
  592. void
  593. file_revert_callback (GtkWidget *widget,
  594.               gpointer   data)
  595. {
  596.   GDisplay  *gdisplay;
  597.   GimpImage *gimage;
  598.   GtkWidget *query_box;
  599.  
  600.   gdisplay = gdisplay_active ();
  601.   if (!gdisplay || !gdisplay->gimage)
  602.     return;
  603.  
  604.   gimage = gdisplay->gimage;
  605.  
  606.   query_box = gtk_object_get_data (GTK_OBJECT (gimage), REVERT_DATA_KEY);
  607.  
  608.   if (gimage->has_filename == FALSE)
  609.     {
  610.       g_message (_("Revert failed.\n"
  611.            "No filename associated with this image."));
  612.     }
  613.   else if (query_box)
  614.     {
  615.       gdk_window_raise (query_box->window);
  616.     }
  617.   else
  618.     {
  619.       gchar *text;
  620.  
  621.       text = g_strdup_printf (_("Reverting %s to\n"
  622.                 "%s\n\n"
  623.                 "(You will loose all your changes\n"
  624.                 "including all undo information)"),
  625.                   g_basename (gimage_filename (gimage)),
  626.                   gimage_filename (gimage));
  627.  
  628.       query_box = gimp_query_boolean_box (_("Revert Image?"),
  629.                       gimp_standard_help_func,
  630.                       "file/revert.html",
  631.                       FALSE,
  632.                       text,
  633.                       _("Yes"), _("No"),
  634.                       GTK_OBJECT (gimage), "destroy",
  635.                       file_revert_confirm_callback,
  636.                       gimage);
  637.  
  638.       g_free (text);
  639.  
  640.       gtk_object_set_data (GTK_OBJECT (gimage), REVERT_DATA_KEY, query_box);
  641.  
  642.       gtk_widget_show (query_box);
  643.     }
  644. }
  645.  
  646. void
  647. file_open_by_extension_callback (GtkWidget *widget,
  648.                  gpointer   data)
  649. {
  650.   load_file_proc = NULL;
  651. }
  652.  
  653. void
  654. file_save_by_extension_callback (GtkWidget *widget,
  655.                  gpointer   data)
  656. {
  657.   save_file_proc = NULL;
  658. }
  659.  
  660. static void
  661. file_update_name (PlugInProcDef *proc,
  662.           GtkWidget     *filesel)
  663. {
  664.   if (proc->extensions_list)
  665.     {
  666.       gchar *text;
  667.       gchar *last_dot;
  668.       GString *s;
  669.  
  670.       text = gtk_entry_get_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry));
  671.       last_dot = strrchr (text, '.');
  672.  
  673.       if (last_dot == text || !text[0])
  674.     return;
  675.  
  676.       s = g_string_new (text);
  677.  
  678.       if (last_dot)
  679.     g_string_truncate (s, last_dot-text);
  680.  
  681.       g_string_append (s, ".");
  682.       g_string_append (s, (gchar *) proc->extensions_list->data);
  683.  
  684.       gtk_entry_set_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry), s->str);
  685.  
  686.       g_string_free (s, TRUE);
  687.     }
  688. }
  689.  
  690. static void
  691. file_open_type_callback (GtkWidget *widget,
  692.              gpointer   data)
  693. {
  694.   PlugInProcDef *proc = (PlugInProcDef *) data;
  695.  
  696.   file_update_name (proc, fileload);
  697.  
  698.   load_file_proc = proc;
  699. }
  700.  
  701. static void
  702. file_save_type_callback (GtkWidget *widget,
  703.              gpointer   data)
  704. {
  705.   PlugInProcDef *proc = (PlugInProcDef *) data;
  706.  
  707.   file_update_name (proc, filesave);
  708.  
  709.   save_file_proc = proc;
  710. }
  711.  
  712. static GimpImage *
  713. file_open_image (gchar       *filename,
  714.          gchar       *raw_filename,
  715.          gchar       *open_mode,
  716.          RunModeType  run_mode,
  717.          gint        *status)
  718. {
  719.   PlugInProcDef *file_proc;
  720.   ProcRecord *proc;
  721.   Argument   *args;
  722.   Argument   *return_vals;
  723.   gint gimage_id;
  724.   gint i;
  725.   struct stat statbuf;
  726.  
  727.   *status = PDB_CANCEL;  /* inhibits error messages by caller */
  728.  
  729.   file_proc = load_file_proc;
  730.  
  731.   if (!file_proc)
  732.     file_proc = file_proc_find (load_procs, filename);
  733.  
  734.   if (!file_proc)
  735.     {
  736.       /*  no errors when making thumbnails  */
  737.       if (run_mode == RUN_INTERACTIVE)
  738.     g_message (_("%s failed.\n"
  739.              "%s: Unknown file type."),
  740.            open_mode, filename);
  741.  
  742.       return NULL;
  743.     }
  744.  
  745.   /* check if we are opening a file */
  746.   if (stat (filename, &statbuf) == 0)
  747.     {
  748.       uid_t euid;
  749.       gid_t egid;
  750.  
  751.       if (! (statbuf.st_mode & S_IFREG))
  752.     {
  753.       /*  no errors when making thumbnails  */
  754.       if (run_mode == RUN_INTERACTIVE)
  755.         g_message (_("%s failed.\n"
  756.              "%s is not a regular file."),
  757.                open_mode, filename);
  758.  
  759.       return NULL;
  760.     }
  761.  
  762.       euid = geteuid ();
  763.       egid = getegid ();
  764.  
  765.       if (! ((statbuf.st_mode & S_IRUSR) ||
  766.  
  767.          ((statbuf.st_mode & S_IRGRP) &&
  768.           (statbuf.st_uid != euid)) ||
  769.  
  770.          ((statbuf.st_mode & S_IROTH) &&
  771.           (statbuf.st_uid != euid) &&
  772.           (statbuf.st_gid != egid))))
  773.     {
  774.       /*  no errors when making thumbnails  */
  775.       if (run_mode == RUN_INTERACTIVE)
  776.         g_message (_("%s failed.\n"
  777.              "%s: Permission denied."),
  778.                open_mode, filename);
  779.  
  780.       return NULL;
  781.     }
  782.     }
  783.  
  784.   proc = &file_proc->db_info;
  785.  
  786.   args = g_new0 (Argument, proc->num_args);
  787.  
  788.   for (i = 0; i < proc->num_args; i++)
  789.     args[i].arg_type = proc->args[i].arg_type;
  790.  
  791.   args[0].value.pdb_int     = run_mode;
  792.   args[1].value.pdb_pointer = filename;
  793.   args[2].value.pdb_pointer = raw_filename;
  794.  
  795.   return_vals = procedural_db_execute (proc->name, args);
  796.  
  797.   *status   = return_vals[0].value.pdb_int;
  798.   gimage_id = return_vals[1].value.pdb_int;
  799.  
  800.   procedural_db_destroy_args (return_vals, proc->num_values);
  801.   g_free (args);
  802.  
  803.   if (*status == PDB_SUCCESS && gimage_id != -1)
  804.     {
  805.       GimpImage *gimage = gimage_get_ID (gimage_id);
  806.  
  807.       if (gimage)
  808.     {
  809.       layer_invalidate_previews (gimage);
  810.       channel_invalidate_previews (gimage);
  811.     }
  812.  
  813.       return pdb_id_to_image (gimage_id);
  814.     }
  815.  
  816.   return NULL;
  817. }
  818.  
  819. gint
  820. file_open (gchar *filename,
  821.        gchar *raw_filename)
  822. {
  823.   GimpImage *gimage;
  824.   GDisplay  *gdisplay;
  825.   gchar     *absolute;
  826.   gint       status;
  827.  
  828.   if ((gimage = file_open_image (filename,
  829.                  raw_filename,
  830.                  _("Open"),
  831.                  RUN_INTERACTIVE,
  832.                  &status)) != NULL)
  833.     {
  834.       /* enable & clear all undo steps */
  835.       gimage_enable_undo (gimage);
  836.  
  837.       /* set the image to clean  */
  838.       gimage_clean_all (gimage);
  839.  
  840.       /* display the image */
  841.       gdisplay = gdisplay_new (gimage, 0x0101);
  842.  
  843.       /* always activate the first display */
  844.       if (g_slist_length (display_list) == 1)
  845.     gimp_context_set_display (gimp_context_get_user (), gdisplay);
  846.  
  847.       absolute = file_absolute_filename (filename);
  848.       document_index_add (absolute);
  849.       menus_last_opened_add (absolute);
  850.       g_free (absolute);
  851.     }
  852.  
  853.   return status;
  854. }
  855.  
  856. TempBuf *
  857. make_thumb_tempbuf (GimpImage *gimage)
  858. {
  859.   gint w, h;
  860.  
  861.   if (gimage->width<=80 && gimage->height<=60)
  862.     {
  863.       w = gimage->width;
  864.       h = gimage->height;
  865.     }
  866.   else
  867.     {
  868.       /* Ratio molesting to fit within .xvpic thumbnail size limits */
  869.       if (60 * gimage->width < 80 * gimage->height)
  870.     {
  871.       h = 60;
  872.       w = (60 * gimage->width) / gimage->height;
  873.       if (w == 0)
  874.         w = 1;
  875.     }
  876.       else
  877.     {
  878.       w = 80;
  879.       h = (80 * gimage->height) / gimage->width;
  880.       if (h == 0)
  881.         h = 1;
  882.     }
  883.     }
  884.  
  885.   /*printf("tn: %d x %d -> ", w, h);fflush(stdout);*/
  886.  
  887.   return (gimp_image_composite_preview (gimage, GRAY_CHANNEL, w, h));
  888. }
  889.  
  890. static guchar *
  891. make_RGBbuf_from_tempbuf (TempBuf *tempbuf,
  892.               gint    *width_rtn,
  893.               gint    *height_rtn)
  894. {
  895.   gint    i, j, w, h;
  896.   guchar *tbd;
  897.   guchar *ptr;
  898.   guchar *rtn = NULL;
  899.   guchar  alpha, r, g, b;
  900.  
  901.   w = (*width_rtn) = tempbuf->width;
  902.   h = (*height_rtn) = tempbuf->height;
  903.   tbd = temp_buf_data (tempbuf);
  904.  
  905.   switch (tempbuf->bytes)
  906.     {
  907.     case 4:
  908.       rtn = ptr = g_malloc (3 * w * h);
  909.       for (i=0; i<h; i++)
  910.     {
  911.       for (j=0; j<w; j++)
  912.         {
  913.           r = *(tbd++);
  914.           g = *(tbd++);
  915.           b = *(tbd++);
  916.           alpha = *(tbd++);
  917.  
  918.           if (alpha & 128)
  919.         {
  920.           *(ptr++) = r;
  921.           *(ptr++) = g;
  922.           *(ptr++) = b;
  923.         }
  924.           else
  925.         {
  926.           r = (( (i^j) & 4 ) << 5) | 64;
  927.           *(ptr++) = r;
  928.           *(ptr++) = r;
  929.           *(ptr++) = r;
  930.         }
  931.         }
  932.     }
  933.       break;
  934.  
  935.     case 2:
  936.       rtn = ptr = g_malloc (3 * w * h);
  937.       for (i=0; i<h; i++)
  938.     {
  939.       for (j=0; j<w; j++)
  940.         {
  941.           r = *(tbd++);
  942.           alpha = *(tbd++);
  943.  
  944.           if (!(alpha & 128))
  945.         r = (( (i^j) & 4 ) << 5) | 64;
  946.  
  947.           *(ptr++) = r;
  948.           *(ptr++) = r;
  949.           *(ptr++) = r;
  950.         }
  951.     }
  952.       break;
  953.  
  954.     default:
  955.       g_warning("UNKNOWN TempBuf width in make_RGBbuf_from_tempbuf()");
  956.     }
  957.  
  958.   return (rtn);
  959. }
  960.  
  961. gboolean
  962. file_save_thumbnail (GimpImage  *gimage,
  963.              const char *full_source_filename,
  964.              TempBuf    *tempbuf)
  965. {
  966.   gint i,j;
  967.   gint w,h;
  968.   guchar *tbd;
  969.   gchar* pathname;
  970.   gchar* filename;
  971.   gchar* xvpathname;
  972.   gchar* thumbnailname;
  973.   GimpImageBaseType basetype;
  974.   FILE *fp;
  975.   struct stat statbuf;
  976.  
  977.   if (stat (full_source_filename, &statbuf) != 0)
  978.     {
  979.       return FALSE;
  980.     }
  981.  
  982.   /* just for debugging 
  983.    *  if (gimp_image_preview_valid (gimage, GRAY_CHANNEL))
  984.    *   {
  985.    *     g_print ("(incidentally, gimage already has a valid preview - %dx%d)\n",
  986.    *             gimage->comp_preview->width,
  987.    *             gimage->comp_preview->height);
  988.    *   }
  989.    */
  990.  
  991.   pathname = g_dirname (full_source_filename);
  992.   filename = g_basename (full_source_filename); /* Don't free! */
  993.  
  994.   xvpathname = g_strconcat (pathname, G_DIR_SEPARATOR_S, ".xvpics",
  995.                 NULL);
  996.  
  997.   thumbnailname = g_strconcat (xvpathname, G_DIR_SEPARATOR_S,
  998.                    filename,
  999.                    NULL);
  1000.  
  1001.   tbd = temp_buf_data (tempbuf);
  1002.  
  1003.   w = tempbuf->width;
  1004.   h = tempbuf->height;
  1005.   /*printf("tn: %d x %d\n", w, h);fflush(stdout);*/
  1006.  
  1007.   mkdir (xvpathname, 0755);
  1008.  
  1009.   fp = fopen (thumbnailname, "wb");
  1010.   g_free (pathname);
  1011.   g_free (xvpathname);
  1012.   g_free (thumbnailname);
  1013.  
  1014.   if (fp)
  1015.     {
  1016.       basetype = gimp_image_base_type(gimage);
  1017.  
  1018.       fprintf (fp,
  1019.            "P7 332\n#IMGINFO:%dx%d %s (%d %s)\n"
  1020.            "#END_OF_COMMENTS\n%d %d 255\n",
  1021.            gimage->width, gimage->height,
  1022.            (basetype == RGB) ? "RGB" :
  1023.            (basetype == GRAY) ? "Greyscale" :
  1024.            (basetype == INDEXED) ? "Indexed" :
  1025.            "(UNKNOWN COLOUR TYPE)",
  1026.            (int)statbuf.st_size,
  1027.            (statbuf.st_size == 1) ? "byte" : "bytes",
  1028.            w, h);
  1029.  
  1030.       switch (basetype)
  1031.     {
  1032.     case INDEXED:
  1033.     case RGB:
  1034.       for (i=0; i<h; i++)
  1035.         {
  1036.           /* Do a cheap unidirectional error-spread to look better */
  1037.           gint rerr=0, gerr=0, berr=0, a;
  1038.  
  1039.           for (j=0; j<w; j++)
  1040.         {
  1041.           gint32 r,g,b;
  1042.  
  1043.           if (128 & *(tbd + 3))
  1044.             {
  1045.               r = *(tbd++) + rerr;
  1046.               g = *(tbd++) + gerr;
  1047.               b = *(tbd++) + berr;
  1048.               tbd++;
  1049.             }
  1050.           else
  1051.             {
  1052.               a = (( (i^j) & 4 ) << 5) | 64; /* cute. */
  1053.               r = a + rerr;
  1054.               g = a + gerr;
  1055.               b = a + berr;
  1056.               tbd += 4;
  1057.             }
  1058.  
  1059.           r = CLAMP0255 (r);
  1060.           g = CLAMP0255 (g);
  1061.           b = CLAMP0255 (b);
  1062.  
  1063.           fputc(((r>>5)<<5) | ((g>>5)<<2) | (b>>6), fp);
  1064.  
  1065.           rerr = r - ( (r>>5) * 255 ) / 7;
  1066.           gerr = g - ( (g>>5) * 255 ) / 7;
  1067.           berr = b - ( (b>>6) * 255 ) / 3;
  1068.         }
  1069.         }
  1070.       break;
  1071.  
  1072.     case GRAY:
  1073.       for (i=0; i<h; i++)
  1074.         {
  1075.           /* Do a cheap unidirectional error-spread to look better */
  1076.           gint b3err=0, b2err=0, v, a;
  1077.  
  1078.           for (j=0; j<w; j++)
  1079.         {
  1080.           gint32 b3, b2;
  1081.  
  1082.           v = *(tbd++);
  1083.           a = *(tbd++);
  1084.  
  1085.           if (!(128 & a))
  1086.             v = (( (i^j) & 4 ) << 5) | 64;
  1087.  
  1088.           b2 = v + b2err;
  1089.           b3 = v + b3err;
  1090.  
  1091.           b2 = CLAMP0255 (b2);
  1092.           b3 = CLAMP0255 (b3);
  1093.  
  1094.           fputc(((b3>>5)<<5) | ((b3>>5)<<2) | (b2>>6), fp);
  1095.  
  1096.           b2err = b2 - ( (b2>>6) * 255 ) / 3;
  1097.           b3err = b3 - ( (b3>>5) * 255 ) / 7;
  1098.         }
  1099.         }
  1100.       break;
  1101.  
  1102.     default:
  1103.       g_warning("UNKNOWN GIMAGE TYPE IN THUMBNAIL SAVE");
  1104.       break;
  1105.     }
  1106.  
  1107.       fclose (fp);
  1108.     }
  1109.   else /* Error writing thumbnail */
  1110.     {
  1111.       return FALSE;
  1112.     }
  1113.  
  1114.   return TRUE;
  1115. }
  1116.  
  1117. static gint
  1118. file_save (GimpImage   *gimage,
  1119.        gchar       *filename,
  1120.        gchar       *raw_filename,
  1121.            RunModeType  run_mode)
  1122. {
  1123.   PlugInProcDef *file_proc;
  1124.   ProcRecord *proc;
  1125.   Argument   *args;
  1126.   Argument   *return_vals;
  1127.   gint status;
  1128.   gint i;
  1129.   struct stat statbuf;
  1130.  
  1131.   if (gimage_active_drawable (gimage) == NULL)
  1132.     return PDB_EXECUTION_ERROR;
  1133.  
  1134.   file_proc = gimage_get_save_proc (gimage);
  1135.  
  1136.   if (!file_proc)
  1137.     file_proc = file_proc_find (save_procs, raw_filename);
  1138.  
  1139.   if (!file_proc)
  1140.     {
  1141.       g_message (_("Save failed.\n"
  1142.            "%s: Unknown file type."),
  1143.          filename);
  1144.  
  1145.       return PDB_CANCEL;  /* inhibits error messages by caller */
  1146.     }
  1147.  
  1148.   /* check if we are saving to a file */
  1149.   if (stat (filename, &statbuf) == 0)
  1150.     {
  1151.       uid_t euid;
  1152.       gid_t egid;
  1153.  
  1154.       if (! (statbuf.st_mode & S_IFREG))
  1155.         {
  1156.       g_message (_("Save failed.\n"
  1157.                "%s is not a regular file."),
  1158.              filename);
  1159.  
  1160.           return PDB_CANCEL;  /* inhibits error messages by caller */
  1161.         }
  1162.  
  1163.       euid = geteuid ();
  1164.       egid = getegid ();
  1165.  
  1166.       if (! ((statbuf.st_mode & S_IWUSR) ||
  1167.  
  1168.              ((statbuf.st_mode & S_IWGRP) &&
  1169.               (statbuf.st_uid != euid)) ||
  1170.  
  1171.              ((statbuf.st_mode & S_IWOTH) &&
  1172.               (statbuf.st_uid != euid) &&
  1173.               (statbuf.st_gid != egid))))
  1174.         {
  1175.       g_message (_("Save failed.\n"
  1176.                "%s: Permission denied."),
  1177.              filename);
  1178.  
  1179.           return PDB_CANCEL;  /* inhibits error messages by caller */
  1180.         }
  1181.     }
  1182.  
  1183.   /* ref the image, so it can't get deleted during save */
  1184.   gtk_object_ref (GTK_OBJECT (gimage));
  1185.  
  1186.   proc = &file_proc->db_info;
  1187.  
  1188.   args = g_new0 (Argument, proc->num_args);
  1189.  
  1190.   for (i = 0; i < proc->num_args; i++)
  1191.     args[i].arg_type = proc->args[i].arg_type;
  1192.  
  1193.   args[0].value.pdb_int     = run_mode;
  1194.   args[1].value.pdb_int     = pdb_image_to_id (gimage);
  1195.   args[2].value.pdb_int     = drawable_ID (gimage_active_drawable (gimage));
  1196.   args[3].value.pdb_pointer = filename;
  1197.   args[4].value.pdb_pointer = raw_filename;
  1198.  
  1199.   return_vals = procedural_db_execute (proc->name, args);
  1200.  
  1201.   status = return_vals[0].value.pdb_int;
  1202.  
  1203.   if (status == PDB_SUCCESS)
  1204.     {
  1205.       /*  set this image to clean  */
  1206.       gimage_clean_all (gimage);
  1207.  
  1208.       /* these calls must come before the call to gimage_set_filename */
  1209.       document_index_add (filename);
  1210.       menus_last_opened_add (filename);
  1211.  
  1212.       /*  use the same plug-in for this image next time  */
  1213.       /* DISABLED - gets stuck on first saved format... needs
  1214.      attention --Adam */
  1215.       /* gimage_set_save_proc(gimage, file_proc); */
  1216.  
  1217.       /* Write a thumbnail for the saved image, where appropriate */
  1218.       switch (thumbnail_mode)
  1219.     {
  1220.     case 0:
  1221.       break;
  1222.     default:
  1223.       {
  1224.         TempBuf *tempbuf;
  1225.  
  1226.         tempbuf = make_thumb_tempbuf (gimage);
  1227.         file_save_thumbnail (gimage, filename, tempbuf);
  1228.       }
  1229.     }
  1230.  
  1231.       /*  set the image title  */
  1232.       gimp_image_set_filename (gimage, filename);
  1233.       /* note: 'filename' may have been free'd by above call! */
  1234.     }
  1235.  
  1236.   g_free (return_vals);
  1237.   g_free (args);
  1238.  
  1239.   gtk_object_unref (GTK_OBJECT (gimage));
  1240.  
  1241.   return status;
  1242. }
  1243.  
  1244. /* The readXVThumb function source may be re-used under
  1245.    the XFree86-style license. <adam@gimp.org> */
  1246. guchar *
  1247. readXVThumb (const gchar  *fnam,
  1248.          gint         *w,
  1249.          gint         *h,
  1250.          gchar       **imginfo /* caller frees if != NULL */)
  1251. {
  1252.   FILE *fp;
  1253.   const gchar *P7_332 = "P7 332";
  1254.   gchar P7_buf[7];
  1255.   gchar linebuf[200];
  1256.   guchar *buf;
  1257.   gint twofivefive;
  1258.   void *ptr;
  1259.  
  1260.   *w = *h = 0;
  1261.   *imginfo = NULL;
  1262.  
  1263.   fp = fopen (fnam, "rb");
  1264.   if (!fp)
  1265.     return NULL;
  1266.  
  1267.   fread (P7_buf, 6, 1, fp);
  1268.  
  1269.   if (strncmp(P7_buf, P7_332, 6)!=0)
  1270.     {
  1271.       g_warning ("Thumbnail doesn't have the 'P7 332' header.");
  1272.       fclose (fp);
  1273.       return NULL;
  1274.     }
  1275.  
  1276.   /*newline*/
  1277.   fread (P7_buf, 1, 1, fp);
  1278.  
  1279.   do
  1280.     {
  1281.       ptr = fgets(linebuf, 199, fp);
  1282.       if ((strncmp(linebuf, "#IMGINFO:", 9) == 0) &&
  1283.       (linebuf[9] != '\0') &&
  1284.       (linebuf[9] != '\n'))
  1285.     {
  1286.       if (linebuf[strlen(linebuf)-1] == '\n')
  1287.         linebuf[strlen(linebuf)-1] = '\0';
  1288.  
  1289.       if (linebuf[9] != '\0')
  1290.         {
  1291.           if (*imginfo)
  1292.         g_free(*imginfo);
  1293.           *imginfo = g_strdup (&linebuf[9]);
  1294.         }
  1295.     }
  1296.     }
  1297.   while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */
  1298.  
  1299.   if (!ptr)
  1300.     {
  1301.       /* g_warning("Thumbnail ended - not an image?"); */
  1302.       fclose (fp);
  1303.       return NULL;
  1304.     }
  1305.  
  1306.   sscanf(linebuf, "%d %d %d\n", w, h, &twofivefive);
  1307.  
  1308.   if (twofivefive!=255)
  1309.     {
  1310.       g_warning ("Thumbnail is of funky depth.");
  1311.       fclose (fp);
  1312.       return NULL;
  1313.     }
  1314.  
  1315.   if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60)
  1316.     {
  1317.       g_warning ("Thumbnail size bad.  Corrupted?");
  1318.       fclose (fp);
  1319.       return NULL;
  1320.     }
  1321.  
  1322.   buf = g_malloc ((*w) * (*h));
  1323.  
  1324.   fread (buf, (*w) * (*h), 1, fp);
  1325.  
  1326.   fclose (fp);
  1327.   
  1328.   return buf;
  1329. }
  1330.  
  1331. /* don't call with preview_fullname as parameter!  will be clobbered! */
  1332. static void
  1333. set_preview (const gchar *fullfname,
  1334.          guchar      *RGB_source,
  1335.          gint         RGB_w,
  1336.          gint         RGB_h)
  1337. {
  1338.   guchar *thumb_rgb;
  1339.   guchar *raw_thumb;
  1340.   gint    tnw,tnh, i;
  1341.   gchar  *pname;
  1342.   gchar  *fname;
  1343.   gchar  *tname;
  1344.   gchar  *imginfo = NULL;
  1345.   struct stat file_stat;
  1346.   struct stat thumb_stat;
  1347.   gboolean thumb_may_be_outdated = FALSE;
  1348.   gboolean show_generate_label = TRUE;
  1349.  
  1350.   pname = g_dirname (fullfname);
  1351.   fname = g_basename (fullfname); /* Don't free this! */
  1352.   tname = g_strconcat (pname, G_DIR_SEPARATOR_S,
  1353.                ".xvpics", G_DIR_SEPARATOR_S,
  1354.                fname, NULL);
  1355.  
  1356.   g_free (pname);
  1357.  
  1358.   /*  If the file is newer than its thumbnail, the thumbnail may
  1359.    *  be out of date.
  1360.    */
  1361.   if ((stat (tname,     &thumb_stat) == 0) &&
  1362.       (stat (fullfname, &file_stat ) == 0))
  1363.     {
  1364.       if ((thumb_stat.st_mtime) < (file_stat.st_mtime))
  1365.     {
  1366.       thumb_may_be_outdated = TRUE;
  1367.     }
  1368.     }
  1369.  
  1370.   raw_thumb = readXVThumb (tname, &tnw, &tnh, &imginfo);
  1371.  
  1372.   g_free (tname);
  1373.  
  1374.   gtk_frame_set_label (GTK_FRAME (open_options_frame), fname);
  1375.  
  1376.   g_free (preview_fullname);
  1377.   preview_fullname = g_strdup (fullfname);
  1378.  
  1379.   if (RGB_source)
  1380.     {
  1381.       gtk_preview_size (open_options_preview, RGB_w, RGB_h);
  1382.       
  1383.       for (i = 0; i < RGB_h; i++)
  1384.     {
  1385.       gtk_preview_draw_row (open_options_preview, &RGB_source[3*i*RGB_w],
  1386.                 0, i,
  1387.                 RGB_w);
  1388.     }
  1389.     }
  1390.   else
  1391.     {
  1392.       if (raw_thumb)
  1393.     {
  1394.       thumb_rgb = g_malloc (3 * tnw * tnh);
  1395.  
  1396.       for (i = 0; i < tnw * tnh; i++)
  1397.         {
  1398.           thumb_rgb[i*3  ] = ((raw_thumb[i]>>5)*255)/7;
  1399.           thumb_rgb[i*3+1] = (((raw_thumb[i]>>2)&7)*255)/7;
  1400.           thumb_rgb[i*3+2] = (((raw_thumb[i])&3)*255)/3;
  1401.         }
  1402.  
  1403.       gtk_preview_size (open_options_preview, tnw, tnh);
  1404.  
  1405.       for (i = 0; i < tnh; i++)
  1406.         {
  1407.           gtk_preview_draw_row (open_options_preview, &thumb_rgb[3*i*tnw],
  1408.                     0, i,
  1409.                     tnw);
  1410.         }
  1411.  
  1412.       g_free (thumb_rgb);
  1413.     }
  1414.     }
  1415.  
  1416.   if (raw_thumb || RGB_source)  /* We can show *some* kind of preview. */
  1417.     {
  1418.       if (raw_thumb) /* Managed to commit thumbnail file to disk */
  1419.     {
  1420.       gtk_label_set_text (GTK_LABEL (open_options_label),
  1421.                   thumb_may_be_outdated ?
  1422.                   _("(This thumbnail may be out of date)") :
  1423.                   (imginfo ? imginfo : _("(No Information)")));
  1424.       if (imginfo)
  1425.         g_free (imginfo);
  1426.     }
  1427.       else
  1428.     {
  1429.       switch (thumbnail_mode)
  1430.         {
  1431.         case 0:
  1432.           gtk_label_set_text (GTK_LABEL(open_options_label),
  1433.                   _("(Thumbnail saving is disabled)"));
  1434.           break;
  1435.         case 1:
  1436.           gtk_label_set_text (GTK_LABEL(open_options_label),
  1437.                   _("(Could not write thumbnail file)"));
  1438.           break;
  1439.         default:
  1440.           gtk_label_set_text (GTK_LABEL(open_options_label),
  1441.                   _("(Thumbnail file not written)"));
  1442.         }
  1443.     }
  1444.  
  1445.       gtk_widget_show (GTK_WIDGET (open_options_preview));
  1446.       gtk_widget_queue_draw (GTK_WIDGET(open_options_preview));
  1447.  
  1448.       show_generate_label = FALSE;
  1449.       
  1450.       g_free (raw_thumb);
  1451.     }
  1452.   else
  1453.     {
  1454.       if (imginfo)
  1455.     g_free (imginfo);
  1456.  
  1457.       gtk_widget_hide (GTK_WIDGET (open_options_preview));
  1458.       gtk_label_set_text (GTK_LABEL (open_options_label),
  1459.               _("No preview available"));
  1460.     }
  1461.  
  1462.   if (show_generate_label)
  1463.     {
  1464.       gtk_widget_hide (GTK_WIDGET (open_options_preview));
  1465.       gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel));
  1466.     }
  1467.   else
  1468.     {
  1469.       gtk_widget_hide (GTK_WIDGET (open_options_genbuttonlabel));
  1470.       gtk_widget_show (GTK_WIDGET (open_options_preview));
  1471.     }
  1472. }
  1473.  
  1474. static void
  1475. file_open_clistrow_callback (GtkWidget *widget,
  1476.                  gint       row)
  1477. {
  1478.   gchar *fullfname = NULL;
  1479.  
  1480.   fullfname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fileload));
  1481.  
  1482.   gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), TRUE);
  1483.   set_preview (fullfname, NULL, 0, 0);
  1484. }
  1485.  
  1486. static void
  1487. file_open_genbutton_callback (GtkWidget *widget,
  1488.                   gpointer   data)
  1489. {
  1490.   GimpImage *gimage_to_be_thumbed;
  1491.   guchar    *RGBbuf;
  1492.   TempBuf   *tempbuf;
  1493.   gint       RGBbuf_w;
  1494.   gint       RGBbuf_h;
  1495.  
  1496.   /* added for multi-file preview generation... */
  1497.   GtkFileSelection *fs;
  1498.   gchar *full_filename = NULL;
  1499.   gchar *filedirname;
  1500.   struct stat buf;
  1501.   gint err;
  1502.  
  1503.   fs = GTK_FILE_SELECTION (data);
  1504.  
  1505.   if (!preview_fullname)
  1506.     {
  1507.       g_warning ("Tried to generate thumbnail for NULL filename.");
  1508.       return;
  1509.     }
  1510.  
  1511.   gimp_add_busy_cursors ();
  1512.   gtk_widget_set_sensitive (GTK_WIDGET (fileload), FALSE);
  1513.  
  1514.   /* new mult-file preview make: */  
  1515.   {
  1516.     GSList *list, *toplist;
  1517.  
  1518.     /* Have to read the clist before touching anything else */
  1519.  
  1520.     list= clist_to_slist(GTK_CLIST(fs->file_list));
  1521.     toplist = list;
  1522.     
  1523.     /* Find a real base directory for the multiple selection */
  1524.  
  1525.     gtk_file_selection_set_filename (fs, "");
  1526.     filedirname= gtk_file_selection_get_filename (fs);
  1527.     if (filedirname[strlen (filedirname) - 1] == G_DIR_SEPARATOR)
  1528.       filedirname[strlen (filedirname) - 1] = '\0';
  1529.  
  1530.     while(list)
  1531.       {
  1532.         full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S,
  1533.                      (gchar *) list->data, NULL);
  1534.  
  1535.     err = stat (full_filename, &buf);
  1536.  
  1537.     if (! (err == 0 && (buf.st_mode & S_IFDIR)))
  1538.       { /* Is not directory. */
  1539.         gint dummy;
  1540.  
  1541.         gimage_to_be_thumbed = file_open_image (full_filename,
  1542.                             list->data,
  1543.                             NULL,
  1544.                             RUN_NONINTERACTIVE,
  1545.                             &dummy);
  1546.  
  1547.         if (gimage_to_be_thumbed)
  1548.           {            
  1549.         tempbuf = make_thumb_tempbuf (gimage_to_be_thumbed);
  1550.         RGBbuf  = make_RGBbuf_from_tempbuf (tempbuf,
  1551.                             &RGBbuf_w,
  1552.                             &RGBbuf_h);
  1553.         if (thumbnail_mode)
  1554.           {
  1555.             file_save_thumbnail (gimage_to_be_thumbed,
  1556.                          full_filename, tempbuf);
  1557.           }
  1558.         set_preview (full_filename, RGBbuf, RGBbuf_w, RGBbuf_h);
  1559.  
  1560.         gimage_delete (gimage_to_be_thumbed);
  1561.  
  1562.         if (RGBbuf)
  1563.           g_free (RGBbuf);
  1564.           }
  1565.         else
  1566.           {
  1567.         gtk_label_set_text (GTK_LABEL (open_options_label),
  1568.                     _("(could not make preview)"));
  1569.           }
  1570.       }
  1571.  
  1572.         g_free(full_filename);
  1573.         list= g_slist_next(list);
  1574.       }
  1575.     
  1576.     for (list = toplist; list; list = g_slist_next (list))
  1577.       {
  1578.     if (!(g_slist_next (list)))
  1579.       {
  1580.         full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S,
  1581.                      (gchar *) list->data, NULL);
  1582.             gtk_file_selection_set_filename (fs, full_filename);
  1583.         g_free (full_filename);
  1584.       }
  1585.  
  1586.         g_free (list->data);
  1587.       }
  1588.  
  1589.     g_slist_free (toplist);
  1590.     toplist = NULL;
  1591.   }
  1592.  
  1593.   gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE);
  1594.   gimp_remove_busy_cursors (NULL);
  1595. }
  1596.  
  1597. static void
  1598. file_open_ok_callback (GtkWidget *widget,
  1599.                gpointer   data)
  1600. {
  1601.   GtkFileSelection *fs;
  1602.   gchar *full_filename, *raw_filename;
  1603.   gchar *filedirname;
  1604.   struct stat buf;
  1605.   gint err;
  1606.   gint status;
  1607.  
  1608.   fs = GTK_FILE_SELECTION (data);
  1609.   full_filename = gtk_file_selection_get_filename (fs);
  1610.   raw_filename = gtk_entry_get_text (GTK_ENTRY(fs->selection_entry));
  1611.  
  1612.   g_assert (full_filename && raw_filename);
  1613.  
  1614.   if (strlen (raw_filename) == 0)
  1615.     return;
  1616.  
  1617.   err = stat (full_filename, &buf);
  1618.  
  1619.   if (err == 0 && (buf.st_mode & S_IFDIR))
  1620.     {
  1621.       if (full_filename[strlen (full_filename) - 1] != G_DIR_SEPARATOR)
  1622.     {
  1623.       gchar *s = g_strconcat (full_filename, G_DIR_SEPARATOR_S, NULL);
  1624.       gtk_file_selection_set_filename (fs, s);
  1625.       g_free (s);
  1626.     }
  1627.       else
  1628.     gtk_file_selection_set_filename (fs, full_filename);
  1629.  
  1630.       return;
  1631.     }
  1632.  
  1633.   gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE);
  1634.  
  1635.   if (err) /* e.g. http://server/filename.jpg */
  1636.     full_filename = raw_filename;
  1637.  
  1638.   status = file_open (full_filename, raw_filename);
  1639.  
  1640.   if (status == PDB_SUCCESS)
  1641.     {
  1642.       file_dialog_hide (data);
  1643.     }
  1644.   else if (status != PDB_CANCEL)
  1645.     {
  1646.       g_message (_("Open failed.\n%s"), full_filename);
  1647.     }
  1648.  
  1649.  
  1650.   /*
  1651.    * Now deal with multiple selections from the filesel clist
  1652.    */
  1653.  
  1654.   {
  1655.     GSList *list;
  1656.  
  1657.     /* Have to read the clist before touching anything else */
  1658.  
  1659.     list = clist_to_slist (GTK_CLIST (fs->file_list));
  1660.  
  1661.     /* Find a real base directory for the multiple selection */
  1662.  
  1663.     raw_filename = g_strdup(raw_filename);
  1664.     gtk_file_selection_set_filename (fs, "");
  1665.     filedirname = gtk_file_selection_get_filename (fs);
  1666.  
  1667.     while (list)
  1668.       {
  1669.         full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S,
  1670.                      (gchar *) list->data, NULL);
  1671.  
  1672.         if (strcmp (list->data, raw_filename))
  1673.           { /* don't load current selection twice */
  1674.  
  1675.             err = stat (full_filename, &buf);
  1676.  
  1677.             if (! (err == 0 && (buf.st_mode & S_IFDIR)))
  1678.               { /* Is not directory. */
  1679.  
  1680.                 status = file_open (full_filename, (char *) list->data);
  1681.  
  1682.                 if (status == PDB_SUCCESS)
  1683.                   {
  1684.                     file_dialog_hide (data);
  1685.                   }
  1686.                 else if (status != PDB_CANCEL)
  1687.                   {
  1688.                     g_message (_("Open failed.\n%s"), full_filename);
  1689.                   }
  1690.               }
  1691.           }
  1692.      
  1693.         g_free (full_filename);
  1694.         g_free (list->data);
  1695.         list = g_slist_next (list);
  1696.       }
  1697.  
  1698.     g_slist_free (list);
  1699.     list = NULL;
  1700.   }
  1701.     
  1702.   gtk_file_selection_set_filename (fs, raw_filename);
  1703.   g_free (raw_filename);
  1704.   gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE);
  1705.  
  1706. }
  1707.  
  1708. static GSList *
  1709. clist_to_slist (GtkCList *file_list)
  1710. {
  1711.   GSList *list = NULL;
  1712.   GList  *row;
  1713.   gint    rownum;
  1714.   gchar  *temp;
  1715.   
  1716.   for (row = file_list->row_list, rownum = 0;
  1717.        row; 
  1718.        row = g_list_next (row), rownum++)
  1719.     {
  1720.       if (GTK_CLIST_ROW (row)->state == GTK_STATE_SELECTED)
  1721.         {
  1722.           if (gtk_clist_get_cell_type (file_list, rownum, 0) == GTK_CELL_TEXT)
  1723.             {
  1724.               gtk_clist_get_text (file_list, rownum, 0, &temp);
  1725.               list = g_slist_prepend (list, g_strdup (temp));
  1726.             }
  1727.         }
  1728.     }
  1729.  
  1730.   return list;
  1731. }
  1732.  
  1733.  
  1734. /* Set "gimage"s save handler to "save_proc", then save the image.  
  1735.  * Hide the dialog if all went well, otherwise make the user knows an
  1736.  * error happened and leave the dialog up.  Make sure it's sensitive.
  1737.  */
  1738. static void
  1739. file_save_with_proc (GImage *gimage,
  1740.              gchar *full_filename,
  1741.              gchar *raw_filename,
  1742.              PlugInProcDef *save_proc)
  1743. {
  1744.     gint status = PDB_EXECUTION_ERROR;
  1745.  
  1746.     if (gimage != NULL)
  1747.       {
  1748.     gimage_set_save_proc (gimage, save_proc);
  1749.     status = file_save (gimage,
  1750.                 full_filename,
  1751.                 raw_filename,
  1752.                 RUN_INTERACTIVE);
  1753.  
  1754.     /* hide the file save dialog on success */
  1755.     if (status == PDB_SUCCESS)
  1756.       file_dialog_hide (filesave);
  1757.       }
  1758.  
  1759.     /* If there was an error but file_save() didn't print an error
  1760.      * message, then we'd better. */
  1761.     if (status != PDB_SUCCESS && status != PDB_CANCEL)
  1762.       g_message (_("Save failed.\n%s"), full_filename);
  1763.      
  1764.     /* always make file save dialog sensitive */
  1765.     gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE);
  1766. }
  1767.  
  1768.  
  1769. static void
  1770. file_save_ok_callback (GtkWidget *widget,
  1771.                gpointer   data)
  1772. {
  1773.   GtkFileSelection *fs;
  1774.   gchar       *filename;
  1775.   gchar       *raw_filename;
  1776.   gchar       *dot;
  1777.   gint         x;
  1778.   struct stat  buf;
  1779.   gint         err;
  1780.  
  1781.   fs = GTK_FILE_SELECTION (data);
  1782.   filename = gtk_file_selection_get_filename (fs);
  1783.   raw_filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
  1784.  
  1785.   g_assert (filename && raw_filename);
  1786.  
  1787.   for (dot = strrchr (filename, '.'), x = 0; dot && *(++dot);)
  1788.     {
  1789.       if (*dot != 'e' || ++x < 0)
  1790.     break;
  1791.       else if (x > 3 && !strcmp (dot + 1, "k"))
  1792.     { 
  1793.       ProcRecord   *proc_rec;
  1794.       Argument     *args;
  1795.       GimpDrawable *the_drawable;
  1796.  
  1797.       the_drawable = gimp_image_active_drawable (the_gimage);
  1798.       if (!the_drawable)
  1799.         return;
  1800.  
  1801.       proc_rec = procedural_db_lookup ("plug_in_the_slimy_egg");
  1802.       if (!proc_rec)
  1803.         break;
  1804.  
  1805.       file_dialog_hide (filesave);
  1806.  
  1807.       args = g_new (Argument, 3);
  1808.       args[0].arg_type      = PDB_INT32;
  1809.       args[0].value.pdb_int = RUN_INTERACTIVE;
  1810.       args[1].arg_type      = PDB_IMAGE;
  1811.       args[1].value.pdb_int = pdb_image_to_id (the_gimage);
  1812.       args[2].arg_type      = PDB_DRAWABLE;
  1813.       args[2].value.pdb_int = the_drawable->ID;
  1814.  
  1815.       plug_in_run (proc_rec, args, 3, FALSE, TRUE, 0);
  1816.  
  1817.       g_free (args);
  1818.       
  1819.       return;
  1820.     }
  1821.    }
  1822.  
  1823.   err = stat (filename, &buf);
  1824.  
  1825.   if (err == 0)
  1826.     {
  1827.       if (buf.st_mode & S_IFDIR)
  1828.     {
  1829.       if (filename[strlen (filename) - 1] != G_DIR_SEPARATOR)
  1830.         {
  1831.           gchar *s = g_strconcat (filename, G_DIR_SEPARATOR_S, NULL);
  1832.           gtk_file_selection_set_filename (fs, s);
  1833.           g_free (s);
  1834.         }
  1835.       else
  1836.         gtk_file_selection_set_filename (fs, filename);
  1837.     }
  1838.       else
  1839.     {
  1840.       gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE);
  1841.       file_overwrite (g_strdup (filename), g_strdup (raw_filename));
  1842.     }
  1843.     }
  1844.   else
  1845.     {
  1846.       gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE);
  1847.  
  1848.       file_save_with_proc (the_gimage, filename, raw_filename, save_file_proc);
  1849.  
  1850.       gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE);
  1851.     }
  1852. }
  1853.  
  1854. static void
  1855. file_dialog_show (GtkWidget *filesel)
  1856. {
  1857.   menus_set_sensitive ("<Toolbox>/File/Open...", FALSE);
  1858.   menus_set_sensitive ("<Image>/File/Open...", FALSE);
  1859.   menus_set_sensitive ("<Image>/File/Save", FALSE);
  1860.   menus_set_sensitive ("<Image>/File/Save As...", FALSE);
  1861.  
  1862.   gtk_widget_grab_focus (GTK_FILE_SELECTION (filesel)->selection_entry);
  1863.   gtk_widget_show (filesel);
  1864. }
  1865.  
  1866. static int
  1867. file_dialog_hide (GtkWidget *filesel)
  1868. {
  1869.   gimp_dialog_hide (filesel);
  1870.   
  1871.   menus_set_sensitive ("<Toolbox>/File/Open...", TRUE);
  1872.   menus_set_sensitive ("<Image>/File/Open...", TRUE);
  1873.  
  1874.   if (gdisplay_active ())
  1875.     {
  1876.       menus_set_sensitive ("<Image>/File/Save", TRUE);
  1877.       menus_set_sensitive ("<Image>/File/Save As...", TRUE);
  1878.     }
  1879.  
  1880.   return TRUE;
  1881. }
  1882.  
  1883. static void
  1884. file_overwrite (gchar *filename,
  1885.         gchar *raw_filename)
  1886. {
  1887.   OverwriteData *overwrite_data;
  1888.   GtkWidget *query_box;
  1889.   gchar     *overwrite_text;
  1890.  
  1891.   overwrite_data = g_new (OverwriteData, 1);
  1892.   overwrite_data->full_filename = filename;
  1893.   overwrite_data->raw_filename  = raw_filename;
  1894.  
  1895.   overwrite_text = g_strdup_printf (_("%s exists, overwrite?"), filename);
  1896.  
  1897.   query_box = gimp_query_boolean_box (_("File Exists!"),
  1898.                       gimp_standard_help_func,
  1899.                       "save/file_exists.html",
  1900.                       FALSE,
  1901.                       overwrite_text,
  1902.                       _("Yes"), _("No"),
  1903.                       NULL, NULL,
  1904.                       file_overwrite_callback,
  1905.                       overwrite_data);
  1906.  
  1907.   g_free (overwrite_text);
  1908.  
  1909.   gtk_widget_show (query_box);
  1910. }
  1911.  
  1912. static void
  1913. file_overwrite_callback (GtkWidget *widget,
  1914.              gboolean   overwrite,
  1915.              gpointer   data)
  1916. {
  1917.   OverwriteData *overwrite_data;
  1918.  
  1919.   overwrite_data = (OverwriteData *) data;
  1920.  
  1921.   if (overwrite)
  1922.     {
  1923.       file_save_with_proc (the_gimage,
  1924.                overwrite_data->full_filename,
  1925.                overwrite_data->raw_filename,
  1926.                save_file_proc);
  1927.     }
  1928.  
  1929.   /* always make file save dialog sensitive */
  1930.   gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE);
  1931.  
  1932.   g_free (overwrite_data->full_filename);
  1933.   g_free (overwrite_data->raw_filename);
  1934.   g_free (overwrite_data);
  1935. }
  1936.  
  1937. static void
  1938. file_revert_confirm_callback (GtkWidget *widget,
  1939.                   gboolean   revert,
  1940.                   gpointer   data)
  1941. {
  1942.   GimpImage *old_gimage;
  1943.  
  1944.   old_gimage = (GimpImage *) data;
  1945.  
  1946.   gtk_object_set_data (GTK_OBJECT (old_gimage), REVERT_DATA_KEY, NULL);
  1947.  
  1948.   if (revert)
  1949.     {
  1950.       GimpImage *new_gimage;
  1951.       gchar     *filename;
  1952.       gint       status;
  1953.  
  1954.       filename = gimage_filename (old_gimage);
  1955.  
  1956.       new_gimage = file_open_image (filename, filename, _("Revert"),
  1957.                     RUN_INTERACTIVE, &status);
  1958.  
  1959.       if (new_gimage != NULL)
  1960.     {
  1961.       undo_free (new_gimage);
  1962.       gdisplays_reconnect (old_gimage, new_gimage);
  1963.       gimp_image_clean_all (new_gimage);
  1964.     }
  1965.       else if (status != PDB_CANCEL)
  1966.     {
  1967.       g_message (_("Revert failed.\n%s"), filename);
  1968.     }
  1969.     }
  1970. }
  1971.  
  1972. static PlugInProcDef *
  1973. file_proc_find_by_name (GSList   *procs,
  1974.                 gchar    *filename,
  1975.                 gboolean  skip_magic)
  1976. {
  1977.   GSList *p;
  1978.   gchar  *ext = strrchr (filename, '.');
  1979.  
  1980.   if (ext)
  1981.     ext++;
  1982.  
  1983.   for (p = procs; p; p = g_slist_next (p))
  1984.     {
  1985.       PlugInProcDef *proc = p->data;
  1986.       GSList        *prefixes;
  1987.  
  1988.       if (skip_magic && proc->magics_list)
  1989.     continue;
  1990.  
  1991.       for (prefixes = proc->prefixes_list;
  1992.        prefixes;
  1993.        prefixes = g_slist_next (prefixes))
  1994.     {
  1995.       if (strncmp (filename, prefixes->data, strlen (prefixes->data)) == 0)
  1996.         return proc;
  1997.     }
  1998.      }
  1999.  
  2000.   for (p = procs; p; p = g_slist_next (p))
  2001.     {
  2002.       PlugInProcDef *proc = p->data;
  2003.       GSList        *extensions;
  2004.  
  2005.       for (extensions = proc->extensions_list;
  2006.        ext && extensions;
  2007.        extensions = g_slist_next (extensions))
  2008.     {
  2009.       gchar *p1 = ext;
  2010.       gchar *p2 = (gchar *) extensions->data;
  2011.  
  2012.           if (skip_magic && proc->magics_list)
  2013.         continue;
  2014.  
  2015.       while (*p1 && *p2)
  2016.         {
  2017.           if (tolower (*p1) != tolower (*p2))
  2018.         break;
  2019.  
  2020.           p1++;
  2021.           p2++;
  2022.         }
  2023.  
  2024.       if (!(*p1) && !(*p2))
  2025.         return proc;
  2026.     }
  2027.     }
  2028.  
  2029.   return NULL;
  2030. }
  2031.  
  2032. PlugInProcDef *
  2033. file_proc_find (GSList *procs,
  2034.         gchar  *filename)
  2035. {
  2036.   PlugInProcDef *file_proc;
  2037.   PlugInProcDef *size_matched_proc;
  2038.   GSList *all_procs = procs;
  2039.   FILE   *ifp = NULL;
  2040.   gint    head_size        = -2;
  2041.   gint    size_match_count = 0;
  2042.   gint    match_val;
  2043.   guchar  head[256];
  2044.  
  2045.   size_matched_proc = NULL;
  2046.  
  2047.   /* First, check magicless prefixes/suffixes */
  2048.   if ( (file_proc = file_proc_find_by_name (all_procs, filename, TRUE)) != NULL)
  2049.     return file_proc;
  2050.  
  2051.   /* Then look for magics */
  2052.   while (procs)
  2053.     {
  2054.       file_proc = procs->data;
  2055.       procs = procs->next;
  2056.  
  2057.       if (file_proc->magics_list)
  2058.         {
  2059.           if (head_size == -2)
  2060.             {
  2061.               head_size = 0;
  2062.               if ((ifp = fopen (filename, "rb")) != NULL)
  2063.                 head_size = fread ((gchar *) head, 1, sizeof (head), ifp);
  2064.             }
  2065.           if (head_size >= 4)
  2066.             {
  2067.               match_val = file_check_magic_list (file_proc->magics_list,
  2068.                                                  head_size, head, ifp);
  2069.               if (match_val == 2)  /* size match ? */
  2070.                 { /* Use it only if no other magic matches */
  2071.                   size_match_count++;
  2072.                   size_matched_proc = file_proc;
  2073.                 }
  2074.               else if (match_val)
  2075.                 {
  2076.                   fclose (ifp);
  2077.                   return (file_proc);
  2078.                 }
  2079.             }
  2080.         }
  2081.     }
  2082.   if (ifp) fclose (ifp);
  2083.   if (size_match_count == 1)
  2084.     return (size_matched_proc);
  2085.  
  2086.   /* As a last ditch, try matching by name */
  2087.   return file_proc_find_by_name (all_procs, filename, FALSE);
  2088. }
  2089.  
  2090. static void
  2091. file_convert_string (gchar *instr,
  2092.              gchar *outmem,
  2093.              gint   maxmem,
  2094.              gint  *nmem)
  2095. {
  2096.   /* Convert a string in C-notation to array of char */
  2097.   guchar *uin = (guchar *) instr;
  2098.   guchar *uout = (guchar *) outmem;
  2099.   guchar  tmp[5], *tmpptr;
  2100.   gint    k;
  2101.  
  2102.   while ((*uin != '\0') && ((((char *)uout) - outmem) < maxmem))
  2103.     {
  2104.       if (*uin != '\\')   /* Not an escaped character ? */
  2105.         {
  2106.           *(uout++) = *(uin++);
  2107.           continue;
  2108.         }
  2109.       if (*(++uin) == '\0')
  2110.         {
  2111.           *(uout++) = '\\';
  2112.           break;
  2113.         }
  2114.       switch (*uin)
  2115.         {
  2116.           case '0':  case '1':  case '2':  case '3': /* octal */
  2117.             for (tmpptr = tmp; (tmpptr-tmp) <= 3;)
  2118.               {
  2119.                 *(tmpptr++) = *(uin++);
  2120.                 if (   (*uin == '\0') || (!isdigit (*uin))
  2121.                     || (*uin == '8') || (*uin == '9'))
  2122.                   break;
  2123.               }
  2124.             *tmpptr = '\0';
  2125.             sscanf ((char *)tmp, "%o", &k);
  2126.             *(uout++) = k;
  2127.             break;
  2128.  
  2129.           case 'a': *(uout++) = '\a'; uin++; break;
  2130.           case 'b': *(uout++) = '\b'; uin++; break;
  2131.           case 't': *(uout++) = '\t'; uin++; break;
  2132.           case 'n': *(uout++) = '\n'; uin++; break;
  2133.           case 'v': *(uout++) = '\v'; uin++; break;
  2134.           case 'f': *(uout++) = '\f'; uin++; break;
  2135.           case 'r': *(uout++) = '\r'; uin++; break;
  2136.  
  2137.           default : *(uout++) = *(uin++); break;
  2138.         }
  2139.     }
  2140.   *nmem = ((gchar *) uout) - outmem;
  2141. }
  2142.  
  2143. static gchar *
  2144. file_absolute_filename (gchar *name)
  2145. {
  2146.   PlugInProcDef *proc;
  2147.   GSList *procs;
  2148.   GSList *prefixes;
  2149.   gchar  *absolute;
  2150.   gchar  *current;
  2151.  
  2152.   g_return_val_if_fail (name != NULL, NULL);
  2153.  
  2154.   /*  check for prefixes like http or ftp  */
  2155.   for (procs = load_procs; procs; procs = g_slist_next (procs))
  2156.     {
  2157.       proc = (PlugInProcDef *)procs->data;
  2158.  
  2159.       for (prefixes = proc->prefixes_list;
  2160.        prefixes;
  2161.        prefixes = g_slist_next (prefixes))
  2162.     {
  2163.       if (strncmp (name, prefixes->data, strlen (prefixes->data)) == 0)
  2164.         return g_strdup (name);
  2165.     }
  2166.      }
  2167.  
  2168.   if (g_path_is_absolute (name))
  2169.     return g_strdup (name);
  2170.   
  2171.   current = g_get_current_dir ();
  2172.   absolute = g_strconcat (current, G_DIR_SEPARATOR_S, name, NULL);
  2173.   g_free (current);
  2174.  
  2175.   return absolute;
  2176. }
  2177.  
  2178. static gint
  2179. file_check_single_magic (gchar  *offset,
  2180.                          gchar  *type,
  2181.                          gchar  *value,
  2182.                          gint    headsize,
  2183.                          guchar *file_head,
  2184.                          FILE   *ifp)
  2185.  
  2186. {
  2187.   /* Return values are 0: no match, 1: magic match, 2: size match */
  2188.   glong   offs;
  2189.   gulong  num_testval, num_operatorval;
  2190.   gulong  fileval;
  2191.   gint    numbytes, k, c = 0, found = 0;
  2192.   gchar  *num_operator_ptr, num_operator, num_test;
  2193.   guchar  mem_testval[256];
  2194.  
  2195.   /* Check offset */
  2196.   if (sscanf (offset, "%ld", &offs) != 1) return (0);
  2197.   if (offs < 0) return (0);
  2198.  
  2199.   /* Check type of test */
  2200.   num_operator_ptr = NULL;
  2201.   num_operator = '\0';
  2202.   num_test = '=';
  2203.   if (strncmp (type, "byte", 4) == 0)
  2204.     {
  2205.       numbytes = 1;
  2206.       num_operator_ptr = type+4;
  2207.     }
  2208.   else if (strncmp (type, "short", 5) == 0)
  2209.     {
  2210.       numbytes = 2;
  2211.       num_operator_ptr = type+5;
  2212.     }
  2213.   else if (strncmp (type, "long", 4) == 0)
  2214.     {
  2215.       numbytes = 4;
  2216.       num_operator_ptr = type+4;
  2217.     }
  2218.   else if (strncmp (type, "size", 4) == 0)
  2219.     {
  2220.       numbytes = 5;
  2221.     }
  2222.   else if (strcmp (type, "string") == 0)
  2223.     {
  2224.       numbytes = 0;
  2225.     }
  2226.   else return (0);
  2227.  
  2228.   /* Check numerical operator value if present */
  2229.   if (num_operator_ptr && (*num_operator_ptr == '&'))
  2230.     {
  2231.       if (isdigit (num_operator_ptr[1]))
  2232.         {
  2233.           if (num_operator_ptr[1] != '0')      /* decimal */
  2234.             sscanf (num_operator_ptr+1, "%ld", &num_operatorval);
  2235.           else if (num_operator_ptr[2] == 'x') /* hexadecimal */
  2236.             sscanf (num_operator_ptr+3, "%lx", &num_operatorval);
  2237.           else                                 /* octal */
  2238.             sscanf (num_operator_ptr+2, "%lo", &num_operatorval);
  2239.           num_operator = *num_operator_ptr;
  2240.         }
  2241.     }
  2242.  
  2243.   if (numbytes > 0)   /* Numerical test ? */
  2244.     {
  2245.       /* Check test value */
  2246.       if ((value[0] == '=') || (value[0] == '>') || (value[0] == '<'))
  2247.       {
  2248.         num_test = value[0];
  2249.         value++;
  2250.       }
  2251.       if (!isdigit (value[0])) return (0);
  2252.  
  2253.       /* 
  2254.        * to anybody reading this: is strtol's parsing behaviour (e.g. "0x" prefix)
  2255.        * broken on some systems or why do we do the base detection ourselves?
  2256.        * */
  2257.       if (value[0] != '0')      /* decimal */
  2258.         num_testval = strtol(value, NULL, 10);
  2259.       else if (value[1] == 'x') /* hexadecimal */
  2260.         num_testval = (unsigned long)strtoul(value+2, NULL, 16);
  2261.       else                      /* octal */
  2262.         num_testval = strtol(value+1, NULL, 8);
  2263.  
  2264.       fileval = 0;
  2265.       if (numbytes == 5)    /* Check for file size ? */
  2266.         {
  2267.       struct stat buf;
  2268.       
  2269.           if (fstat (fileno (ifp), &buf) < 0) return (0);
  2270.           fileval = buf.st_size;
  2271.         }
  2272.       else if (offs + numbytes <= headsize)  /* We have it in memory ? */
  2273.         {
  2274.           for (k = 0; k < numbytes; k++)
  2275.           fileval = (fileval << 8) | (long)file_head[offs+k];
  2276.         }
  2277.       else   /* Read it from file */
  2278.         {
  2279.           if (fseek (ifp, offs, SEEK_SET) < 0) return (0);
  2280.           for (k = 0; k < numbytes; k++)
  2281.             fileval = (fileval << 8) | (c = getc (ifp));
  2282.           if (c == EOF) return (0);
  2283.         }
  2284.       if (num_operator == '&')
  2285.         fileval &= num_operatorval;
  2286.  
  2287.       if (num_test == '<')
  2288.         found = (fileval < num_testval);
  2289.       else if (num_test == '>')
  2290.         found = (fileval > num_testval);
  2291.       else
  2292.         found = (fileval == num_testval);
  2293.  
  2294.       if (found && (numbytes == 5)) found = 2;
  2295.     }
  2296.   else if (numbytes == 0) /* String test */
  2297.     {
  2298.       file_convert_string ((char *)value, (char *)mem_testval,
  2299.                            sizeof (mem_testval), &numbytes);
  2300.       if (numbytes <= 0) return (0);
  2301.  
  2302.       if (offs + numbytes <= headsize)  /* We have it in memory ? */
  2303.         {
  2304.           found = (memcmp (mem_testval, file_head+offs, numbytes) == 0);
  2305.         }
  2306.       else   /* Read it from file */
  2307.         {
  2308.           if (fseek (ifp, offs, SEEK_SET) < 0) return (0);
  2309.           found = 1;
  2310.           for (k = 0; found && (k < numbytes); k++)
  2311.             {
  2312.               c = getc (ifp);
  2313.               found = (c != EOF) && (c == (int)mem_testval[k]);
  2314.             }
  2315.         }
  2316.     }
  2317.  
  2318.   return (found);
  2319. }
  2320.  
  2321. static int
  2322. file_check_magic_list (GSList *magics_list,
  2323.                gint    headsize,
  2324.                guchar *head,
  2325.                FILE   *ifp)
  2326.  
  2327. {
  2328.   /* Return values are 0: no match, 1: magic match, 2: size match */
  2329.   gchar *offset, *type, *value;
  2330.   gint and = 0;
  2331.   gint found = 0, match_val;
  2332.  
  2333.   while (magics_list)
  2334.     {
  2335.       if ((offset = (char *)magics_list->data) == NULL) break;
  2336.       if ((magics_list = magics_list->next) == NULL) break;
  2337.       if ((type = (char *)magics_list->data) == NULL) break;
  2338.       if ((magics_list = magics_list->next) == NULL) break;
  2339.       if ((value = (char *)magics_list->data) == NULL) break;
  2340.       magics_list = magics_list->next;
  2341.  
  2342.       match_val = file_check_single_magic (offset, type, value,
  2343.                                            headsize, head, ifp);
  2344.       if (and)
  2345.     found = found && match_val;
  2346.       else
  2347.     found = match_val;
  2348.  
  2349.       and = (strchr (offset, '&') != NULL);
  2350.       if ((!and) && found) return (match_val);
  2351.     }
  2352.   return (0);
  2353. }
  2354.  
  2355. static void
  2356. file_update_menus (GSList *procs,
  2357.            gint    image_type)
  2358. {
  2359.   PlugInProcDef *file_proc;
  2360.  
  2361.   while (procs)
  2362.     {
  2363.       file_proc = procs->data;
  2364.       procs = procs->next;
  2365.  
  2366.       if (file_proc->db_info.proc_type != PDB_EXTENSION)
  2367.     menus_set_sensitive (file_proc->menu_path,
  2368.                  (file_proc->image_types_val & image_type));
  2369.     }
  2370. }
  2371.