diff --git a/ChangeLog b/ChangeLog index 54897e9775..27afeab9e3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2001-03-30 Michael Natterer + + * app/fileops.[ch] + * app/fileopsP.h: removed... + + * app/file-open.[ch] + * app/file-save.[ch] + * app/file-utils.[ch]: ...and split up into more hackable chunks. + (didn't change any logic, just moved the functions around) + + * app/Makefile.am + * app/app_procs.c + * app/commands.c + * app/docindex.c + * app/gimpdnd.c + * app/gimprc.c + * app/menus.c + * app/pdb/fileops_cmds.c + * po/POTFILES.in + * tools/pdbgen/pdb/fileops.pdb: changed accordingly (mostly changing + #include's). + 2001-03-30 Michael Natterer * app/app_procs.[ch]: some random cleanups in pre-historic code. diff --git a/app/Makefile.am b/app/Makefile.am index 8d2a93a628..0f934815ca 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -86,9 +86,15 @@ gimp_SOURCES = \ errors.h \ equalize.c \ equalize.h \ - fileops.c \ - fileops.h \ - fileopsP.h \ +## fileops.c \ +## fileops.h \ +## fileopsP.h \ + file-open.c \ + file-open.h \ + file-save.c \ + file-save.h \ + file-utils.h \ + file-utils.c \ floating_sel.c \ floating_sel.h \ file_new_dialog.c \ diff --git a/app/actions/help-commands.c b/app/actions/help-commands.c index adb85f0a30..e062989745 100644 --- a/app/actions/help-commands.c +++ b/app/actions/help-commands.c @@ -48,7 +48,8 @@ #include "channel_ops.h" #include "equalize.h" #include "errorconsole.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "floating_sel.h" #include "gdisplay_ops.h" #include "gimage_mask.h" diff --git a/app/app_procs.c b/app/app_procs.c index 8c125fcf7a..7c2af9233f 100644 --- a/app/app_procs.c +++ b/app/app_procs.c @@ -53,7 +53,8 @@ #include "context_manager.h" #include "devices.h" #include "errorconsole.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "gdisplay.h" #include "gdisplay_ops.h" #include "gimpcontext.h" @@ -562,7 +563,9 @@ app_init (void) } RESET_BAR(); - file_ops_pre_init (); /* pre-initialize the file types */ + file_open_pre_init (); /* pre-initialize the file types */ + file_save_pre_init (); + RESET_BAR(); xcf_init (); /* initialize the xcf file format routines */ @@ -590,8 +593,10 @@ app_init (void) plug_in_init (); /* initialize the plug in structures */ module_db_init (); /* load any modules we need */ + RESET_BAR(); - file_ops_post_init (); /* post-initialize the file types */ + file_open_post_init (); /* post-initialize the file types */ + file_save_post_init (); menus_reorder_plugins (); /* beautify some menus */ diff --git a/app/commands.c b/app/commands.c index adb85f0a30..e062989745 100644 --- a/app/commands.c +++ b/app/commands.c @@ -48,7 +48,8 @@ #include "channel_ops.h" #include "equalize.h" #include "errorconsole.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "floating_sel.h" #include "gdisplay_ops.h" #include "gimage_mask.h" diff --git a/app/docindex.c b/app/docindex.c index c67218e9a4..d7ef9bb220 100644 --- a/app/docindex.c +++ b/app/docindex.c @@ -28,7 +28,7 @@ #include "dialog_handler.h" #include "docindex.h" -#include "fileops.h" +#include "file-open.h" #include "gdisplay.h" #include "gimpimage.h" #include "gimpdnd.h" diff --git a/app/file-open.c b/app/file-open.c new file mode 100644 index 0000000000..0687feeeed --- /dev/null +++ b/app/file-open.c @@ -0,0 +1,1115 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWGRP +#define S_IWGRP (_S_IWRITE>>3) +#define S_IWOTH (_S_IWRITE>>6) +#endif +#ifndef S_IRGRP +#define S_IRGRP (_S_IREAD>>3) +#define S_IROTH (_S_IREAD>>6) +#endif +#define uid_t gint +#define gid_t gint +#define geteuid() 0 +#define getegid() 0 +#endif + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "apptypes.h" + +#include "cursorutil.h" +#include "dialog_handler.h" +#include "docindex.h" +#include "gimpdrawable.h" +#include "gdisplay.h" +#include "gimage.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimpui.h" +#include "gimprc.h" +#include "file-open.h" +#include "file-utils.h" +#include "menus.h" +#include "plug_in.h" +#include "temp_buf.h" +#include "undo.h" + +#include "libgimp/gimpintl.h" + + +#define REVERT_DATA_KEY "revert_confirm_dialog" + + +static void file_open_dialog_create (void); + +static void file_revert_confirm_callback (GtkWidget *widget, + gboolean revert, + gpointer data); + +static GimpImage * file_open_image (const gchar *filename, + const gchar *raw_filename, + const gchar *open_mode, + RunModeType run_mode, + gint *status); + +static void file_open_genbutton_callback (GtkWidget *widget, + gpointer data); + +static void file_open_clistrow_callback (GtkWidget *widget, + gint row); + +static void file_open_ok_callback (GtkWidget *widget, + gpointer data); + +static void file_open_type_callback (GtkWidget *widget, + gpointer data); + +static gchar * file_absolute_filename (gchar *name); + +static GSList* clist_to_slist (GtkCList *file_list); + + + +static GtkWidget *fileload = NULL; +static GtkWidget *open_options = NULL; + +static GtkPreview *open_options_preview = NULL; +static GtkWidget *open_options_fixed = NULL; +static GtkWidget *open_options_label = NULL; +static GtkWidget *open_options_frame = NULL; +static GtkWidget *open_options_genbuttonlabel = NULL; + +/* Some state for the thumbnailer */ +static gchar *preview_fullname = NULL; + +GSList *load_procs = NULL; + +static PlugInProcDef *load_file_proc = NULL; + +extern GSList *display_list; /* from gdisplay.c */ + + +/* public functions */ + +void +file_open_pre_init (void) +{ +} + +void +file_open_post_init (void) +{ + GimpItemFactoryEntry entry; + PlugInProcDef *file_proc; + GSList *list; + + load_procs = g_slist_reverse (load_procs); + + for (list = load_procs; list; list = g_slist_next (list)) + { + gchar *help_page; + + file_proc = (PlugInProcDef *) list->data; + + help_page = g_strconcat ("filters/", + g_basename (file_proc->prog), + ".html", + NULL); + g_strdown (help_page); + + entry.entry.path = file_proc->menu_path; + entry.entry.accelerator = NULL; + entry.entry.callback = file_open_type_callback; + entry.entry.callback_action = 0; + entry.entry.item_type = NULL; + entry.help_page = help_page; + entry.description = NULL; + + menus_create_item_from_full_path (&entry, NULL, file_proc); + } +} + +void +file_open_callback (GtkWidget *widget, + gpointer data) +{ + if (!fileload) + file_open_dialog_create (); + + gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE); + if (GTK_WIDGET_VISIBLE (fileload)) + return; + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (fileload), + "." G_DIR_SEPARATOR_S); + gtk_window_set_title (GTK_WINDOW (fileload), _("Load Image")); + + file_dialog_show (fileload); +} + +void +file_revert_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + GimpImage *gimage; + GtkWidget *query_box; + const gchar *filename; + + gdisplay = gdisplay_active (); + if (!gdisplay || !gdisplay->gimage) + return; + + gimage = gdisplay->gimage; + + filename = gimp_object_get_name (GIMP_OBJECT (gimage)); + + query_box = gtk_object_get_data (GTK_OBJECT (gimage), REVERT_DATA_KEY); + + if (! filename) + { + g_message (_("Revert failed.\n" + "No filename associated with this image.")); + } + else if (query_box) + { + gdk_window_raise (query_box->window); + } + else + { + gchar *text; + + text = g_strdup_printf (_("Reverting %s to\n" + "%s\n\n" + "(You will lose all your changes\n" + "including all undo information)"), + g_basename (filename), + filename); + + query_box = gimp_query_boolean_box (_("Revert Image?"), + gimp_standard_help_func, + "file/revert.html", + FALSE, + text, + _("Yes"), _("No"), + GTK_OBJECT (gimage), "destroy", + file_revert_confirm_callback, + gimage); + + g_free (text); + + gtk_object_set_data (GTK_OBJECT (gimage), REVERT_DATA_KEY, query_box); + + gtk_widget_show (query_box); + } +} + +void +file_open_by_extension_callback (GtkWidget *widget, + gpointer data) +{ + load_file_proc = NULL; +} + + +/* private functions */ + +static void +file_open_dialog_create (void) +{ + fileload = gtk_file_selection_new (_("Load Image")); + gtk_window_set_position (GTK_WINDOW (fileload), GTK_WIN_POS_MOUSE); + gtk_window_set_wmclass (GTK_WINDOW (fileload), "load_image", "Gimp"); + + gtk_container_set_border_width (GTK_CONTAINER (fileload), 2); + gtk_container_set_border_width + (GTK_CONTAINER (GTK_FILE_SELECTION (fileload)->button_area), 2); + + dialog_register_fileload (fileload); + + gtk_signal_connect_object + (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->cancel_button), "clicked", + GTK_SIGNAL_FUNC (file_dialog_hide), + GTK_OBJECT (fileload)); + gtk_signal_connect (GTK_OBJECT (fileload), "delete_event", + GTK_SIGNAL_FUNC (file_dialog_hide), + NULL); + gtk_signal_connect + (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->ok_button), "clicked", + GTK_SIGNAL_FUNC (file_open_ok_callback), + fileload); + gtk_quit_add_destroy (1, GTK_OBJECT (fileload)); + + gtk_clist_set_selection_mode + (GTK_CLIST (GTK_FILE_SELECTION (fileload)->file_list), + GTK_SELECTION_EXTENDED); + + /* Catch file-clist clicks so we can update the preview thumbnail */ + gtk_signal_connect + (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->file_list), "select_row", + GTK_SIGNAL_FUNC (file_open_clistrow_callback), + fileload); + + /* Connect the "F1" help key */ + gimp_help_connect_help_accel (fileload, + gimp_standard_help_func, + "open/dialogs/file_open.html"); + + { + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *option_menu; + GtkWidget *load_menu; + GtkWidget *open_options_genbutton; + + open_options = gtk_hbox_new (TRUE, 1); + + /* format-chooser frame */ + frame = gtk_frame_new (_("Determine File Type")); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start (GTK_BOX (open_options), frame, TRUE, TRUE, 4); + + vbox = gtk_vbox_new (FALSE, 2); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + option_menu = gtk_option_menu_new (); + gtk_box_pack_start (GTK_BOX (hbox), option_menu, FALSE, FALSE, 0); + gtk_widget_show (option_menu); + + menus_get_load_menu (&load_menu, NULL); + gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), load_menu); + + gtk_widget_show (vbox); + gtk_widget_show (frame); + + /* Preview frame */ + open_options_frame = frame = gtk_frame_new (""); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_end (GTK_BOX (open_options), frame, FALSE, TRUE, 4); + + vbox = gtk_vbox_new (FALSE, 2); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + hbox = gtk_hbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + open_options_genbutton = gtk_button_new (); + gtk_signal_connect (GTK_OBJECT (open_options_genbutton), "clicked", + GTK_SIGNAL_FUNC (file_open_genbutton_callback), + fileload); + gtk_box_pack_start (GTK_BOX (hbox), open_options_genbutton, + TRUE, FALSE, 0); + gtk_widget_show (open_options_genbutton); + + open_options_fixed = gtk_fixed_new (); + gtk_widget_set_usize (open_options_fixed, 80, 60); + gtk_container_add (GTK_CONTAINER (GTK_BIN (open_options_genbutton)), + open_options_fixed); + gtk_widget_show (open_options_fixed); + + { + GtkWidget* abox; + GtkWidget* sbox; + GtkWidget* align; + + sbox = gtk_vbox_new (TRUE, 0); + gtk_container_add (GTK_CONTAINER (open_options_fixed), sbox); + gtk_widget_show (sbox); + + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_widget_set_usize (align, 80, 60); + gtk_box_pack_start (GTK_BOX (sbox), align, FALSE, TRUE, 0); + gtk_widget_show (align); + + abox = gtk_hbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (align), abox); + gtk_widget_show (abox); + + open_options_preview = + GTK_PREVIEW (gtk_preview_new (GTK_PREVIEW_COLOR)); + gtk_box_pack_start (GTK_BOX (abox), GTK_WIDGET (open_options_preview), + FALSE, TRUE, 0); + gtk_widget_show (GTK_WIDGET (open_options_preview)); + + open_options_genbuttonlabel = gtk_label_new (_("Generate\nPreview")); + gtk_box_pack_start (GTK_BOX (abox), open_options_genbuttonlabel, + FALSE, TRUE, 0); + gtk_widget_show (open_options_genbuttonlabel); + } + + open_options_label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX (vbox), open_options_label, FALSE, FALSE, 0); + gtk_widget_show (open_options_label); + + gtk_widget_show (vbox); + gtk_widget_show (frame); + + /* pack the containing open_options hbox into the open-dialog */ + gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (fileload)->main_vbox), + open_options, FALSE, FALSE, 0); + } + + gtk_frame_set_label (GTK_FRAME (open_options_frame), _("Preview")); + gtk_label_set_text (GTK_LABEL (open_options_label), _("No Selection.")); + + gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel)); + gtk_widget_hide (GTK_WIDGET (open_options_preview)); + gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), FALSE); + + gtk_widget_show (open_options); +} + +static void +file_open_type_callback (GtkWidget *widget, + gpointer data) +{ + PlugInProcDef *proc = (PlugInProcDef *) data; + + file_update_name (proc, fileload); + + load_file_proc = proc; +} + +static GimpImage * +file_open_image (const gchar *filename, + const gchar *raw_filename, + const gchar *open_mode, + RunModeType run_mode, + gint *status) +{ + PlugInProcDef *file_proc; + ProcRecord *proc; + Argument *args; + Argument *return_vals; + gint gimage_id; + gint i; + struct stat statbuf; + + *status = PDB_CANCEL; /* inhibits error messages by caller */ + + file_proc = load_file_proc; + + if (!file_proc) + file_proc = file_proc_find (load_procs, filename); + + if (!file_proc) + { + /* no errors when making thumbnails */ + if (run_mode == RUN_INTERACTIVE) + g_message (_("%s failed.\n" + "%s: Unknown file type."), + open_mode, filename); + + return NULL; + } + + /* check if we are opening a file */ + if (stat (filename, &statbuf) == 0) + { + uid_t euid; + gid_t egid; + + if (! (statbuf.st_mode & S_IFREG)) + { + /* no errors when making thumbnails */ + if (run_mode == RUN_INTERACTIVE) + g_message (_("%s failed.\n" + "%s is not a regular file."), + open_mode, filename); + + return NULL; + } + + euid = geteuid (); + egid = getegid (); + + if (! ((statbuf.st_mode & S_IRUSR) || + + ((statbuf.st_mode & S_IRGRP) && + (statbuf.st_uid != euid)) || + + ((statbuf.st_mode & S_IROTH) && + (statbuf.st_uid != euid) && + (statbuf.st_gid != egid)))) + { + /* no errors when making thumbnails */ + if (run_mode == RUN_INTERACTIVE) + g_message (_("%s failed.\n" + "%s: Permission denied."), + open_mode, filename); + + return NULL; + } + } + + proc = &file_proc->db_info; + + args = g_new0 (Argument, proc->num_args); + + for (i = 0; i < proc->num_args; i++) + args[i].arg_type = proc->args[i].arg_type; + + args[0].value.pdb_int = run_mode; + args[1].value.pdb_pointer = (gchar *) filename; + args[2].value.pdb_pointer = (gchar *) raw_filename; + + return_vals = procedural_db_execute (proc->name, args); + + *status = return_vals[0].value.pdb_int; + gimage_id = return_vals[1].value.pdb_int; + + procedural_db_destroy_args (return_vals, proc->num_values); + g_free (args); + + if (*status == PDB_SUCCESS && gimage_id != -1) + { + GimpImage *gimage = pdb_id_to_image (gimage_id); + + if (gimage) + { + gimp_image_invalidate_layer_previews (gimage); + gimp_image_invalidate_channel_previews (gimage); + } + + return pdb_id_to_image (gimage_id); + } + + return NULL; +} + +gint +file_open (gchar *filename, + gchar *raw_filename) +{ + GimpImage *gimage; + GDisplay *gdisplay; + gchar *absolute; + gint status; + + if ((gimage = file_open_image (filename, + raw_filename, + _("Open"), + RUN_INTERACTIVE, + &status)) != NULL) + { + /* enable & clear all undo steps */ + gimp_image_undo_enable (gimage); + + /* set the image to clean */ + gimp_image_clean_all (gimage); + + /* display the image */ + gdisplay = gdisplay_new (gimage, 0x0101); + + /* always activate the first display */ + if (g_slist_length (display_list) == 1) + gimp_context_set_display (gimp_context_get_user (), gdisplay); + + absolute = file_absolute_filename (filename); + document_index_add (absolute); + menus_last_opened_add (absolute); + g_free (absolute); + } + + return status; +} + +static guchar * +make_RGBbuf_from_tempbuf (TempBuf *tempbuf, + gint *width_rtn, + gint *height_rtn) +{ + gint i, j, w, h; + guchar *tbd; + guchar *ptr; + guchar *rtn = NULL; + guchar alpha, r, g, b; + + w = (*width_rtn) = tempbuf->width; + h = (*height_rtn) = tempbuf->height; + tbd = temp_buf_data (tempbuf); + + switch (tempbuf->bytes) + { + case 4: + rtn = ptr = g_malloc (3 * w * h); + for (i=0; i>5)*255)/7; + thumb_rgb[i*3+1] = (((raw_thumb[i]>>2)&7)*255)/7; + thumb_rgb[i*3+2] = (((raw_thumb[i])&3)*255)/3; + } + + gtk_preview_size (open_options_preview, tnw, tnh); + + for (i = 0; i < tnh; i++) + { + gtk_preview_draw_row (open_options_preview, &thumb_rgb[3*i*tnw], + 0, i, + tnw); + } + + g_free (thumb_rgb); + } + } + + if (raw_thumb || RGB_source) /* We can show *some* kind of preview. */ + { + if (raw_thumb) /* Managed to commit thumbnail file to disk */ + { + gtk_label_set_text (GTK_LABEL (open_options_label), + thumb_may_be_outdated ? + _("(This thumbnail may be out of date)") : + (imginfo ? imginfo : _("(No Information)"))); + if (imginfo) + g_free (imginfo); + } + else + { + switch (thumbnail_mode) + { + case 0: + gtk_label_set_text (GTK_LABEL(open_options_label), + _("(Thumbnail saving is disabled)")); + break; + case 1: + gtk_label_set_text (GTK_LABEL(open_options_label), + _("(Could not write thumbnail file)")); + break; + default: + gtk_label_set_text (GTK_LABEL(open_options_label), + _("(Thumbnail file not written)")); + } + } + + gtk_widget_show (GTK_WIDGET (open_options_preview)); + gtk_widget_queue_draw (GTK_WIDGET(open_options_preview)); + + show_generate_label = FALSE; + + g_free (raw_thumb); + } + else + { + if (imginfo) + g_free (imginfo); + + gtk_widget_hide (GTK_WIDGET (open_options_preview)); + gtk_label_set_text (GTK_LABEL (open_options_label), + _("No preview available")); + } + + if (show_generate_label) + { + gtk_widget_hide (GTK_WIDGET (open_options_preview)); + gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel)); + } + else + { + gtk_widget_hide (GTK_WIDGET (open_options_genbuttonlabel)); + gtk_widget_show (GTK_WIDGET (open_options_preview)); + } +} + +static void +file_open_clistrow_callback (GtkWidget *widget, + gint row) +{ + gchar *fullfname = NULL; + + fullfname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fileload)); + + gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), TRUE); + set_preview (fullfname, NULL, 0, 0); +} + +static void +file_open_genbutton_callback (GtkWidget *widget, + gpointer data) +{ + GimpImage *gimage_to_be_thumbed; + guchar *RGBbuf; + TempBuf *tempbuf; + gint RGBbuf_w; + gint RGBbuf_h; + + /* added for multi-file preview generation... */ + GtkFileSelection *fs; + gchar *full_filename = NULL; + gchar *filedirname; + struct stat buf; + gint err; + + fs = GTK_FILE_SELECTION (data); + + if (! preview_fullname) + { + g_warning ("Tried to generate thumbnail for NULL filename."); + return; + } + + gimp_add_busy_cursors (); + gtk_widget_set_sensitive (GTK_WIDGET (fileload), FALSE); + + /* new mult-file preview make: */ + { + GSList *list, *toplist; + + /* Have to read the clist before touching anything else */ + + list= clist_to_slist(GTK_CLIST(fs->file_list)); + toplist = list; + + /* Find a real base directory for the multiple selection */ + + gtk_file_selection_set_filename (fs, ""); + filedirname= gtk_file_selection_get_filename (fs); + if (filedirname[strlen (filedirname) - 1] == G_DIR_SEPARATOR) + filedirname[strlen (filedirname) - 1] = '\0'; + + while(list) + { + full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, + (gchar *) list->data, NULL); + + err = stat (full_filename, &buf); + + if (! (err == 0 && (buf.st_mode & S_IFDIR))) + { /* Is not directory. */ + gint dummy; + + gimage_to_be_thumbed = file_open_image (full_filename, + list->data, + NULL, + RUN_NONINTERACTIVE, + &dummy); + + if (gimage_to_be_thumbed) + { + tempbuf = make_thumb_tempbuf (gimage_to_be_thumbed); + RGBbuf = make_RGBbuf_from_tempbuf (tempbuf, + &RGBbuf_w, + &RGBbuf_h); + if (thumbnail_mode) + { + file_save_thumbnail (gimage_to_be_thumbed, + full_filename, tempbuf); + } + set_preview (full_filename, RGBbuf, RGBbuf_w, RGBbuf_h); + + gtk_object_unref (GTK_OBJECT (gimage_to_be_thumbed)); + + if (RGBbuf) + g_free (RGBbuf); + } + else + { + gtk_label_set_text (GTK_LABEL (open_options_label), + _("(could not make preview)")); + } + } + + g_free(full_filename); + list= g_slist_next(list); + } + + for (list = toplist; list; list = g_slist_next (list)) + { + if (!(g_slist_next (list))) + { + full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, + (gchar *) list->data, NULL); + gtk_file_selection_set_filename (fs, full_filename); + g_free (full_filename); + } + + g_free (list->data); + } + + g_slist_free (toplist); + toplist = NULL; + } + + gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE); + gimp_remove_busy_cursors (NULL); +} + +static void +file_open_ok_callback (GtkWidget *widget, + gpointer data) +{ + GtkFileSelection *fs; + gchar *full_filename; + gchar *raw_filename; + gchar *filedirname; + struct stat buf; + gint err; + gint status; + + fs = GTK_FILE_SELECTION (data); + full_filename = gtk_file_selection_get_filename (fs); + raw_filename = gtk_entry_get_text (GTK_ENTRY(fs->selection_entry)); + + g_assert (full_filename && raw_filename); + + if (strlen (raw_filename) == 0) + return; + + err = stat (full_filename, &buf); + + if (err == 0 && (buf.st_mode & S_IFDIR)) + { + if (full_filename[strlen (full_filename) - 1] != G_DIR_SEPARATOR) + { + gchar *s = g_strconcat (full_filename, G_DIR_SEPARATOR_S, NULL); + gtk_file_selection_set_filename (fs, s); + g_free (s); + } + else + gtk_file_selection_set_filename (fs, full_filename); + + return; + } + + gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); + + if (err) /* e.g. http://server/filename.jpg */ + full_filename = raw_filename; + + status = file_open (full_filename, raw_filename); + + if (status == PDB_SUCCESS) + { + file_dialog_hide (data); + } + else if (status != PDB_CANCEL) + { + g_message (_("Open failed.\n%s"), full_filename); + } + + + /* + * Now deal with multiple selections from the filesel clist + */ + + { + GSList *list; + + /* Have to read the clist before touching anything else */ + + list = clist_to_slist (GTK_CLIST (fs->file_list)); + + /* Find a real base directory for the multiple selection */ + + raw_filename = g_strdup(raw_filename); + gtk_file_selection_set_filename (fs, ""); + filedirname = gtk_file_selection_get_filename (fs); + + while (list) + { + full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, + (gchar *) list->data, NULL); + + if (strcmp (list->data, raw_filename)) + { /* don't load current selection twice */ + + err = stat (full_filename, &buf); + + if (! (err == 0 && (buf.st_mode & S_IFDIR))) + { /* Is not directory. */ + + status = file_open (full_filename, (char *) list->data); + + if (status == PDB_SUCCESS) + { + file_dialog_hide (data); + } + else if (status != PDB_CANCEL) + { + g_message (_("Open failed.\n%s"), full_filename); + } + } + } + + g_free (full_filename); + g_free (list->data); + list = g_slist_next (list); + } + + g_slist_free (list); + list = NULL; + } + + gtk_file_selection_set_filename (fs, raw_filename); + g_free (raw_filename); + gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE); + +} + +static GSList * +clist_to_slist (GtkCList *file_list) +{ + GSList *list = NULL; + GList *row; + gint rownum; + gchar *temp; + + for (row = file_list->row_list, rownum = 0; + row; + row = g_list_next (row), rownum++) + { + if (GTK_CLIST_ROW (row)->state == GTK_STATE_SELECTED) + { + if (gtk_clist_get_cell_type (file_list, rownum, 0) == GTK_CELL_TEXT) + { + gtk_clist_get_text (file_list, rownum, 0, &temp); + list = g_slist_prepend (list, g_strdup (temp)); + } + } + } + + return list; +} + +static void +file_revert_confirm_callback (GtkWidget *widget, + gboolean revert, + gpointer data) +{ + GimpImage *old_gimage; + + old_gimage = (GimpImage *) data; + + gtk_object_set_data (GTK_OBJECT (old_gimage), REVERT_DATA_KEY, NULL); + + if (revert) + { + GimpImage *new_gimage; + const gchar *filename; + gint status; + + filename = gimp_object_get_name (GIMP_OBJECT (old_gimage)); + + new_gimage = file_open_image (filename, filename, _("Revert"), + RUN_INTERACTIVE, &status); + + if (new_gimage != NULL) + { + undo_free (new_gimage); + gdisplays_reconnect (old_gimage, new_gimage); + gdisplays_resize_cursor_label (new_gimage); + gdisplays_update_full (new_gimage); + gdisplays_shrink_wrap (new_gimage); + gimp_image_clean_all (new_gimage); + } + else if (status != PDB_CANCEL) + { + g_message (_("Revert failed.\n%s"), filename); + } + } +} + +static gchar * +file_absolute_filename (gchar *name) +{ + PlugInProcDef *proc; + GSList *procs; + GSList *prefixes; + gchar *absolute; + gchar *current; + + g_return_val_if_fail (name != NULL, NULL); + + /* check for prefixes like http or ftp */ + for (procs = load_procs; procs; procs = g_slist_next (procs)) + { + proc = (PlugInProcDef *)procs->data; + + for (prefixes = proc->prefixes_list; + prefixes; + prefixes = g_slist_next (prefixes)) + { + if (strncmp (name, prefixes->data, strlen (prefixes->data)) == 0) + return g_strdup (name); + } + } + + if (g_path_is_absolute (name)) + return g_strdup (name); + + current = g_get_current_dir (); + absolute = g_strconcat (current, G_DIR_SEPARATOR_S, name, NULL); + g_free (current); + + return absolute; +} diff --git a/app/file-open.h b/app/file-open.h new file mode 100644 index 0000000000..74461468a9 --- /dev/null +++ b/app/file-open.h @@ -0,0 +1,42 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FILE_OPEN_H__ +#define __FILE_OPEN_H__ + + +extern GSList *load_procs; + + +void file_open_pre_init (void); +void file_open_post_init (void); + +void file_open_callback (GtkWidget *widget, + gpointer data); + +void file_revert_callback (GtkWidget *widget, + gpointer data); + +void file_open_by_extension_callback (GtkWidget *widget, + gpointer data); + +gint file_open (gchar *filename, + gchar *raw_filename); + + +#endif /* __FILE_OPEN_H__ */ diff --git a/app/file-save.c b/app/file-save.c new file mode 100644 index 0000000000..af3a777772 --- /dev/null +++ b/app/file-save.c @@ -0,0 +1,730 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWGRP +#define S_IWGRP (_S_IWRITE>>3) +#define S_IWOTH (_S_IWRITE>>6) +#endif +#ifndef S_IRGRP +#define S_IRGRP (_S_IREAD>>3) +#define S_IROTH (_S_IREAD>>6) +#endif +#define uid_t gint +#define gid_t gint +#define geteuid() 0 +#define getegid() 0 +#endif + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "apptypes.h" + +#include "cursorutil.h" +#include "dialog_handler.h" +#include "docindex.h" +#include "gimpdrawable.h" +#include "gdisplay.h" +#include "gimage.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimpui.h" +#include "gimprc.h" +#include "file-save.h" +#include "file-utils.h" +#include "menus.h" +#include "plug_in.h" +#include "temp_buf.h" +#include "undo.h" + +#include "libgimp/gimpintl.h" + + +typedef struct _OverwriteData OverwriteData; + +struct _OverwriteData +{ + gchar *full_filename; + gchar *raw_filename; +}; + + +static void file_save_dialog_create (void); + +static void file_overwrite (gchar *filename, + gchar *raw_filename); +static void file_overwrite_callback (GtkWidget *widget, + gboolean overwrite, + gpointer data); + +static gint file_save (GimpImage *gimage, + gchar *filename, + gchar *raw_filename, + RunModeType run_mode, + gboolean set_filename); + +static void file_save_ok_callback (GtkWidget *widget, + gpointer data); + +static void file_save_type_callback (GtkWidget *widget, + gpointer data); + + +static GtkWidget *filesave = NULL; +static GtkWidget *save_options = NULL; + +GSList *save_procs = NULL; + +static PlugInProcDef *save_file_proc = NULL; + +static GimpImage *the_gimage = NULL; +static gboolean set_filename = TRUE; + + +/* public functions */ + +void +file_save_pre_init (void) +{ +} + +void +file_save_post_init (void) +{ + GimpItemFactoryEntry entry; + PlugInProcDef *file_proc; + GSList *list; + + save_procs = g_slist_reverse (save_procs); + + for (list = save_procs; list; list = g_slist_next (list)) + { + gchar *help_page; + + file_proc = (PlugInProcDef *) list->data; + + help_page = g_strconcat ("filters/", + g_basename (file_proc->prog), + ".html", + NULL); + g_strdown (help_page); + + entry.entry.path = file_proc->menu_path; + entry.entry.accelerator = NULL; + entry.entry.callback = file_save_type_callback; + entry.entry.callback_action = 0; + entry.entry.item_type = NULL; + entry.help_page = help_page; + entry.description = NULL; + + menus_create_item_from_full_path (&entry, NULL, file_proc); + } +} + +void +file_save_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + + gdisplay = gdisplay_active (); + if (! gdisplay) + return; + + if (! gimp_image_active_drawable (gdisplay->gimage)) + return; + + /* Only save if the gimage has been modified */ + if (!trust_dirty_flag || gdisplay->gimage->dirty != 0) + { + gchar *filename; + + filename = + g_strdup (gimp_object_get_name (GIMP_OBJECT (gdisplay->gimage))); + + if (! filename) + { + file_save_as_callback (widget, data); + } + else + { + gchar *raw_filename; + gint status; + + raw_filename = g_basename (filename); + + status = file_save (gdisplay->gimage, + filename, + raw_filename, + RUN_WITH_LAST_VALS, + TRUE); + + if (status != PDB_SUCCESS && + status != PDB_CANCEL) + { + g_message (_("Save failed.\n%s"), filename); + } + + } + + g_free (filename); + } +} + +void +file_save_as_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + const gchar *filename; + + gdisplay = gdisplay_active (); + if (! gdisplay) + return; + + if (! gimp_image_active_drawable (gdisplay->gimage)) + return; + + the_gimage = gdisplay->gimage; + + set_filename = TRUE; + + filename = gimp_object_get_name (GIMP_OBJECT (the_gimage)); + + if (! filesave) + file_save_dialog_create (); + + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); + if (GTK_WIDGET_VISIBLE (filesave)) + return; + + gtk_window_set_title (GTK_WINDOW (filesave), _("Save Image")); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesave), + filename ? + filename : + "." G_DIR_SEPARATOR_S); + + switch (gimp_drawable_type (gimp_image_active_drawable (gdisplay->gimage))) + { + case RGB_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGB_IMAGE); + break; + case RGBA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE); + break; + case GRAY_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE); + break; + case GRAYA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE); + break; + case INDEXED_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE); + break; + case INDEXEDA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE); + break; + } + + file_dialog_show (filesave); +} + +void +file_save_a_copy_as_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + const gchar *filename; + + gdisplay = gdisplay_active (); + if (! gdisplay) + return; + + if (! gimp_image_active_drawable (gdisplay->gimage)) + return; + + the_gimage = gdisplay->gimage; + + set_filename = FALSE; + + filename = gimp_object_get_name (GIMP_OBJECT (the_gimage)); + + if (!filesave) + file_save_dialog_create (); + + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); + if (GTK_WIDGET_VISIBLE (filesave)) + return; + + gtk_window_set_title (GTK_WINDOW (filesave), _("Save a Copy of the Image")); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesave), + filename ? + filename : + "." G_DIR_SEPARATOR_S); + + switch (gimp_drawable_type (gimp_image_active_drawable (gdisplay->gimage))) + { + case RGB_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGB_IMAGE); + break; + case RGBA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE); + break; + case GRAY_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE); + break; + case GRAYA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE); + break; + case INDEXED_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE); + break; + case INDEXEDA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE); + break; + } + + file_dialog_show (filesave); +} + +void +file_save_by_extension_callback (GtkWidget *widget, + gpointer data) +{ + save_file_proc = NULL; +} + + +/* private functions */ + +static void +file_save_dialog_create (void) +{ + filesave = gtk_file_selection_new (_("Save Image")); + gtk_window_set_wmclass (GTK_WINDOW (filesave), "save_image", "Gimp"); + gtk_window_set_position (GTK_WINDOW (filesave), GTK_WIN_POS_MOUSE); + + gtk_container_set_border_width (GTK_CONTAINER (filesave), 2); + gtk_container_set_border_width + (GTK_CONTAINER (GTK_FILE_SELECTION (filesave)->button_area), 2); + + gtk_signal_connect_object + (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->cancel_button), "clicked", + GTK_SIGNAL_FUNC (file_dialog_hide), + GTK_OBJECT (filesave)); + gtk_signal_connect (GTK_OBJECT (filesave), "delete_event", + GTK_SIGNAL_FUNC (file_dialog_hide), + NULL); + gtk_signal_connect + (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->ok_button), "clicked", + GTK_SIGNAL_FUNC (file_save_ok_callback), + filesave); + gtk_quit_add_destroy (1, GTK_OBJECT (filesave)); + + /* Connect the "F1" help key */ + gimp_help_connect_help_accel (filesave, + gimp_standard_help_func, + "save/dialogs/file_save.html"); + + { + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *option_menu; + GtkWidget *save_menu; + + save_options = gtk_hbox_new (TRUE, 1); + + frame = gtk_frame_new (_("Save Options")); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start (GTK_BOX (save_options), frame, TRUE, TRUE, 4); + + hbox = gtk_hbox_new (FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); + gtk_container_add (GTK_CONTAINER (frame), hbox); + gtk_widget_show (hbox); + + label = gtk_label_new (_("Determine File Type:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + option_menu = gtk_option_menu_new (); + gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 0); + gtk_widget_show (option_menu); + + menus_get_save_menu (&save_menu, NULL); + gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), save_menu); + + gtk_widget_show (frame); + + /* pack the containing save_options hbox into the save-dialog */ + gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (filesave)->main_vbox), + save_options, FALSE, FALSE, 0); + } + + gtk_widget_show (save_options); +} + +static void +file_save_type_callback (GtkWidget *widget, + gpointer data) +{ + PlugInProcDef *proc = (PlugInProcDef *) data; + + file_update_name (proc, filesave); + + save_file_proc = proc; +} + +static gint +file_save (GimpImage *gimage, + gchar *filename, + gchar *raw_filename, + RunModeType run_mode, + gboolean set_filename) +{ + PlugInProcDef *file_proc; + ProcRecord *proc; + Argument *args; + Argument *return_vals; + gint status; + gint i; + struct stat statbuf; + + if (gimp_image_active_drawable (gimage) == NULL) + return PDB_EXECUTION_ERROR; + + file_proc = gimp_image_get_save_proc (gimage); + + if (!file_proc) + file_proc = file_proc_find (save_procs, raw_filename); + + if (!file_proc) + { + g_message (_("Save failed.\n" + "%s: Unknown file type."), + filename); + + return PDB_CANCEL; /* inhibits error messages by caller */ + } + + /* check if we are saving to a file */ + if (stat (filename, &statbuf) == 0) + { + uid_t euid; + gid_t egid; + + if (! (statbuf.st_mode & S_IFREG)) + { + g_message (_("Save failed.\n" + "%s is not a regular file."), + filename); + + return PDB_CANCEL; /* inhibits error messages by caller */ + } + + euid = geteuid (); + egid = getegid (); + + if (! ((statbuf.st_mode & S_IWUSR) || + + ((statbuf.st_mode & S_IWGRP) && + (statbuf.st_uid != euid)) || + + ((statbuf.st_mode & S_IWOTH) && + (statbuf.st_uid != euid) && + (statbuf.st_gid != egid)))) + { + g_message (_("Save failed.\n" + "%s: Permission denied."), + filename); + + return PDB_CANCEL; /* inhibits error messages by caller */ + } + } + + /* ref the image, so it can't get deleted during save */ + gtk_object_ref (GTK_OBJECT (gimage)); + + proc = &file_proc->db_info; + + args = g_new0 (Argument, proc->num_args); + + for (i = 0; i < proc->num_args; i++) + args[i].arg_type = proc->args[i].arg_type; + + args[0].value.pdb_int = run_mode; + args[1].value.pdb_int = pdb_image_to_id (gimage); + args[2].value.pdb_int = gimp_drawable_get_ID (gimp_image_active_drawable (gimage)); + args[3].value.pdb_pointer = filename; + args[4].value.pdb_pointer = raw_filename; + + return_vals = procedural_db_execute (proc->name, args); + + status = return_vals[0].value.pdb_int; + + if (status == PDB_SUCCESS) + { + /* set this image to clean */ + gimp_image_clean_all (gimage); + + /* these calls must come before the call to gimage_set_filename */ + document_index_add (filename); + menus_last_opened_add (filename); + + /* use the same plug-in for this image next time */ + /* DISABLED - gets stuck on first saved format... needs + attention --Adam */ + /* gimage_set_save_proc(gimage, file_proc); */ + + /* Write a thumbnail for the saved image, where appropriate */ + switch (thumbnail_mode) + { + case 0: + break; + default: + { + TempBuf *tempbuf; + + tempbuf = make_thumb_tempbuf (gimage); + file_save_thumbnail (gimage, filename, tempbuf); + } + } + + if (set_filename) + { + /* set the image title */ + gimp_object_set_name (GIMP_OBJECT (gimage), filename); + /* note: 'filename' may have been free'd by above call! */ + } + } + + g_free (return_vals); + g_free (args); + + gtk_object_unref (GTK_OBJECT (gimage)); + + return status; +} + +/* Set "gimage"s save handler to "save_proc", then save the image. + * Hide the dialog if all went well, otherwise make the user knows an + * error happened and leave the dialog up. Make sure it's sensitive. + */ +static void +file_save_with_proc (GimpImage *gimage, + gchar *full_filename, + gchar *raw_filename, + PlugInProcDef *save_proc, + gboolean set_filename) +{ + gint status = PDB_EXECUTION_ERROR; + + if (gimage != NULL) + { + gimp_image_set_save_proc (gimage, save_proc); + status = file_save (gimage, + full_filename, + raw_filename, + RUN_INTERACTIVE, + set_filename); + + /* hide the file save dialog on success */ + if (status == PDB_SUCCESS) + file_dialog_hide (filesave); + } + + /* If there was an error but file_save() didn't print an error + * message, then we'd better. */ + if (status != PDB_SUCCESS && status != PDB_CANCEL) + g_message (_("Save failed.\n%s"), full_filename); + + /* always make file save dialog sensitive */ + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); +} + + +static void +file_save_ok_callback (GtkWidget *widget, + gpointer data) +{ + GtkFileSelection *fs; + gchar *filename; + gchar *raw_filename; + gchar *dot; + gint x; + struct stat buf; + gint err; + + fs = GTK_FILE_SELECTION (data); + filename = gtk_file_selection_get_filename (fs); + raw_filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)); + + g_assert (filename && raw_filename); + + for (dot = strrchr (filename, '.'), x = 0; dot && *(++dot);) + { + if (*dot != 'e' || ++x < 0) + break; + else if (x > 3 && !strcmp (dot + 1, "k")) + { + ProcRecord *proc_rec; + Argument *args; + GimpDrawable *the_drawable; + + the_drawable = gimp_image_active_drawable (the_gimage); + if (!the_drawable) + return; + + proc_rec = procedural_db_lookup ("plug_in_the_slimy_egg"); + if (!proc_rec) + break; + + file_dialog_hide (filesave); + + args = g_new (Argument, 3); + args[0].arg_type = PDB_INT32; + args[0].value.pdb_int = RUN_INTERACTIVE; + args[1].arg_type = PDB_IMAGE; + args[1].value.pdb_int = pdb_image_to_id (the_gimage); + args[2].arg_type = PDB_DRAWABLE; + args[2].value.pdb_int = the_drawable->ID; + + plug_in_run (proc_rec, args, 3, FALSE, TRUE, 0); + + g_free (args); + + return; + } + } + + err = stat (filename, &buf); + + if (err == 0) + { + if (buf.st_mode & S_IFDIR) + { + if (filename[strlen (filename) - 1] != G_DIR_SEPARATOR) + { + gchar *s = g_strconcat (filename, G_DIR_SEPARATOR_S, NULL); + gtk_file_selection_set_filename (fs, s); + g_free (s); + } + else + gtk_file_selection_set_filename (fs, filename); + } + else + { + gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); + file_overwrite (g_strdup (filename), g_strdup (raw_filename)); + } + } + else + { + gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); + + file_save_with_proc (the_gimage, filename, raw_filename, save_file_proc, + set_filename); + + gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE); + } +} + +static void +file_overwrite (gchar *filename, + gchar *raw_filename) +{ + OverwriteData *overwrite_data; + GtkWidget *query_box; + gchar *overwrite_text; + + overwrite_data = g_new (OverwriteData, 1); + overwrite_data->full_filename = filename; + overwrite_data->raw_filename = raw_filename; + + overwrite_text = g_strdup_printf (_("%s exists, overwrite?"), filename); + + query_box = gimp_query_boolean_box (_("File Exists!"), + gimp_standard_help_func, + "save/file_exists.html", + FALSE, + overwrite_text, + _("Yes"), _("No"), + NULL, NULL, + file_overwrite_callback, + overwrite_data); + + g_free (overwrite_text); + + gtk_widget_show (query_box); +} + +static void +file_overwrite_callback (GtkWidget *widget, + gboolean overwrite, + gpointer data) +{ + OverwriteData *overwrite_data; + + overwrite_data = (OverwriteData *) data; + + if (overwrite) + { + file_save_with_proc (the_gimage, + overwrite_data->full_filename, + overwrite_data->raw_filename, + save_file_proc, + set_filename); + } + + /* always make file save dialog sensitive */ + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); + + g_free (overwrite_data->full_filename); + g_free (overwrite_data->raw_filename); + g_free (overwrite_data); +} diff --git a/app/fileopsP.h b/app/file-save.h similarity index 54% rename from app/fileopsP.h rename to app/file-save.h index beacc520f1..864588d08a 100644 --- a/app/fileopsP.h +++ b/app/file-save.h @@ -16,19 +16,25 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -#ifndef __FILE_OPS_P_H__ -#define __FILE_OPS_P_H__ +#ifndef __FILE_SAVE_H__ +#define __FILE_SAVE_H__ -/* functions that need only be accessed from file_ops_cmds.c */ +extern GSList *save_procs; -TempBuf* make_thumb_tempbuf (GimpImage *gimage); -guchar* readXVThumb (const gchar *fnam, - gint *w, - gint *h, - gchar **imginfo /* caller frees if != NULL */); -gboolean file_save_thumbnail (GimpImage *gimage, - const char *full_source_filename, - TempBuf *tempbuf); -#endif /* FILE_OPS_P_H */ +void file_save_pre_init (void); +void file_save_post_init (void); + +void file_save_callback (GtkWidget *widget, + gpointer data); +void file_save_as_callback (GtkWidget *widget, + gpointer data); +void file_save_a_copy_as_callback (GtkWidget *widget, + gpointer data); + +void file_save_by_extension_callback (GtkWidget *widget, + gpointer data); + + +#endif /* __FILE_SAVE_H__ */ diff --git a/app/file-utils.c b/app/file-utils.c new file mode 100644 index 0000000000..f0dd23b411 --- /dev/null +++ b/app/file-utils.c @@ -0,0 +1,759 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include /* For _mkdir() */ +#define mkdir(path,mode) _mkdir(path) +#endif + +#include "libgimpmath/gimpmath.h" + +#include "apptypes.h" + +#include "file-utils.h" +#include "gdisplay.h" +#include "gimpimage.h" +#include "gimpui.h" +#include "menus.h" +#include "plug_in.h" +#include "temp_buf.h" + + +void +file_update_name (PlugInProcDef *proc, + GtkWidget *filesel) +{ + if (proc->extensions_list) + { + gchar *text; + gchar *last_dot; + GString *s; + + text = gtk_entry_get_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry)); + last_dot = strrchr (text, '.'); + + if (last_dot == text || !text[0]) + return; + + s = g_string_new (text); + + if (last_dot) + g_string_truncate (s, last_dot-text); + + g_string_append (s, "."); + g_string_append (s, (gchar *) proc->extensions_list->data); + + gtk_entry_set_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry), s->str); + + g_string_free (s, TRUE); + } +} + +void +file_update_menus (GSList *procs, + gint image_type) +{ + PlugInProcDef *file_proc; + + while (procs) + { + file_proc = procs->data; + procs = procs->next; + + if (file_proc->db_info.proc_type != PDB_EXTENSION) + menus_set_sensitive (file_proc->menu_path, + (file_proc->image_types_val & image_type)); + } +} + +void +file_dialog_show (GtkWidget *filesel) +{ + menus_set_sensitive ("/File/Open...", FALSE); + menus_set_sensitive ("/File/Open...", FALSE); + menus_set_sensitive ("/File/Save", FALSE); + menus_set_sensitive ("/File/Save as...", FALSE); + menus_set_sensitive ("/File/Save a Copy as...", FALSE); + + gtk_widget_grab_focus (GTK_FILE_SELECTION (filesel)->selection_entry); + gtk_widget_show (filesel); +} + +void +file_dialog_hide (GtkWidget *filesel) +{ + gimp_dialog_hide (filesel); + + menus_set_sensitive ("/File/Open...", TRUE); + menus_set_sensitive ("/File/Open...", TRUE); + + if (gdisplay_active ()) + { + menus_set_sensitive ("/File/Save", TRUE); + menus_set_sensitive ("/File/Save as...", TRUE); + menus_set_sensitive ("/File/Save a Copy as...", TRUE); + } +} + +static PlugInProcDef * +file_proc_find_by_name (GSList *procs, + const gchar *filename, + gboolean skip_magic) +{ + GSList *p; + gchar *ext = strrchr (filename, '.'); + + if (ext) + ext++; + + for (p = procs; p; p = g_slist_next (p)) + { + PlugInProcDef *proc = p->data; + GSList *prefixes; + + if (skip_magic && proc->magics_list) + continue; + + for (prefixes = proc->prefixes_list; + prefixes; + prefixes = g_slist_next (prefixes)) + { + if (strncmp (filename, prefixes->data, strlen (prefixes->data)) == 0) + return proc; + } + } + + for (p = procs; p; p = g_slist_next (p)) + { + PlugInProcDef *proc = p->data; + GSList *extensions; + + for (extensions = proc->extensions_list; + ext && extensions; + extensions = g_slist_next (extensions)) + { + gchar *p1 = ext; + gchar *p2 = (gchar *) extensions->data; + + if (skip_magic && proc->magics_list) + continue; + + while (*p1 && *p2) + { + if (tolower (*p1) != tolower (*p2)) + break; + + p1++; + p2++; + } + + if (!(*p1) && !(*p2)) + return proc; + } + } + + return NULL; +} + +PlugInProcDef * +file_proc_find (GSList *procs, + const gchar *filename) +{ + PlugInProcDef *file_proc; + PlugInProcDef *size_matched_proc = NULL; + GSList *all_procs = procs; + FILE *ifp = NULL; + gint head_size = -2; + gint size_match_count = 0; + gint match_val; + guchar head[256]; + + /* First, check magicless prefixes/suffixes */ + if ( (file_proc = file_proc_find_by_name (all_procs, filename, TRUE)) != NULL) + return file_proc; + + /* Then look for magics */ + while (procs) + { + file_proc = procs->data; + procs = procs->next; + + if (file_proc->magics_list) + { + if (head_size == -2) + { + head_size = 0; + if ((ifp = fopen (filename, "rb")) != NULL) + head_size = fread ((gchar *) head, 1, sizeof (head), ifp); + } + if (head_size >= 4) + { + match_val = file_check_magic_list (file_proc->magics_list, + head_size, head, ifp); + if (match_val == 2) /* size match ? */ + { /* Use it only if no other magic matches */ + size_match_count++; + size_matched_proc = file_proc; + } + else if (match_val) + { + fclose (ifp); + return (file_proc); + } + } + } + } + if (ifp) fclose (ifp); + if (size_match_count == 1) + return (size_matched_proc); + + /* As a last ditch, try matching by name */ + return file_proc_find_by_name (all_procs, filename, FALSE); +} + +static void +file_convert_string (gchar *instr, + gchar *outmem, + gint maxmem, + gint *nmem) +{ + /* Convert a string in C-notation to array of char */ + guchar *uin = (guchar *) instr; + guchar *uout = (guchar *) outmem; + guchar tmp[5], *tmpptr; + gint k; + + while ((*uin != '\0') && ((((char *)uout) - outmem) < maxmem)) + { + if (*uin != '\\') /* Not an escaped character ? */ + { + *(uout++) = *(uin++); + continue; + } + if (*(++uin) == '\0') + { + *(uout++) = '\\'; + break; + } + switch (*uin) + { + case '0': case '1': case '2': case '3': /* octal */ + for (tmpptr = tmp; (tmpptr-tmp) <= 3;) + { + *(tmpptr++) = *(uin++); + if ( (*uin == '\0') || (!isdigit (*uin)) + || (*uin == '8') || (*uin == '9')) + break; + } + *tmpptr = '\0'; + sscanf ((char *)tmp, "%o", &k); + *(uout++) = k; + break; + + case 'a': *(uout++) = '\a'; uin++; break; + case 'b': *(uout++) = '\b'; uin++; break; + case 't': *(uout++) = '\t'; uin++; break; + case 'n': *(uout++) = '\n'; uin++; break; + case 'v': *(uout++) = '\v'; uin++; break; + case 'f': *(uout++) = '\f'; uin++; break; + case 'r': *(uout++) = '\r'; uin++; break; + + default : *(uout++) = *(uin++); break; + } + } + *nmem = ((gchar *) uout) - outmem; +} + +static gint +file_check_single_magic (gchar *offset, + gchar *type, + gchar *value, + gint headsize, + guchar *file_head, + FILE *ifp) + +{ + /* Return values are 0: no match, 1: magic match, 2: size match */ + glong offs; + gulong num_testval, num_operatorval; + gulong fileval; + gint numbytes, k, c = 0, found = 0; + gchar *num_operator_ptr, num_operator, num_test; + guchar mem_testval[256]; + + /* Check offset */ + if (sscanf (offset, "%ld", &offs) != 1) return (0); + if (offs < 0) return (0); + + /* Check type of test */ + num_operator_ptr = NULL; + num_operator = '\0'; + num_test = '='; + if (strncmp (type, "byte", 4) == 0) + { + numbytes = 1; + num_operator_ptr = type+4; + } + else if (strncmp (type, "short", 5) == 0) + { + numbytes = 2; + num_operator_ptr = type+5; + } + else if (strncmp (type, "long", 4) == 0) + { + numbytes = 4; + num_operator_ptr = type+4; + } + else if (strncmp (type, "size", 4) == 0) + { + numbytes = 5; + } + else if (strcmp (type, "string") == 0) + { + numbytes = 0; + } + else return (0); + + /* Check numerical operator value if present */ + if (num_operator_ptr && (*num_operator_ptr == '&')) + { + if (isdigit (num_operator_ptr[1])) + { + if (num_operator_ptr[1] != '0') /* decimal */ + sscanf (num_operator_ptr+1, "%ld", &num_operatorval); + else if (num_operator_ptr[2] == 'x') /* hexadecimal */ + sscanf (num_operator_ptr+3, "%lx", &num_operatorval); + else /* octal */ + sscanf (num_operator_ptr+2, "%lo", &num_operatorval); + num_operator = *num_operator_ptr; + } + } + + if (numbytes > 0) /* Numerical test ? */ + { + /* Check test value */ + if ((value[0] == '=') || (value[0] == '>') || (value[0] == '<')) + { + num_test = value[0]; + value++; + } + if (!isdigit (value[0])) return (0); + + /* + * to anybody reading this: is strtol's parsing behaviour (e.g. "0x" prefix) + * broken on some systems or why do we do the base detection ourselves? + * */ + if (value[0] != '0') /* decimal */ + num_testval = strtol(value, NULL, 10); + else if (value[1] == 'x') /* hexadecimal */ + num_testval = (unsigned long)strtoul(value+2, NULL, 16); + else /* octal */ + num_testval = strtol(value+1, NULL, 8); + + fileval = 0; + if (numbytes == 5) /* Check for file size ? */ + { + struct stat buf; + + if (fstat (fileno (ifp), &buf) < 0) return (0); + fileval = buf.st_size; + } + else if (offs + numbytes <= headsize) /* We have it in memory ? */ + { + for (k = 0; k < numbytes; k++) + fileval = (fileval << 8) | (long)file_head[offs+k]; + } + else /* Read it from file */ + { + if (fseek (ifp, offs, SEEK_SET) < 0) return (0); + for (k = 0; k < numbytes; k++) + fileval = (fileval << 8) | (c = getc (ifp)); + if (c == EOF) return (0); + } + if (num_operator == '&') + fileval &= num_operatorval; + + if (num_test == '<') + found = (fileval < num_testval); + else if (num_test == '>') + found = (fileval > num_testval); + else + found = (fileval == num_testval); + + if (found && (numbytes == 5)) found = 2; + } + else if (numbytes == 0) /* String test */ + { + file_convert_string ((char *)value, (char *)mem_testval, + sizeof (mem_testval), &numbytes); + if (numbytes <= 0) return (0); + + if (offs + numbytes <= headsize) /* We have it in memory ? */ + { + found = (memcmp (mem_testval, file_head+offs, numbytes) == 0); + } + else /* Read it from file */ + { + if (fseek (ifp, offs, SEEK_SET) < 0) return (0); + found = 1; + for (k = 0; found && (k < numbytes); k++) + { + c = getc (ifp); + found = (c != EOF) && (c == (int)mem_testval[k]); + } + } + } + + return found; +} + +gint +file_check_magic_list (GSList *magics_list, + gint headsize, + guchar *head, + FILE *ifp) + +{ + /* Return values are 0: no match, 1: magic match, 2: size match */ + gchar *offset; + gchar *type; + gchar *value; + gint and = 0; + gint found = 0; + gint match_val; + + while (magics_list) + { + if ((offset = (gchar *)magics_list->data) == NULL) break; + if ((magics_list = magics_list->next) == NULL) break; + if ((type = (gchar *)magics_list->data) == NULL) break; + if ((magics_list = magics_list->next) == NULL) break; + if ((value = (gchar *)magics_list->data) == NULL) break; + magics_list = magics_list->next; + + match_val = file_check_single_magic (offset, type, value, + headsize, head, ifp); + if (and) + found = found && match_val; + else + found = match_val; + + and = (strchr (offset, '&') != NULL); + + if ((!and) && found) + return match_val; + } + + return 0; +} + +TempBuf * +make_thumb_tempbuf (GimpImage *gimage) +{ + gint w, h; + + if (gimage->width<=80 && gimage->height<=60) + { + w = gimage->width; + h = gimage->height; + } + else + { + /* Ratio molesting to fit within .xvpic thumbnail size limits */ + if (60 * gimage->width < 80 * gimage->height) + { + h = 60; + w = (60 * gimage->width) / gimage->height; + if (w == 0) + w = 1; + } + else + { + w = 80; + h = (80 * gimage->height) / gimage->width; + if (h == 0) + h = 1; + } + } + + /*printf("tn: %d x %d -> ", w, h);fflush(stdout);*/ + + return gimp_viewable_get_preview (GIMP_VIEWABLE (gimage), w, h); +} + +/* The readXVThumb function source may be re-used under + the XFree86-style license. */ +guchar * +readXVThumb (const gchar *fnam, + gint *w, + gint *h, + gchar **imginfo /* caller frees if != NULL */) +{ + FILE *fp; + const gchar *P7_332 = "P7 332"; + gchar P7_buf[7]; + gchar linebuf[200]; + guchar *buf; + gint twofivefive; + void *ptr; + + *w = *h = 0; + *imginfo = NULL; + + fp = fopen (fnam, "rb"); + if (!fp) + return NULL; + + fread (P7_buf, 6, 1, fp); + + if (strncmp(P7_buf, P7_332, 6)!=0) + { + g_warning ("Thumbnail doesn't have the 'P7 332' header."); + fclose (fp); + return NULL; + } + + /*newline*/ + fread (P7_buf, 1, 1, fp); + + do + { + ptr = fgets(linebuf, 199, fp); + if ((strncmp(linebuf, "#IMGINFO:", 9) == 0) && + (linebuf[9] != '\0') && + (linebuf[9] != '\n')) + { + if (linebuf[strlen(linebuf)-1] == '\n') + linebuf[strlen(linebuf)-1] = '\0'; + + if (linebuf[9] != '\0') + { + if (*imginfo) + g_free(*imginfo); + *imginfo = g_strdup (&linebuf[9]); + } + } + } + while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */ + + if (!ptr) + { + /* g_warning("Thumbnail ended - not an image?"); */ + fclose (fp); + return NULL; + } + + sscanf(linebuf, "%d %d %d\n", w, h, &twofivefive); + + if (twofivefive!=255) + { + g_warning ("Thumbnail is of funky depth."); + fclose (fp); + return NULL; + } + + if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60) + { + g_warning ("Thumbnail size bad. Corrupted?"); + fclose (fp); + return NULL; + } + + buf = g_malloc ((*w) * (*h)); + + fread (buf, (*w) * (*h), 1, fp); + + fclose (fp); + + return buf; +} + +gboolean +file_save_thumbnail (GimpImage *gimage, + const gchar *full_source_filename, + TempBuf *tempbuf) +{ + gint i,j; + gint w,h; + guchar *tbd; + gchar *pathname; + gchar *filename; + gchar *xvpathname; + gchar *thumbnailname; + GimpImageBaseType basetype; + FILE *fp; + struct stat statbuf; + + if (stat (full_source_filename, &statbuf) != 0) + { + return FALSE; + } + + /* just for debugging + * if (gimp_image_preview_valid (gimage, GRAY_CHANNEL)) + * { + * g_print ("(incidentally, gimage already has a valid preview - %dx%d)\n", + * gimage->comp_preview->width, + * gimage->comp_preview->height); + * } + */ + + pathname = g_dirname (full_source_filename); + filename = g_basename (full_source_filename); /* Don't free! */ + + xvpathname = g_strconcat (pathname, G_DIR_SEPARATOR_S, ".xvpics", + NULL); + + thumbnailname = g_strconcat (xvpathname, G_DIR_SEPARATOR_S, + filename, + NULL); + + tbd = temp_buf_data (tempbuf); + + w = tempbuf->width; + h = tempbuf->height; + /*printf("tn: %d x %d\n", w, h);fflush(stdout);*/ + + mkdir (xvpathname, 0755); + + fp = fopen (thumbnailname, "wb"); + g_free (pathname); + g_free (xvpathname); + g_free (thumbnailname); + + if (fp) + { + basetype = gimp_image_base_type (gimage); + + fprintf (fp, + "P7 332\n#IMGINFO:%dx%d %s (%d %s)\n" + "#END_OF_COMMENTS\n%d %d 255\n", + gimage->width, gimage->height, + (basetype == RGB) ? "RGB" : + (basetype == GRAY) ? "Greyscale" : + (basetype == INDEXED) ? "Indexed" : + "(UNKNOWN COLOUR TYPE)", + (int)statbuf.st_size, + (statbuf.st_size == 1) ? "byte" : "bytes", + w, h); + + switch (basetype) + { + case INDEXED: + case RGB: + for (i=0; i>5)<<5) | ((g>>5)<<2) | (b>>6), fp); + + rerr = r - ( (r>>5) * 255 ) / 7; + gerr = g - ( (g>>5) * 255 ) / 7; + berr = b - ( (b>>6) * 255 ) / 3; + } + } + break; + + case GRAY: + for (i=0; i>5)<<5) | ((b3>>5)<<2) | (b2>>6), fp); + + b2err = b2 - ( (b2>>6) * 255 ) / 3; + b3err = b3 - ( (b3>>5) * 255 ) / 7; + } + } + break; + + default: + g_warning("UNKNOWN GIMAGE TYPE IN THUMBNAIL SAVE"); + break; + } + + fclose (fp); + } + else /* Error writing thumbnail */ + { + return FALSE; + } + + return TRUE; +} diff --git a/app/file-utils.h b/app/file-utils.h new file mode 100644 index 0000000000..e57ac666bd --- /dev/null +++ b/app/file-utils.h @@ -0,0 +1,54 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FILE_UTILS_H__ +#define __FILE_UTILS_H__ + + +#include + + +void file_dialog_show (GtkWidget *filesel); +void file_dialog_hide (GtkWidget *filesel); + +void file_update_name (PlugInProcDef *proc, + GtkWidget *filesel); +void file_update_menus (GSList *procs, + gint image_type); + +PlugInProcDef * file_proc_find (GSList *procs, + const gchar *filename); + +/* Return values are 0: no match, 1: magic match, 2: size match */ +gint file_check_magic_list (GSList *magics_list, + gint headsize, + guchar *head, + FILE *ifp); + + +TempBuf * make_thumb_tempbuf (GimpImage *gimage); +guchar * readXVThumb (const gchar *fnam, + gint *w, + gint *h, + gchar **imginfo /* caller frees if != NULL */); +gboolean file_save_thumbnail (GimpImage *gimage, + const char *full_source_filename, + TempBuf *tempbuf); + + +#endif /* __FILE_UTILS_H__ */ diff --git a/app/file/file-open.c b/app/file/file-open.c new file mode 100644 index 0000000000..0687feeeed --- /dev/null +++ b/app/file/file-open.c @@ -0,0 +1,1115 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWGRP +#define S_IWGRP (_S_IWRITE>>3) +#define S_IWOTH (_S_IWRITE>>6) +#endif +#ifndef S_IRGRP +#define S_IRGRP (_S_IREAD>>3) +#define S_IROTH (_S_IREAD>>6) +#endif +#define uid_t gint +#define gid_t gint +#define geteuid() 0 +#define getegid() 0 +#endif + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "apptypes.h" + +#include "cursorutil.h" +#include "dialog_handler.h" +#include "docindex.h" +#include "gimpdrawable.h" +#include "gdisplay.h" +#include "gimage.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimpui.h" +#include "gimprc.h" +#include "file-open.h" +#include "file-utils.h" +#include "menus.h" +#include "plug_in.h" +#include "temp_buf.h" +#include "undo.h" + +#include "libgimp/gimpintl.h" + + +#define REVERT_DATA_KEY "revert_confirm_dialog" + + +static void file_open_dialog_create (void); + +static void file_revert_confirm_callback (GtkWidget *widget, + gboolean revert, + gpointer data); + +static GimpImage * file_open_image (const gchar *filename, + const gchar *raw_filename, + const gchar *open_mode, + RunModeType run_mode, + gint *status); + +static void file_open_genbutton_callback (GtkWidget *widget, + gpointer data); + +static void file_open_clistrow_callback (GtkWidget *widget, + gint row); + +static void file_open_ok_callback (GtkWidget *widget, + gpointer data); + +static void file_open_type_callback (GtkWidget *widget, + gpointer data); + +static gchar * file_absolute_filename (gchar *name); + +static GSList* clist_to_slist (GtkCList *file_list); + + + +static GtkWidget *fileload = NULL; +static GtkWidget *open_options = NULL; + +static GtkPreview *open_options_preview = NULL; +static GtkWidget *open_options_fixed = NULL; +static GtkWidget *open_options_label = NULL; +static GtkWidget *open_options_frame = NULL; +static GtkWidget *open_options_genbuttonlabel = NULL; + +/* Some state for the thumbnailer */ +static gchar *preview_fullname = NULL; + +GSList *load_procs = NULL; + +static PlugInProcDef *load_file_proc = NULL; + +extern GSList *display_list; /* from gdisplay.c */ + + +/* public functions */ + +void +file_open_pre_init (void) +{ +} + +void +file_open_post_init (void) +{ + GimpItemFactoryEntry entry; + PlugInProcDef *file_proc; + GSList *list; + + load_procs = g_slist_reverse (load_procs); + + for (list = load_procs; list; list = g_slist_next (list)) + { + gchar *help_page; + + file_proc = (PlugInProcDef *) list->data; + + help_page = g_strconcat ("filters/", + g_basename (file_proc->prog), + ".html", + NULL); + g_strdown (help_page); + + entry.entry.path = file_proc->menu_path; + entry.entry.accelerator = NULL; + entry.entry.callback = file_open_type_callback; + entry.entry.callback_action = 0; + entry.entry.item_type = NULL; + entry.help_page = help_page; + entry.description = NULL; + + menus_create_item_from_full_path (&entry, NULL, file_proc); + } +} + +void +file_open_callback (GtkWidget *widget, + gpointer data) +{ + if (!fileload) + file_open_dialog_create (); + + gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE); + if (GTK_WIDGET_VISIBLE (fileload)) + return; + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (fileload), + "." G_DIR_SEPARATOR_S); + gtk_window_set_title (GTK_WINDOW (fileload), _("Load Image")); + + file_dialog_show (fileload); +} + +void +file_revert_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + GimpImage *gimage; + GtkWidget *query_box; + const gchar *filename; + + gdisplay = gdisplay_active (); + if (!gdisplay || !gdisplay->gimage) + return; + + gimage = gdisplay->gimage; + + filename = gimp_object_get_name (GIMP_OBJECT (gimage)); + + query_box = gtk_object_get_data (GTK_OBJECT (gimage), REVERT_DATA_KEY); + + if (! filename) + { + g_message (_("Revert failed.\n" + "No filename associated with this image.")); + } + else if (query_box) + { + gdk_window_raise (query_box->window); + } + else + { + gchar *text; + + text = g_strdup_printf (_("Reverting %s to\n" + "%s\n\n" + "(You will lose all your changes\n" + "including all undo information)"), + g_basename (filename), + filename); + + query_box = gimp_query_boolean_box (_("Revert Image?"), + gimp_standard_help_func, + "file/revert.html", + FALSE, + text, + _("Yes"), _("No"), + GTK_OBJECT (gimage), "destroy", + file_revert_confirm_callback, + gimage); + + g_free (text); + + gtk_object_set_data (GTK_OBJECT (gimage), REVERT_DATA_KEY, query_box); + + gtk_widget_show (query_box); + } +} + +void +file_open_by_extension_callback (GtkWidget *widget, + gpointer data) +{ + load_file_proc = NULL; +} + + +/* private functions */ + +static void +file_open_dialog_create (void) +{ + fileload = gtk_file_selection_new (_("Load Image")); + gtk_window_set_position (GTK_WINDOW (fileload), GTK_WIN_POS_MOUSE); + gtk_window_set_wmclass (GTK_WINDOW (fileload), "load_image", "Gimp"); + + gtk_container_set_border_width (GTK_CONTAINER (fileload), 2); + gtk_container_set_border_width + (GTK_CONTAINER (GTK_FILE_SELECTION (fileload)->button_area), 2); + + dialog_register_fileload (fileload); + + gtk_signal_connect_object + (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->cancel_button), "clicked", + GTK_SIGNAL_FUNC (file_dialog_hide), + GTK_OBJECT (fileload)); + gtk_signal_connect (GTK_OBJECT (fileload), "delete_event", + GTK_SIGNAL_FUNC (file_dialog_hide), + NULL); + gtk_signal_connect + (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->ok_button), "clicked", + GTK_SIGNAL_FUNC (file_open_ok_callback), + fileload); + gtk_quit_add_destroy (1, GTK_OBJECT (fileload)); + + gtk_clist_set_selection_mode + (GTK_CLIST (GTK_FILE_SELECTION (fileload)->file_list), + GTK_SELECTION_EXTENDED); + + /* Catch file-clist clicks so we can update the preview thumbnail */ + gtk_signal_connect + (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->file_list), "select_row", + GTK_SIGNAL_FUNC (file_open_clistrow_callback), + fileload); + + /* Connect the "F1" help key */ + gimp_help_connect_help_accel (fileload, + gimp_standard_help_func, + "open/dialogs/file_open.html"); + + { + GtkWidget *frame; + GtkWidget *vbox; + GtkWidget *hbox; + GtkWidget *option_menu; + GtkWidget *load_menu; + GtkWidget *open_options_genbutton; + + open_options = gtk_hbox_new (TRUE, 1); + + /* format-chooser frame */ + frame = gtk_frame_new (_("Determine File Type")); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start (GTK_BOX (open_options), frame, TRUE, TRUE, 4); + + vbox = gtk_vbox_new (FALSE, 2); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + hbox = gtk_hbox_new (FALSE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + option_menu = gtk_option_menu_new (); + gtk_box_pack_start (GTK_BOX (hbox), option_menu, FALSE, FALSE, 0); + gtk_widget_show (option_menu); + + menus_get_load_menu (&load_menu, NULL); + gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), load_menu); + + gtk_widget_show (vbox); + gtk_widget_show (frame); + + /* Preview frame */ + open_options_frame = frame = gtk_frame_new (""); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_end (GTK_BOX (open_options), frame, FALSE, TRUE, 4); + + vbox = gtk_vbox_new (FALSE, 2); + gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); + gtk_container_add (GTK_CONTAINER (frame), vbox); + + hbox = gtk_hbox_new (TRUE, 0); + gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); + gtk_widget_show (hbox); + + open_options_genbutton = gtk_button_new (); + gtk_signal_connect (GTK_OBJECT (open_options_genbutton), "clicked", + GTK_SIGNAL_FUNC (file_open_genbutton_callback), + fileload); + gtk_box_pack_start (GTK_BOX (hbox), open_options_genbutton, + TRUE, FALSE, 0); + gtk_widget_show (open_options_genbutton); + + open_options_fixed = gtk_fixed_new (); + gtk_widget_set_usize (open_options_fixed, 80, 60); + gtk_container_add (GTK_CONTAINER (GTK_BIN (open_options_genbutton)), + open_options_fixed); + gtk_widget_show (open_options_fixed); + + { + GtkWidget* abox; + GtkWidget* sbox; + GtkWidget* align; + + sbox = gtk_vbox_new (TRUE, 0); + gtk_container_add (GTK_CONTAINER (open_options_fixed), sbox); + gtk_widget_show (sbox); + + align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); + gtk_widget_set_usize (align, 80, 60); + gtk_box_pack_start (GTK_BOX (sbox), align, FALSE, TRUE, 0); + gtk_widget_show (align); + + abox = gtk_hbox_new (FALSE, 0); + gtk_container_add (GTK_CONTAINER (align), abox); + gtk_widget_show (abox); + + open_options_preview = + GTK_PREVIEW (gtk_preview_new (GTK_PREVIEW_COLOR)); + gtk_box_pack_start (GTK_BOX (abox), GTK_WIDGET (open_options_preview), + FALSE, TRUE, 0); + gtk_widget_show (GTK_WIDGET (open_options_preview)); + + open_options_genbuttonlabel = gtk_label_new (_("Generate\nPreview")); + gtk_box_pack_start (GTK_BOX (abox), open_options_genbuttonlabel, + FALSE, TRUE, 0); + gtk_widget_show (open_options_genbuttonlabel); + } + + open_options_label = gtk_label_new (""); + gtk_box_pack_start (GTK_BOX (vbox), open_options_label, FALSE, FALSE, 0); + gtk_widget_show (open_options_label); + + gtk_widget_show (vbox); + gtk_widget_show (frame); + + /* pack the containing open_options hbox into the open-dialog */ + gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (fileload)->main_vbox), + open_options, FALSE, FALSE, 0); + } + + gtk_frame_set_label (GTK_FRAME (open_options_frame), _("Preview")); + gtk_label_set_text (GTK_LABEL (open_options_label), _("No Selection.")); + + gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel)); + gtk_widget_hide (GTK_WIDGET (open_options_preview)); + gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), FALSE); + + gtk_widget_show (open_options); +} + +static void +file_open_type_callback (GtkWidget *widget, + gpointer data) +{ + PlugInProcDef *proc = (PlugInProcDef *) data; + + file_update_name (proc, fileload); + + load_file_proc = proc; +} + +static GimpImage * +file_open_image (const gchar *filename, + const gchar *raw_filename, + const gchar *open_mode, + RunModeType run_mode, + gint *status) +{ + PlugInProcDef *file_proc; + ProcRecord *proc; + Argument *args; + Argument *return_vals; + gint gimage_id; + gint i; + struct stat statbuf; + + *status = PDB_CANCEL; /* inhibits error messages by caller */ + + file_proc = load_file_proc; + + if (!file_proc) + file_proc = file_proc_find (load_procs, filename); + + if (!file_proc) + { + /* no errors when making thumbnails */ + if (run_mode == RUN_INTERACTIVE) + g_message (_("%s failed.\n" + "%s: Unknown file type."), + open_mode, filename); + + return NULL; + } + + /* check if we are opening a file */ + if (stat (filename, &statbuf) == 0) + { + uid_t euid; + gid_t egid; + + if (! (statbuf.st_mode & S_IFREG)) + { + /* no errors when making thumbnails */ + if (run_mode == RUN_INTERACTIVE) + g_message (_("%s failed.\n" + "%s is not a regular file."), + open_mode, filename); + + return NULL; + } + + euid = geteuid (); + egid = getegid (); + + if (! ((statbuf.st_mode & S_IRUSR) || + + ((statbuf.st_mode & S_IRGRP) && + (statbuf.st_uid != euid)) || + + ((statbuf.st_mode & S_IROTH) && + (statbuf.st_uid != euid) && + (statbuf.st_gid != egid)))) + { + /* no errors when making thumbnails */ + if (run_mode == RUN_INTERACTIVE) + g_message (_("%s failed.\n" + "%s: Permission denied."), + open_mode, filename); + + return NULL; + } + } + + proc = &file_proc->db_info; + + args = g_new0 (Argument, proc->num_args); + + for (i = 0; i < proc->num_args; i++) + args[i].arg_type = proc->args[i].arg_type; + + args[0].value.pdb_int = run_mode; + args[1].value.pdb_pointer = (gchar *) filename; + args[2].value.pdb_pointer = (gchar *) raw_filename; + + return_vals = procedural_db_execute (proc->name, args); + + *status = return_vals[0].value.pdb_int; + gimage_id = return_vals[1].value.pdb_int; + + procedural_db_destroy_args (return_vals, proc->num_values); + g_free (args); + + if (*status == PDB_SUCCESS && gimage_id != -1) + { + GimpImage *gimage = pdb_id_to_image (gimage_id); + + if (gimage) + { + gimp_image_invalidate_layer_previews (gimage); + gimp_image_invalidate_channel_previews (gimage); + } + + return pdb_id_to_image (gimage_id); + } + + return NULL; +} + +gint +file_open (gchar *filename, + gchar *raw_filename) +{ + GimpImage *gimage; + GDisplay *gdisplay; + gchar *absolute; + gint status; + + if ((gimage = file_open_image (filename, + raw_filename, + _("Open"), + RUN_INTERACTIVE, + &status)) != NULL) + { + /* enable & clear all undo steps */ + gimp_image_undo_enable (gimage); + + /* set the image to clean */ + gimp_image_clean_all (gimage); + + /* display the image */ + gdisplay = gdisplay_new (gimage, 0x0101); + + /* always activate the first display */ + if (g_slist_length (display_list) == 1) + gimp_context_set_display (gimp_context_get_user (), gdisplay); + + absolute = file_absolute_filename (filename); + document_index_add (absolute); + menus_last_opened_add (absolute); + g_free (absolute); + } + + return status; +} + +static guchar * +make_RGBbuf_from_tempbuf (TempBuf *tempbuf, + gint *width_rtn, + gint *height_rtn) +{ + gint i, j, w, h; + guchar *tbd; + guchar *ptr; + guchar *rtn = NULL; + guchar alpha, r, g, b; + + w = (*width_rtn) = tempbuf->width; + h = (*height_rtn) = tempbuf->height; + tbd = temp_buf_data (tempbuf); + + switch (tempbuf->bytes) + { + case 4: + rtn = ptr = g_malloc (3 * w * h); + for (i=0; i>5)*255)/7; + thumb_rgb[i*3+1] = (((raw_thumb[i]>>2)&7)*255)/7; + thumb_rgb[i*3+2] = (((raw_thumb[i])&3)*255)/3; + } + + gtk_preview_size (open_options_preview, tnw, tnh); + + for (i = 0; i < tnh; i++) + { + gtk_preview_draw_row (open_options_preview, &thumb_rgb[3*i*tnw], + 0, i, + tnw); + } + + g_free (thumb_rgb); + } + } + + if (raw_thumb || RGB_source) /* We can show *some* kind of preview. */ + { + if (raw_thumb) /* Managed to commit thumbnail file to disk */ + { + gtk_label_set_text (GTK_LABEL (open_options_label), + thumb_may_be_outdated ? + _("(This thumbnail may be out of date)") : + (imginfo ? imginfo : _("(No Information)"))); + if (imginfo) + g_free (imginfo); + } + else + { + switch (thumbnail_mode) + { + case 0: + gtk_label_set_text (GTK_LABEL(open_options_label), + _("(Thumbnail saving is disabled)")); + break; + case 1: + gtk_label_set_text (GTK_LABEL(open_options_label), + _("(Could not write thumbnail file)")); + break; + default: + gtk_label_set_text (GTK_LABEL(open_options_label), + _("(Thumbnail file not written)")); + } + } + + gtk_widget_show (GTK_WIDGET (open_options_preview)); + gtk_widget_queue_draw (GTK_WIDGET(open_options_preview)); + + show_generate_label = FALSE; + + g_free (raw_thumb); + } + else + { + if (imginfo) + g_free (imginfo); + + gtk_widget_hide (GTK_WIDGET (open_options_preview)); + gtk_label_set_text (GTK_LABEL (open_options_label), + _("No preview available")); + } + + if (show_generate_label) + { + gtk_widget_hide (GTK_WIDGET (open_options_preview)); + gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel)); + } + else + { + gtk_widget_hide (GTK_WIDGET (open_options_genbuttonlabel)); + gtk_widget_show (GTK_WIDGET (open_options_preview)); + } +} + +static void +file_open_clistrow_callback (GtkWidget *widget, + gint row) +{ + gchar *fullfname = NULL; + + fullfname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fileload)); + + gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), TRUE); + set_preview (fullfname, NULL, 0, 0); +} + +static void +file_open_genbutton_callback (GtkWidget *widget, + gpointer data) +{ + GimpImage *gimage_to_be_thumbed; + guchar *RGBbuf; + TempBuf *tempbuf; + gint RGBbuf_w; + gint RGBbuf_h; + + /* added for multi-file preview generation... */ + GtkFileSelection *fs; + gchar *full_filename = NULL; + gchar *filedirname; + struct stat buf; + gint err; + + fs = GTK_FILE_SELECTION (data); + + if (! preview_fullname) + { + g_warning ("Tried to generate thumbnail for NULL filename."); + return; + } + + gimp_add_busy_cursors (); + gtk_widget_set_sensitive (GTK_WIDGET (fileload), FALSE); + + /* new mult-file preview make: */ + { + GSList *list, *toplist; + + /* Have to read the clist before touching anything else */ + + list= clist_to_slist(GTK_CLIST(fs->file_list)); + toplist = list; + + /* Find a real base directory for the multiple selection */ + + gtk_file_selection_set_filename (fs, ""); + filedirname= gtk_file_selection_get_filename (fs); + if (filedirname[strlen (filedirname) - 1] == G_DIR_SEPARATOR) + filedirname[strlen (filedirname) - 1] = '\0'; + + while(list) + { + full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, + (gchar *) list->data, NULL); + + err = stat (full_filename, &buf); + + if (! (err == 0 && (buf.st_mode & S_IFDIR))) + { /* Is not directory. */ + gint dummy; + + gimage_to_be_thumbed = file_open_image (full_filename, + list->data, + NULL, + RUN_NONINTERACTIVE, + &dummy); + + if (gimage_to_be_thumbed) + { + tempbuf = make_thumb_tempbuf (gimage_to_be_thumbed); + RGBbuf = make_RGBbuf_from_tempbuf (tempbuf, + &RGBbuf_w, + &RGBbuf_h); + if (thumbnail_mode) + { + file_save_thumbnail (gimage_to_be_thumbed, + full_filename, tempbuf); + } + set_preview (full_filename, RGBbuf, RGBbuf_w, RGBbuf_h); + + gtk_object_unref (GTK_OBJECT (gimage_to_be_thumbed)); + + if (RGBbuf) + g_free (RGBbuf); + } + else + { + gtk_label_set_text (GTK_LABEL (open_options_label), + _("(could not make preview)")); + } + } + + g_free(full_filename); + list= g_slist_next(list); + } + + for (list = toplist; list; list = g_slist_next (list)) + { + if (!(g_slist_next (list))) + { + full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, + (gchar *) list->data, NULL); + gtk_file_selection_set_filename (fs, full_filename); + g_free (full_filename); + } + + g_free (list->data); + } + + g_slist_free (toplist); + toplist = NULL; + } + + gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE); + gimp_remove_busy_cursors (NULL); +} + +static void +file_open_ok_callback (GtkWidget *widget, + gpointer data) +{ + GtkFileSelection *fs; + gchar *full_filename; + gchar *raw_filename; + gchar *filedirname; + struct stat buf; + gint err; + gint status; + + fs = GTK_FILE_SELECTION (data); + full_filename = gtk_file_selection_get_filename (fs); + raw_filename = gtk_entry_get_text (GTK_ENTRY(fs->selection_entry)); + + g_assert (full_filename && raw_filename); + + if (strlen (raw_filename) == 0) + return; + + err = stat (full_filename, &buf); + + if (err == 0 && (buf.st_mode & S_IFDIR)) + { + if (full_filename[strlen (full_filename) - 1] != G_DIR_SEPARATOR) + { + gchar *s = g_strconcat (full_filename, G_DIR_SEPARATOR_S, NULL); + gtk_file_selection_set_filename (fs, s); + g_free (s); + } + else + gtk_file_selection_set_filename (fs, full_filename); + + return; + } + + gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); + + if (err) /* e.g. http://server/filename.jpg */ + full_filename = raw_filename; + + status = file_open (full_filename, raw_filename); + + if (status == PDB_SUCCESS) + { + file_dialog_hide (data); + } + else if (status != PDB_CANCEL) + { + g_message (_("Open failed.\n%s"), full_filename); + } + + + /* + * Now deal with multiple selections from the filesel clist + */ + + { + GSList *list; + + /* Have to read the clist before touching anything else */ + + list = clist_to_slist (GTK_CLIST (fs->file_list)); + + /* Find a real base directory for the multiple selection */ + + raw_filename = g_strdup(raw_filename); + gtk_file_selection_set_filename (fs, ""); + filedirname = gtk_file_selection_get_filename (fs); + + while (list) + { + full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, + (gchar *) list->data, NULL); + + if (strcmp (list->data, raw_filename)) + { /* don't load current selection twice */ + + err = stat (full_filename, &buf); + + if (! (err == 0 && (buf.st_mode & S_IFDIR))) + { /* Is not directory. */ + + status = file_open (full_filename, (char *) list->data); + + if (status == PDB_SUCCESS) + { + file_dialog_hide (data); + } + else if (status != PDB_CANCEL) + { + g_message (_("Open failed.\n%s"), full_filename); + } + } + } + + g_free (full_filename); + g_free (list->data); + list = g_slist_next (list); + } + + g_slist_free (list); + list = NULL; + } + + gtk_file_selection_set_filename (fs, raw_filename); + g_free (raw_filename); + gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE); + +} + +static GSList * +clist_to_slist (GtkCList *file_list) +{ + GSList *list = NULL; + GList *row; + gint rownum; + gchar *temp; + + for (row = file_list->row_list, rownum = 0; + row; + row = g_list_next (row), rownum++) + { + if (GTK_CLIST_ROW (row)->state == GTK_STATE_SELECTED) + { + if (gtk_clist_get_cell_type (file_list, rownum, 0) == GTK_CELL_TEXT) + { + gtk_clist_get_text (file_list, rownum, 0, &temp); + list = g_slist_prepend (list, g_strdup (temp)); + } + } + } + + return list; +} + +static void +file_revert_confirm_callback (GtkWidget *widget, + gboolean revert, + gpointer data) +{ + GimpImage *old_gimage; + + old_gimage = (GimpImage *) data; + + gtk_object_set_data (GTK_OBJECT (old_gimage), REVERT_DATA_KEY, NULL); + + if (revert) + { + GimpImage *new_gimage; + const gchar *filename; + gint status; + + filename = gimp_object_get_name (GIMP_OBJECT (old_gimage)); + + new_gimage = file_open_image (filename, filename, _("Revert"), + RUN_INTERACTIVE, &status); + + if (new_gimage != NULL) + { + undo_free (new_gimage); + gdisplays_reconnect (old_gimage, new_gimage); + gdisplays_resize_cursor_label (new_gimage); + gdisplays_update_full (new_gimage); + gdisplays_shrink_wrap (new_gimage); + gimp_image_clean_all (new_gimage); + } + else if (status != PDB_CANCEL) + { + g_message (_("Revert failed.\n%s"), filename); + } + } +} + +static gchar * +file_absolute_filename (gchar *name) +{ + PlugInProcDef *proc; + GSList *procs; + GSList *prefixes; + gchar *absolute; + gchar *current; + + g_return_val_if_fail (name != NULL, NULL); + + /* check for prefixes like http or ftp */ + for (procs = load_procs; procs; procs = g_slist_next (procs)) + { + proc = (PlugInProcDef *)procs->data; + + for (prefixes = proc->prefixes_list; + prefixes; + prefixes = g_slist_next (prefixes)) + { + if (strncmp (name, prefixes->data, strlen (prefixes->data)) == 0) + return g_strdup (name); + } + } + + if (g_path_is_absolute (name)) + return g_strdup (name); + + current = g_get_current_dir (); + absolute = g_strconcat (current, G_DIR_SEPARATOR_S, name, NULL); + g_free (current); + + return absolute; +} diff --git a/app/file/file-open.h b/app/file/file-open.h new file mode 100644 index 0000000000..74461468a9 --- /dev/null +++ b/app/file/file-open.h @@ -0,0 +1,42 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FILE_OPEN_H__ +#define __FILE_OPEN_H__ + + +extern GSList *load_procs; + + +void file_open_pre_init (void); +void file_open_post_init (void); + +void file_open_callback (GtkWidget *widget, + gpointer data); + +void file_revert_callback (GtkWidget *widget, + gpointer data); + +void file_open_by_extension_callback (GtkWidget *widget, + gpointer data); + +gint file_open (gchar *filename, + gchar *raw_filename); + + +#endif /* __FILE_OPEN_H__ */ diff --git a/app/file/file-save.c b/app/file/file-save.c new file mode 100644 index 0000000000..af3a777772 --- /dev/null +++ b/app/file/file-save.c @@ -0,0 +1,730 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include +#ifndef S_IWUSR +#define S_IWUSR _S_IWRITE +#endif +#ifndef S_IRUSR +#define S_IRUSR _S_IREAD +#endif +#ifndef S_IWGRP +#define S_IWGRP (_S_IWRITE>>3) +#define S_IWOTH (_S_IWRITE>>6) +#endif +#ifndef S_IRGRP +#define S_IRGRP (_S_IREAD>>3) +#define S_IROTH (_S_IREAD>>6) +#endif +#define uid_t gint +#define gid_t gint +#define geteuid() 0 +#define getegid() 0 +#endif + +#include "libgimpmath/gimpmath.h" +#include "libgimpwidgets/gimpwidgets.h" + +#include "apptypes.h" + +#include "cursorutil.h" +#include "dialog_handler.h" +#include "docindex.h" +#include "gimpdrawable.h" +#include "gdisplay.h" +#include "gimage.h" +#include "gimpcontext.h" +#include "gimpdrawable.h" +#include "gimpui.h" +#include "gimprc.h" +#include "file-save.h" +#include "file-utils.h" +#include "menus.h" +#include "plug_in.h" +#include "temp_buf.h" +#include "undo.h" + +#include "libgimp/gimpintl.h" + + +typedef struct _OverwriteData OverwriteData; + +struct _OverwriteData +{ + gchar *full_filename; + gchar *raw_filename; +}; + + +static void file_save_dialog_create (void); + +static void file_overwrite (gchar *filename, + gchar *raw_filename); +static void file_overwrite_callback (GtkWidget *widget, + gboolean overwrite, + gpointer data); + +static gint file_save (GimpImage *gimage, + gchar *filename, + gchar *raw_filename, + RunModeType run_mode, + gboolean set_filename); + +static void file_save_ok_callback (GtkWidget *widget, + gpointer data); + +static void file_save_type_callback (GtkWidget *widget, + gpointer data); + + +static GtkWidget *filesave = NULL; +static GtkWidget *save_options = NULL; + +GSList *save_procs = NULL; + +static PlugInProcDef *save_file_proc = NULL; + +static GimpImage *the_gimage = NULL; +static gboolean set_filename = TRUE; + + +/* public functions */ + +void +file_save_pre_init (void) +{ +} + +void +file_save_post_init (void) +{ + GimpItemFactoryEntry entry; + PlugInProcDef *file_proc; + GSList *list; + + save_procs = g_slist_reverse (save_procs); + + for (list = save_procs; list; list = g_slist_next (list)) + { + gchar *help_page; + + file_proc = (PlugInProcDef *) list->data; + + help_page = g_strconcat ("filters/", + g_basename (file_proc->prog), + ".html", + NULL); + g_strdown (help_page); + + entry.entry.path = file_proc->menu_path; + entry.entry.accelerator = NULL; + entry.entry.callback = file_save_type_callback; + entry.entry.callback_action = 0; + entry.entry.item_type = NULL; + entry.help_page = help_page; + entry.description = NULL; + + menus_create_item_from_full_path (&entry, NULL, file_proc); + } +} + +void +file_save_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + + gdisplay = gdisplay_active (); + if (! gdisplay) + return; + + if (! gimp_image_active_drawable (gdisplay->gimage)) + return; + + /* Only save if the gimage has been modified */ + if (!trust_dirty_flag || gdisplay->gimage->dirty != 0) + { + gchar *filename; + + filename = + g_strdup (gimp_object_get_name (GIMP_OBJECT (gdisplay->gimage))); + + if (! filename) + { + file_save_as_callback (widget, data); + } + else + { + gchar *raw_filename; + gint status; + + raw_filename = g_basename (filename); + + status = file_save (gdisplay->gimage, + filename, + raw_filename, + RUN_WITH_LAST_VALS, + TRUE); + + if (status != PDB_SUCCESS && + status != PDB_CANCEL) + { + g_message (_("Save failed.\n%s"), filename); + } + + } + + g_free (filename); + } +} + +void +file_save_as_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + const gchar *filename; + + gdisplay = gdisplay_active (); + if (! gdisplay) + return; + + if (! gimp_image_active_drawable (gdisplay->gimage)) + return; + + the_gimage = gdisplay->gimage; + + set_filename = TRUE; + + filename = gimp_object_get_name (GIMP_OBJECT (the_gimage)); + + if (! filesave) + file_save_dialog_create (); + + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); + if (GTK_WIDGET_VISIBLE (filesave)) + return; + + gtk_window_set_title (GTK_WINDOW (filesave), _("Save Image")); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesave), + filename ? + filename : + "." G_DIR_SEPARATOR_S); + + switch (gimp_drawable_type (gimp_image_active_drawable (gdisplay->gimage))) + { + case RGB_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGB_IMAGE); + break; + case RGBA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE); + break; + case GRAY_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE); + break; + case GRAYA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE); + break; + case INDEXED_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE); + break; + case INDEXEDA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE); + break; + } + + file_dialog_show (filesave); +} + +void +file_save_a_copy_as_callback (GtkWidget *widget, + gpointer data) +{ + GDisplay *gdisplay; + const gchar *filename; + + gdisplay = gdisplay_active (); + if (! gdisplay) + return; + + if (! gimp_image_active_drawable (gdisplay->gimage)) + return; + + the_gimage = gdisplay->gimage; + + set_filename = FALSE; + + filename = gimp_object_get_name (GIMP_OBJECT (the_gimage)); + + if (!filesave) + file_save_dialog_create (); + + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); + if (GTK_WIDGET_VISIBLE (filesave)) + return; + + gtk_window_set_title (GTK_WINDOW (filesave), _("Save a Copy of the Image")); + + gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesave), + filename ? + filename : + "." G_DIR_SEPARATOR_S); + + switch (gimp_drawable_type (gimp_image_active_drawable (gdisplay->gimage))) + { + case RGB_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGB_IMAGE); + break; + case RGBA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE); + break; + case GRAY_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE); + break; + case GRAYA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE); + break; + case INDEXED_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE); + break; + case INDEXEDA_GIMAGE: + file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE); + break; + } + + file_dialog_show (filesave); +} + +void +file_save_by_extension_callback (GtkWidget *widget, + gpointer data) +{ + save_file_proc = NULL; +} + + +/* private functions */ + +static void +file_save_dialog_create (void) +{ + filesave = gtk_file_selection_new (_("Save Image")); + gtk_window_set_wmclass (GTK_WINDOW (filesave), "save_image", "Gimp"); + gtk_window_set_position (GTK_WINDOW (filesave), GTK_WIN_POS_MOUSE); + + gtk_container_set_border_width (GTK_CONTAINER (filesave), 2); + gtk_container_set_border_width + (GTK_CONTAINER (GTK_FILE_SELECTION (filesave)->button_area), 2); + + gtk_signal_connect_object + (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->cancel_button), "clicked", + GTK_SIGNAL_FUNC (file_dialog_hide), + GTK_OBJECT (filesave)); + gtk_signal_connect (GTK_OBJECT (filesave), "delete_event", + GTK_SIGNAL_FUNC (file_dialog_hide), + NULL); + gtk_signal_connect + (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->ok_button), "clicked", + GTK_SIGNAL_FUNC (file_save_ok_callback), + filesave); + gtk_quit_add_destroy (1, GTK_OBJECT (filesave)); + + /* Connect the "F1" help key */ + gimp_help_connect_help_accel (filesave, + gimp_standard_help_func, + "save/dialogs/file_save.html"); + + { + GtkWidget *frame; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *option_menu; + GtkWidget *save_menu; + + save_options = gtk_hbox_new (TRUE, 1); + + frame = gtk_frame_new (_("Save Options")); + gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); + gtk_box_pack_start (GTK_BOX (save_options), frame, TRUE, TRUE, 4); + + hbox = gtk_hbox_new (FALSE, 4); + gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); + gtk_container_add (GTK_CONTAINER (frame), hbox); + gtk_widget_show (hbox); + + label = gtk_label_new (_("Determine File Type:")); + gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); + gtk_widget_show (label); + + option_menu = gtk_option_menu_new (); + gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 0); + gtk_widget_show (option_menu); + + menus_get_save_menu (&save_menu, NULL); + gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), save_menu); + + gtk_widget_show (frame); + + /* pack the containing save_options hbox into the save-dialog */ + gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (filesave)->main_vbox), + save_options, FALSE, FALSE, 0); + } + + gtk_widget_show (save_options); +} + +static void +file_save_type_callback (GtkWidget *widget, + gpointer data) +{ + PlugInProcDef *proc = (PlugInProcDef *) data; + + file_update_name (proc, filesave); + + save_file_proc = proc; +} + +static gint +file_save (GimpImage *gimage, + gchar *filename, + gchar *raw_filename, + RunModeType run_mode, + gboolean set_filename) +{ + PlugInProcDef *file_proc; + ProcRecord *proc; + Argument *args; + Argument *return_vals; + gint status; + gint i; + struct stat statbuf; + + if (gimp_image_active_drawable (gimage) == NULL) + return PDB_EXECUTION_ERROR; + + file_proc = gimp_image_get_save_proc (gimage); + + if (!file_proc) + file_proc = file_proc_find (save_procs, raw_filename); + + if (!file_proc) + { + g_message (_("Save failed.\n" + "%s: Unknown file type."), + filename); + + return PDB_CANCEL; /* inhibits error messages by caller */ + } + + /* check if we are saving to a file */ + if (stat (filename, &statbuf) == 0) + { + uid_t euid; + gid_t egid; + + if (! (statbuf.st_mode & S_IFREG)) + { + g_message (_("Save failed.\n" + "%s is not a regular file."), + filename); + + return PDB_CANCEL; /* inhibits error messages by caller */ + } + + euid = geteuid (); + egid = getegid (); + + if (! ((statbuf.st_mode & S_IWUSR) || + + ((statbuf.st_mode & S_IWGRP) && + (statbuf.st_uid != euid)) || + + ((statbuf.st_mode & S_IWOTH) && + (statbuf.st_uid != euid) && + (statbuf.st_gid != egid)))) + { + g_message (_("Save failed.\n" + "%s: Permission denied."), + filename); + + return PDB_CANCEL; /* inhibits error messages by caller */ + } + } + + /* ref the image, so it can't get deleted during save */ + gtk_object_ref (GTK_OBJECT (gimage)); + + proc = &file_proc->db_info; + + args = g_new0 (Argument, proc->num_args); + + for (i = 0; i < proc->num_args; i++) + args[i].arg_type = proc->args[i].arg_type; + + args[0].value.pdb_int = run_mode; + args[1].value.pdb_int = pdb_image_to_id (gimage); + args[2].value.pdb_int = gimp_drawable_get_ID (gimp_image_active_drawable (gimage)); + args[3].value.pdb_pointer = filename; + args[4].value.pdb_pointer = raw_filename; + + return_vals = procedural_db_execute (proc->name, args); + + status = return_vals[0].value.pdb_int; + + if (status == PDB_SUCCESS) + { + /* set this image to clean */ + gimp_image_clean_all (gimage); + + /* these calls must come before the call to gimage_set_filename */ + document_index_add (filename); + menus_last_opened_add (filename); + + /* use the same plug-in for this image next time */ + /* DISABLED - gets stuck on first saved format... needs + attention --Adam */ + /* gimage_set_save_proc(gimage, file_proc); */ + + /* Write a thumbnail for the saved image, where appropriate */ + switch (thumbnail_mode) + { + case 0: + break; + default: + { + TempBuf *tempbuf; + + tempbuf = make_thumb_tempbuf (gimage); + file_save_thumbnail (gimage, filename, tempbuf); + } + } + + if (set_filename) + { + /* set the image title */ + gimp_object_set_name (GIMP_OBJECT (gimage), filename); + /* note: 'filename' may have been free'd by above call! */ + } + } + + g_free (return_vals); + g_free (args); + + gtk_object_unref (GTK_OBJECT (gimage)); + + return status; +} + +/* Set "gimage"s save handler to "save_proc", then save the image. + * Hide the dialog if all went well, otherwise make the user knows an + * error happened and leave the dialog up. Make sure it's sensitive. + */ +static void +file_save_with_proc (GimpImage *gimage, + gchar *full_filename, + gchar *raw_filename, + PlugInProcDef *save_proc, + gboolean set_filename) +{ + gint status = PDB_EXECUTION_ERROR; + + if (gimage != NULL) + { + gimp_image_set_save_proc (gimage, save_proc); + status = file_save (gimage, + full_filename, + raw_filename, + RUN_INTERACTIVE, + set_filename); + + /* hide the file save dialog on success */ + if (status == PDB_SUCCESS) + file_dialog_hide (filesave); + } + + /* If there was an error but file_save() didn't print an error + * message, then we'd better. */ + if (status != PDB_SUCCESS && status != PDB_CANCEL) + g_message (_("Save failed.\n%s"), full_filename); + + /* always make file save dialog sensitive */ + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); +} + + +static void +file_save_ok_callback (GtkWidget *widget, + gpointer data) +{ + GtkFileSelection *fs; + gchar *filename; + gchar *raw_filename; + gchar *dot; + gint x; + struct stat buf; + gint err; + + fs = GTK_FILE_SELECTION (data); + filename = gtk_file_selection_get_filename (fs); + raw_filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)); + + g_assert (filename && raw_filename); + + for (dot = strrchr (filename, '.'), x = 0; dot && *(++dot);) + { + if (*dot != 'e' || ++x < 0) + break; + else if (x > 3 && !strcmp (dot + 1, "k")) + { + ProcRecord *proc_rec; + Argument *args; + GimpDrawable *the_drawable; + + the_drawable = gimp_image_active_drawable (the_gimage); + if (!the_drawable) + return; + + proc_rec = procedural_db_lookup ("plug_in_the_slimy_egg"); + if (!proc_rec) + break; + + file_dialog_hide (filesave); + + args = g_new (Argument, 3); + args[0].arg_type = PDB_INT32; + args[0].value.pdb_int = RUN_INTERACTIVE; + args[1].arg_type = PDB_IMAGE; + args[1].value.pdb_int = pdb_image_to_id (the_gimage); + args[2].arg_type = PDB_DRAWABLE; + args[2].value.pdb_int = the_drawable->ID; + + plug_in_run (proc_rec, args, 3, FALSE, TRUE, 0); + + g_free (args); + + return; + } + } + + err = stat (filename, &buf); + + if (err == 0) + { + if (buf.st_mode & S_IFDIR) + { + if (filename[strlen (filename) - 1] != G_DIR_SEPARATOR) + { + gchar *s = g_strconcat (filename, G_DIR_SEPARATOR_S, NULL); + gtk_file_selection_set_filename (fs, s); + g_free (s); + } + else + gtk_file_selection_set_filename (fs, filename); + } + else + { + gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); + file_overwrite (g_strdup (filename), g_strdup (raw_filename)); + } + } + else + { + gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); + + file_save_with_proc (the_gimage, filename, raw_filename, save_file_proc, + set_filename); + + gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE); + } +} + +static void +file_overwrite (gchar *filename, + gchar *raw_filename) +{ + OverwriteData *overwrite_data; + GtkWidget *query_box; + gchar *overwrite_text; + + overwrite_data = g_new (OverwriteData, 1); + overwrite_data->full_filename = filename; + overwrite_data->raw_filename = raw_filename; + + overwrite_text = g_strdup_printf (_("%s exists, overwrite?"), filename); + + query_box = gimp_query_boolean_box (_("File Exists!"), + gimp_standard_help_func, + "save/file_exists.html", + FALSE, + overwrite_text, + _("Yes"), _("No"), + NULL, NULL, + file_overwrite_callback, + overwrite_data); + + g_free (overwrite_text); + + gtk_widget_show (query_box); +} + +static void +file_overwrite_callback (GtkWidget *widget, + gboolean overwrite, + gpointer data) +{ + OverwriteData *overwrite_data; + + overwrite_data = (OverwriteData *) data; + + if (overwrite) + { + file_save_with_proc (the_gimage, + overwrite_data->full_filename, + overwrite_data->raw_filename, + save_file_proc, + set_filename); + } + + /* always make file save dialog sensitive */ + gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); + + g_free (overwrite_data->full_filename); + g_free (overwrite_data->raw_filename); + g_free (overwrite_data); +} diff --git a/app/file/file-save.h b/app/file/file-save.h new file mode 100644 index 0000000000..864588d08a --- /dev/null +++ b/app/file/file-save.h @@ -0,0 +1,40 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FILE_SAVE_H__ +#define __FILE_SAVE_H__ + + +extern GSList *save_procs; + + +void file_save_pre_init (void); +void file_save_post_init (void); + +void file_save_callback (GtkWidget *widget, + gpointer data); +void file_save_as_callback (GtkWidget *widget, + gpointer data); +void file_save_a_copy_as_callback (GtkWidget *widget, + gpointer data); + +void file_save_by_extension_callback (GtkWidget *widget, + gpointer data); + + +#endif /* __FILE_SAVE_H__ */ diff --git a/app/file/file-utils.c b/app/file/file-utils.c new file mode 100644 index 0000000000..f0dd23b411 --- /dev/null +++ b/app/file/file-utils.c @@ -0,0 +1,759 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis + * Copyright (C) 1997 Josh MacDonald + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#ifdef HAVE_SYS_PARAM_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include + +#ifdef G_OS_WIN32 +#include /* For _mkdir() */ +#define mkdir(path,mode) _mkdir(path) +#endif + +#include "libgimpmath/gimpmath.h" + +#include "apptypes.h" + +#include "file-utils.h" +#include "gdisplay.h" +#include "gimpimage.h" +#include "gimpui.h" +#include "menus.h" +#include "plug_in.h" +#include "temp_buf.h" + + +void +file_update_name (PlugInProcDef *proc, + GtkWidget *filesel) +{ + if (proc->extensions_list) + { + gchar *text; + gchar *last_dot; + GString *s; + + text = gtk_entry_get_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry)); + last_dot = strrchr (text, '.'); + + if (last_dot == text || !text[0]) + return; + + s = g_string_new (text); + + if (last_dot) + g_string_truncate (s, last_dot-text); + + g_string_append (s, "."); + g_string_append (s, (gchar *) proc->extensions_list->data); + + gtk_entry_set_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry), s->str); + + g_string_free (s, TRUE); + } +} + +void +file_update_menus (GSList *procs, + gint image_type) +{ + PlugInProcDef *file_proc; + + while (procs) + { + file_proc = procs->data; + procs = procs->next; + + if (file_proc->db_info.proc_type != PDB_EXTENSION) + menus_set_sensitive (file_proc->menu_path, + (file_proc->image_types_val & image_type)); + } +} + +void +file_dialog_show (GtkWidget *filesel) +{ + menus_set_sensitive ("/File/Open...", FALSE); + menus_set_sensitive ("/File/Open...", FALSE); + menus_set_sensitive ("/File/Save", FALSE); + menus_set_sensitive ("/File/Save as...", FALSE); + menus_set_sensitive ("/File/Save a Copy as...", FALSE); + + gtk_widget_grab_focus (GTK_FILE_SELECTION (filesel)->selection_entry); + gtk_widget_show (filesel); +} + +void +file_dialog_hide (GtkWidget *filesel) +{ + gimp_dialog_hide (filesel); + + menus_set_sensitive ("/File/Open...", TRUE); + menus_set_sensitive ("/File/Open...", TRUE); + + if (gdisplay_active ()) + { + menus_set_sensitive ("/File/Save", TRUE); + menus_set_sensitive ("/File/Save as...", TRUE); + menus_set_sensitive ("/File/Save a Copy as...", TRUE); + } +} + +static PlugInProcDef * +file_proc_find_by_name (GSList *procs, + const gchar *filename, + gboolean skip_magic) +{ + GSList *p; + gchar *ext = strrchr (filename, '.'); + + if (ext) + ext++; + + for (p = procs; p; p = g_slist_next (p)) + { + PlugInProcDef *proc = p->data; + GSList *prefixes; + + if (skip_magic && proc->magics_list) + continue; + + for (prefixes = proc->prefixes_list; + prefixes; + prefixes = g_slist_next (prefixes)) + { + if (strncmp (filename, prefixes->data, strlen (prefixes->data)) == 0) + return proc; + } + } + + for (p = procs; p; p = g_slist_next (p)) + { + PlugInProcDef *proc = p->data; + GSList *extensions; + + for (extensions = proc->extensions_list; + ext && extensions; + extensions = g_slist_next (extensions)) + { + gchar *p1 = ext; + gchar *p2 = (gchar *) extensions->data; + + if (skip_magic && proc->magics_list) + continue; + + while (*p1 && *p2) + { + if (tolower (*p1) != tolower (*p2)) + break; + + p1++; + p2++; + } + + if (!(*p1) && !(*p2)) + return proc; + } + } + + return NULL; +} + +PlugInProcDef * +file_proc_find (GSList *procs, + const gchar *filename) +{ + PlugInProcDef *file_proc; + PlugInProcDef *size_matched_proc = NULL; + GSList *all_procs = procs; + FILE *ifp = NULL; + gint head_size = -2; + gint size_match_count = 0; + gint match_val; + guchar head[256]; + + /* First, check magicless prefixes/suffixes */ + if ( (file_proc = file_proc_find_by_name (all_procs, filename, TRUE)) != NULL) + return file_proc; + + /* Then look for magics */ + while (procs) + { + file_proc = procs->data; + procs = procs->next; + + if (file_proc->magics_list) + { + if (head_size == -2) + { + head_size = 0; + if ((ifp = fopen (filename, "rb")) != NULL) + head_size = fread ((gchar *) head, 1, sizeof (head), ifp); + } + if (head_size >= 4) + { + match_val = file_check_magic_list (file_proc->magics_list, + head_size, head, ifp); + if (match_val == 2) /* size match ? */ + { /* Use it only if no other magic matches */ + size_match_count++; + size_matched_proc = file_proc; + } + else if (match_val) + { + fclose (ifp); + return (file_proc); + } + } + } + } + if (ifp) fclose (ifp); + if (size_match_count == 1) + return (size_matched_proc); + + /* As a last ditch, try matching by name */ + return file_proc_find_by_name (all_procs, filename, FALSE); +} + +static void +file_convert_string (gchar *instr, + gchar *outmem, + gint maxmem, + gint *nmem) +{ + /* Convert a string in C-notation to array of char */ + guchar *uin = (guchar *) instr; + guchar *uout = (guchar *) outmem; + guchar tmp[5], *tmpptr; + gint k; + + while ((*uin != '\0') && ((((char *)uout) - outmem) < maxmem)) + { + if (*uin != '\\') /* Not an escaped character ? */ + { + *(uout++) = *(uin++); + continue; + } + if (*(++uin) == '\0') + { + *(uout++) = '\\'; + break; + } + switch (*uin) + { + case '0': case '1': case '2': case '3': /* octal */ + for (tmpptr = tmp; (tmpptr-tmp) <= 3;) + { + *(tmpptr++) = *(uin++); + if ( (*uin == '\0') || (!isdigit (*uin)) + || (*uin == '8') || (*uin == '9')) + break; + } + *tmpptr = '\0'; + sscanf ((char *)tmp, "%o", &k); + *(uout++) = k; + break; + + case 'a': *(uout++) = '\a'; uin++; break; + case 'b': *(uout++) = '\b'; uin++; break; + case 't': *(uout++) = '\t'; uin++; break; + case 'n': *(uout++) = '\n'; uin++; break; + case 'v': *(uout++) = '\v'; uin++; break; + case 'f': *(uout++) = '\f'; uin++; break; + case 'r': *(uout++) = '\r'; uin++; break; + + default : *(uout++) = *(uin++); break; + } + } + *nmem = ((gchar *) uout) - outmem; +} + +static gint +file_check_single_magic (gchar *offset, + gchar *type, + gchar *value, + gint headsize, + guchar *file_head, + FILE *ifp) + +{ + /* Return values are 0: no match, 1: magic match, 2: size match */ + glong offs; + gulong num_testval, num_operatorval; + gulong fileval; + gint numbytes, k, c = 0, found = 0; + gchar *num_operator_ptr, num_operator, num_test; + guchar mem_testval[256]; + + /* Check offset */ + if (sscanf (offset, "%ld", &offs) != 1) return (0); + if (offs < 0) return (0); + + /* Check type of test */ + num_operator_ptr = NULL; + num_operator = '\0'; + num_test = '='; + if (strncmp (type, "byte", 4) == 0) + { + numbytes = 1; + num_operator_ptr = type+4; + } + else if (strncmp (type, "short", 5) == 0) + { + numbytes = 2; + num_operator_ptr = type+5; + } + else if (strncmp (type, "long", 4) == 0) + { + numbytes = 4; + num_operator_ptr = type+4; + } + else if (strncmp (type, "size", 4) == 0) + { + numbytes = 5; + } + else if (strcmp (type, "string") == 0) + { + numbytes = 0; + } + else return (0); + + /* Check numerical operator value if present */ + if (num_operator_ptr && (*num_operator_ptr == '&')) + { + if (isdigit (num_operator_ptr[1])) + { + if (num_operator_ptr[1] != '0') /* decimal */ + sscanf (num_operator_ptr+1, "%ld", &num_operatorval); + else if (num_operator_ptr[2] == 'x') /* hexadecimal */ + sscanf (num_operator_ptr+3, "%lx", &num_operatorval); + else /* octal */ + sscanf (num_operator_ptr+2, "%lo", &num_operatorval); + num_operator = *num_operator_ptr; + } + } + + if (numbytes > 0) /* Numerical test ? */ + { + /* Check test value */ + if ((value[0] == '=') || (value[0] == '>') || (value[0] == '<')) + { + num_test = value[0]; + value++; + } + if (!isdigit (value[0])) return (0); + + /* + * to anybody reading this: is strtol's parsing behaviour (e.g. "0x" prefix) + * broken on some systems or why do we do the base detection ourselves? + * */ + if (value[0] != '0') /* decimal */ + num_testval = strtol(value, NULL, 10); + else if (value[1] == 'x') /* hexadecimal */ + num_testval = (unsigned long)strtoul(value+2, NULL, 16); + else /* octal */ + num_testval = strtol(value+1, NULL, 8); + + fileval = 0; + if (numbytes == 5) /* Check for file size ? */ + { + struct stat buf; + + if (fstat (fileno (ifp), &buf) < 0) return (0); + fileval = buf.st_size; + } + else if (offs + numbytes <= headsize) /* We have it in memory ? */ + { + for (k = 0; k < numbytes; k++) + fileval = (fileval << 8) | (long)file_head[offs+k]; + } + else /* Read it from file */ + { + if (fseek (ifp, offs, SEEK_SET) < 0) return (0); + for (k = 0; k < numbytes; k++) + fileval = (fileval << 8) | (c = getc (ifp)); + if (c == EOF) return (0); + } + if (num_operator == '&') + fileval &= num_operatorval; + + if (num_test == '<') + found = (fileval < num_testval); + else if (num_test == '>') + found = (fileval > num_testval); + else + found = (fileval == num_testval); + + if (found && (numbytes == 5)) found = 2; + } + else if (numbytes == 0) /* String test */ + { + file_convert_string ((char *)value, (char *)mem_testval, + sizeof (mem_testval), &numbytes); + if (numbytes <= 0) return (0); + + if (offs + numbytes <= headsize) /* We have it in memory ? */ + { + found = (memcmp (mem_testval, file_head+offs, numbytes) == 0); + } + else /* Read it from file */ + { + if (fseek (ifp, offs, SEEK_SET) < 0) return (0); + found = 1; + for (k = 0; found && (k < numbytes); k++) + { + c = getc (ifp); + found = (c != EOF) && (c == (int)mem_testval[k]); + } + } + } + + return found; +} + +gint +file_check_magic_list (GSList *magics_list, + gint headsize, + guchar *head, + FILE *ifp) + +{ + /* Return values are 0: no match, 1: magic match, 2: size match */ + gchar *offset; + gchar *type; + gchar *value; + gint and = 0; + gint found = 0; + gint match_val; + + while (magics_list) + { + if ((offset = (gchar *)magics_list->data) == NULL) break; + if ((magics_list = magics_list->next) == NULL) break; + if ((type = (gchar *)magics_list->data) == NULL) break; + if ((magics_list = magics_list->next) == NULL) break; + if ((value = (gchar *)magics_list->data) == NULL) break; + magics_list = magics_list->next; + + match_val = file_check_single_magic (offset, type, value, + headsize, head, ifp); + if (and) + found = found && match_val; + else + found = match_val; + + and = (strchr (offset, '&') != NULL); + + if ((!and) && found) + return match_val; + } + + return 0; +} + +TempBuf * +make_thumb_tempbuf (GimpImage *gimage) +{ + gint w, h; + + if (gimage->width<=80 && gimage->height<=60) + { + w = gimage->width; + h = gimage->height; + } + else + { + /* Ratio molesting to fit within .xvpic thumbnail size limits */ + if (60 * gimage->width < 80 * gimage->height) + { + h = 60; + w = (60 * gimage->width) / gimage->height; + if (w == 0) + w = 1; + } + else + { + w = 80; + h = (80 * gimage->height) / gimage->width; + if (h == 0) + h = 1; + } + } + + /*printf("tn: %d x %d -> ", w, h);fflush(stdout);*/ + + return gimp_viewable_get_preview (GIMP_VIEWABLE (gimage), w, h); +} + +/* The readXVThumb function source may be re-used under + the XFree86-style license. */ +guchar * +readXVThumb (const gchar *fnam, + gint *w, + gint *h, + gchar **imginfo /* caller frees if != NULL */) +{ + FILE *fp; + const gchar *P7_332 = "P7 332"; + gchar P7_buf[7]; + gchar linebuf[200]; + guchar *buf; + gint twofivefive; + void *ptr; + + *w = *h = 0; + *imginfo = NULL; + + fp = fopen (fnam, "rb"); + if (!fp) + return NULL; + + fread (P7_buf, 6, 1, fp); + + if (strncmp(P7_buf, P7_332, 6)!=0) + { + g_warning ("Thumbnail doesn't have the 'P7 332' header."); + fclose (fp); + return NULL; + } + + /*newline*/ + fread (P7_buf, 1, 1, fp); + + do + { + ptr = fgets(linebuf, 199, fp); + if ((strncmp(linebuf, "#IMGINFO:", 9) == 0) && + (linebuf[9] != '\0') && + (linebuf[9] != '\n')) + { + if (linebuf[strlen(linebuf)-1] == '\n') + linebuf[strlen(linebuf)-1] = '\0'; + + if (linebuf[9] != '\0') + { + if (*imginfo) + g_free(*imginfo); + *imginfo = g_strdup (&linebuf[9]); + } + } + } + while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */ + + if (!ptr) + { + /* g_warning("Thumbnail ended - not an image?"); */ + fclose (fp); + return NULL; + } + + sscanf(linebuf, "%d %d %d\n", w, h, &twofivefive); + + if (twofivefive!=255) + { + g_warning ("Thumbnail is of funky depth."); + fclose (fp); + return NULL; + } + + if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60) + { + g_warning ("Thumbnail size bad. Corrupted?"); + fclose (fp); + return NULL; + } + + buf = g_malloc ((*w) * (*h)); + + fread (buf, (*w) * (*h), 1, fp); + + fclose (fp); + + return buf; +} + +gboolean +file_save_thumbnail (GimpImage *gimage, + const gchar *full_source_filename, + TempBuf *tempbuf) +{ + gint i,j; + gint w,h; + guchar *tbd; + gchar *pathname; + gchar *filename; + gchar *xvpathname; + gchar *thumbnailname; + GimpImageBaseType basetype; + FILE *fp; + struct stat statbuf; + + if (stat (full_source_filename, &statbuf) != 0) + { + return FALSE; + } + + /* just for debugging + * if (gimp_image_preview_valid (gimage, GRAY_CHANNEL)) + * { + * g_print ("(incidentally, gimage already has a valid preview - %dx%d)\n", + * gimage->comp_preview->width, + * gimage->comp_preview->height); + * } + */ + + pathname = g_dirname (full_source_filename); + filename = g_basename (full_source_filename); /* Don't free! */ + + xvpathname = g_strconcat (pathname, G_DIR_SEPARATOR_S, ".xvpics", + NULL); + + thumbnailname = g_strconcat (xvpathname, G_DIR_SEPARATOR_S, + filename, + NULL); + + tbd = temp_buf_data (tempbuf); + + w = tempbuf->width; + h = tempbuf->height; + /*printf("tn: %d x %d\n", w, h);fflush(stdout);*/ + + mkdir (xvpathname, 0755); + + fp = fopen (thumbnailname, "wb"); + g_free (pathname); + g_free (xvpathname); + g_free (thumbnailname); + + if (fp) + { + basetype = gimp_image_base_type (gimage); + + fprintf (fp, + "P7 332\n#IMGINFO:%dx%d %s (%d %s)\n" + "#END_OF_COMMENTS\n%d %d 255\n", + gimage->width, gimage->height, + (basetype == RGB) ? "RGB" : + (basetype == GRAY) ? "Greyscale" : + (basetype == INDEXED) ? "Indexed" : + "(UNKNOWN COLOUR TYPE)", + (int)statbuf.st_size, + (statbuf.st_size == 1) ? "byte" : "bytes", + w, h); + + switch (basetype) + { + case INDEXED: + case RGB: + for (i=0; i>5)<<5) | ((g>>5)<<2) | (b>>6), fp); + + rerr = r - ( (r>>5) * 255 ) / 7; + gerr = g - ( (g>>5) * 255 ) / 7; + berr = b - ( (b>>6) * 255 ) / 3; + } + } + break; + + case GRAY: + for (i=0; i>5)<<5) | ((b3>>5)<<2) | (b2>>6), fp); + + b2err = b2 - ( (b2>>6) * 255 ) / 3; + b3err = b3 - ( (b3>>5) * 255 ) / 7; + } + } + break; + + default: + g_warning("UNKNOWN GIMAGE TYPE IN THUMBNAIL SAVE"); + break; + } + + fclose (fp); + } + else /* Error writing thumbnail */ + { + return FALSE; + } + + return TRUE; +} diff --git a/app/file/file-utils.h b/app/file/file-utils.h new file mode 100644 index 0000000000..e57ac666bd --- /dev/null +++ b/app/file/file-utils.h @@ -0,0 +1,54 @@ +/* The GIMP -- an image manipulation program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifndef __FILE_UTILS_H__ +#define __FILE_UTILS_H__ + + +#include + + +void file_dialog_show (GtkWidget *filesel); +void file_dialog_hide (GtkWidget *filesel); + +void file_update_name (PlugInProcDef *proc, + GtkWidget *filesel); +void file_update_menus (GSList *procs, + gint image_type); + +PlugInProcDef * file_proc_find (GSList *procs, + const gchar *filename); + +/* Return values are 0: no match, 1: magic match, 2: size match */ +gint file_check_magic_list (GSList *magics_list, + gint headsize, + guchar *head, + FILE *ifp); + + +TempBuf * make_thumb_tempbuf (GimpImage *gimage); +guchar * readXVThumb (const gchar *fnam, + gint *w, + gint *h, + gchar **imginfo /* caller frees if != NULL */); +gboolean file_save_thumbnail (GimpImage *gimage, + const char *full_source_filename, + TempBuf *tempbuf); + + +#endif /* __FILE_UTILS_H__ */ diff --git a/app/fileops.c b/app/fileops.c deleted file mode 100644 index f608a58671..0000000000 --- a/app/fileops.c +++ /dev/null @@ -1,2463 +0,0 @@ -/* The GIMP -- an image manipulation program - * Copyright (C) 1995, 1996, 1997 Spencer Kimball and Peter Mattis - * Copyright (C) 1997 Josh MacDonald - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#include "config.h" - -#ifdef HAVE_SYS_PARAM_H -#include -#endif - -#include - -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_UNISTD_H -#include -#endif -#include - -#ifdef G_OS_WIN32 -#include /* For _mkdir() */ -#define mkdir(path,mode) _mkdir(path) -#include -#ifndef S_IWUSR -#define S_IWUSR _S_IWRITE -#endif -#ifndef S_IRUSR -#define S_IRUSR _S_IREAD -#endif -#ifndef S_IWGRP -#define S_IWGRP (_S_IWRITE>>3) -#define S_IWOTH (_S_IWRITE>>6) -#endif -#ifndef S_IRGRP -#define S_IRGRP (_S_IREAD>>3) -#define S_IROTH (_S_IREAD>>6) -#endif -#define uid_t gint -#define gid_t gint -#define geteuid() 0 -#define getegid() 0 -#endif - -#include "libgimpmath/gimpmath.h" -#include "libgimpwidgets/gimpwidgets.h" - -#include "apptypes.h" - -#include "cursorutil.h" -#include "dialog_handler.h" -#include "docindex.h" -#include "gimpdrawable.h" -#include "gdisplay.h" -#include "gimage.h" -#include "gimpcontext.h" -#include "gimpdrawable.h" -#include "gimpui.h" -#include "gimprc.h" -#include "fileops.h" -#include "fileopsP.h" -#include "menus.h" -#include "plug_in.h" -#include "temp_buf.h" -#include "undo.h" - -#include "libgimp/gimpintl.h" - - -#define REVERT_DATA_KEY "revert_confirm_dialog" - - -typedef struct _OverwriteData OverwriteData; - -struct _OverwriteData -{ - gchar *full_filename; - gchar *raw_filename; -}; - - -static void file_open_dialog_create (void); -static void file_save_dialog_create (void); - -static void file_overwrite (gchar *filename, - gchar *raw_filename); -static void file_overwrite_callback (GtkWidget *widget, - gboolean overwrite, - gpointer data); -static void file_revert_confirm_callback (GtkWidget *widget, - gboolean revert, - gpointer data); - -static GimpImage * file_open_image (const gchar *filename, - const gchar *raw_filename, - const gchar *open_mode, - RunModeType run_mode, - gint *status); - -static gint file_save (GimpImage *gimage, - gchar *filename, - gchar *raw_filename, - RunModeType run_mode, - gboolean set_filename); - -static void file_open_genbutton_callback (GtkWidget *widget, - gpointer data); - -static void file_open_clistrow_callback (GtkWidget *widget, - gint row); - -static void file_open_ok_callback (GtkWidget *widget, - gpointer data); - -static void file_save_ok_callback (GtkWidget *widget, - gpointer data); - -static void file_dialog_show (GtkWidget *filesel); -static gint file_dialog_hide (GtkWidget *filesel); -static void file_update_name (PlugInProcDef *proc, - GtkWidget *filesel); - -static void file_open_type_callback (GtkWidget *widget, - gpointer data); -static void file_save_type_callback (GtkWidget *widget, - gpointer data); - -static void file_convert_string (gchar *instr, - gchar *outmem, - gint maxmem, - gint *nmem); - -static gchar * file_absolute_filename (gchar *name); - -static gint file_check_single_magic (gchar *offset, - gchar *type, - gchar *value, - gint headsize, - guchar *file_head, - FILE *ifp); - -static gint file_check_magic_list (GSList *magics_list, - gint headsize, - guchar *head, - FILE *ifp); - -static void file_update_menus (GSList *procs, - gint image_type); - -static GSList* clist_to_slist (GtkCList *file_list); - - - -static GtkWidget *fileload = NULL; -static GtkWidget *filesave = NULL; -static GtkWidget *open_options = NULL; -static GtkWidget *save_options = NULL; - -/* widgets for the open_options menu */ -static GtkPreview *open_options_preview = NULL; -static GtkWidget *open_options_fixed = NULL; -static GtkWidget *open_options_label = NULL; -static GtkWidget *open_options_frame = NULL; -static GtkWidget *open_options_genbuttonlabel = NULL; - -/* Some state for the thumbnailer */ -static gchar *preview_fullname = NULL; - -GSList *load_procs = NULL; -GSList *save_procs = NULL; - -static PlugInProcDef *load_file_proc = NULL; -static PlugInProcDef *save_file_proc = NULL; - -static GimpImage *the_gimage = NULL; -static gboolean set_filename = TRUE; - -extern GSList *display_list; /* from gdisplay.c */ - - -void -file_ops_pre_init (void) -{ -} - -void -file_ops_post_init (void) -{ - GimpItemFactoryEntry entry; - PlugInProcDef *file_proc; - GSList *tmp; - - load_procs = g_slist_reverse (load_procs); - save_procs = g_slist_reverse (save_procs); - - for (tmp = load_procs; tmp; tmp = g_slist_next (tmp)) - { - gchar *help_page; - - file_proc = tmp->data; - - help_page = g_strconcat ("filters/", - g_basename (file_proc->prog), - ".html", - NULL); - g_strdown (help_page); - - entry.entry.path = file_proc->menu_path; - entry.entry.accelerator = NULL; - entry.entry.callback = file_open_type_callback; - entry.entry.callback_action = 0; - entry.entry.item_type = NULL; - entry.help_page = help_page; - entry.description = NULL; - - menus_create_item_from_full_path (&entry, NULL, file_proc); - } - - for (tmp = save_procs; tmp; tmp = g_slist_next (tmp)) - { - gchar *help_page; - - file_proc = tmp->data; - - help_page = g_strconcat ("filters/", - g_basename (file_proc->prog), - ".html", - NULL); - g_strdown (help_page); - - entry.entry.path = file_proc->menu_path; - entry.entry.accelerator = NULL; - entry.entry.callback = file_save_type_callback; - entry.entry.callback_action = 0; - entry.entry.item_type = NULL; - entry.help_page = help_page; - entry.description = NULL; - - menus_create_item_from_full_path (&entry, NULL, file_proc); - } -} - -static void -file_open_dialog_create (void) -{ - fileload = gtk_file_selection_new (_("Load Image")); - gtk_window_set_position (GTK_WINDOW (fileload), GTK_WIN_POS_MOUSE); - gtk_window_set_wmclass (GTK_WINDOW (fileload), "load_image", "Gimp"); - - gtk_container_set_border_width (GTK_CONTAINER (fileload), 2); - gtk_container_set_border_width - (GTK_CONTAINER (GTK_FILE_SELECTION (fileload)->button_area), 2); - - dialog_register_fileload (fileload); - - gtk_signal_connect_object - (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->cancel_button), "clicked", - GTK_SIGNAL_FUNC (file_dialog_hide), - GTK_OBJECT (fileload)); - gtk_signal_connect (GTK_OBJECT (fileload), "delete_event", - GTK_SIGNAL_FUNC (file_dialog_hide), - NULL); - gtk_signal_connect - (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->ok_button), "clicked", - GTK_SIGNAL_FUNC (file_open_ok_callback), - fileload); - gtk_quit_add_destroy (1, GTK_OBJECT (fileload)); - - gtk_clist_set_selection_mode - (GTK_CLIST (GTK_FILE_SELECTION (fileload)->file_list), - GTK_SELECTION_EXTENDED); - - /* Catch file-clist clicks so we can update the preview thumbnail */ - gtk_signal_connect - (GTK_OBJECT (GTK_FILE_SELECTION (fileload)->file_list), "select_row", - GTK_SIGNAL_FUNC (file_open_clistrow_callback), - fileload); - - /* Connect the "F1" help key */ - gimp_help_connect_help_accel (fileload, - gimp_standard_help_func, - "open/dialogs/file_open.html"); - - { - GtkWidget *frame; - GtkWidget *vbox; - GtkWidget *hbox; - GtkWidget *option_menu; - GtkWidget *load_menu; - GtkWidget *open_options_genbutton; - - open_options = gtk_hbox_new (TRUE, 1); - - /* format-chooser frame */ - frame = gtk_frame_new (_("Determine File Type")); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); - gtk_box_pack_start (GTK_BOX (open_options), frame, TRUE, TRUE, 4); - - vbox = gtk_vbox_new (FALSE, 2); - gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); - gtk_container_add (GTK_CONTAINER (frame), vbox); - - hbox = gtk_hbox_new (FALSE, 0); - gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - option_menu = gtk_option_menu_new (); - gtk_box_pack_start (GTK_BOX (hbox), option_menu, FALSE, FALSE, 0); - gtk_widget_show (option_menu); - - menus_get_load_menu (&load_menu, NULL); - gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), load_menu); - - gtk_widget_show (vbox); - gtk_widget_show (frame); - - /* Preview frame */ - open_options_frame = frame = gtk_frame_new (""); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); - gtk_box_pack_end (GTK_BOX (open_options), frame, FALSE, TRUE, 4); - - vbox = gtk_vbox_new (FALSE, 2); - gtk_container_set_border_width (GTK_CONTAINER (vbox), 4); - gtk_container_add (GTK_CONTAINER (frame), vbox); - - hbox = gtk_hbox_new (TRUE, 0); - gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); - gtk_widget_show (hbox); - - open_options_genbutton = gtk_button_new (); - gtk_signal_connect (GTK_OBJECT (open_options_genbutton), "clicked", - GTK_SIGNAL_FUNC (file_open_genbutton_callback), - fileload); - gtk_box_pack_start (GTK_BOX (hbox), open_options_genbutton, - TRUE, FALSE, 0); - gtk_widget_show (open_options_genbutton); - - open_options_fixed = gtk_fixed_new (); - gtk_widget_set_usize (open_options_fixed, 80, 60); - gtk_container_add (GTK_CONTAINER (GTK_BIN (open_options_genbutton)), - open_options_fixed); - gtk_widget_show (open_options_fixed); - - { - GtkWidget* abox; - GtkWidget* sbox; - GtkWidget* align; - - sbox = gtk_vbox_new (TRUE, 0); - gtk_container_add (GTK_CONTAINER (open_options_fixed), sbox); - gtk_widget_show (sbox); - - align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0); - gtk_widget_set_usize (align, 80, 60); - gtk_box_pack_start (GTK_BOX (sbox), align, FALSE, TRUE, 0); - gtk_widget_show (align); - - abox = gtk_hbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (align), abox); - gtk_widget_show (abox); - - open_options_preview = - GTK_PREVIEW (gtk_preview_new (GTK_PREVIEW_COLOR)); - gtk_box_pack_start (GTK_BOX (abox), GTK_WIDGET (open_options_preview), - FALSE, TRUE, 0); - gtk_widget_show (GTK_WIDGET (open_options_preview)); - - open_options_genbuttonlabel = gtk_label_new (_("Generate\nPreview")); - gtk_box_pack_start (GTK_BOX (abox), open_options_genbuttonlabel, - FALSE, TRUE, 0); - gtk_widget_show (open_options_genbuttonlabel); - } - - open_options_label = gtk_label_new (""); - gtk_box_pack_start (GTK_BOX (vbox), open_options_label, FALSE, FALSE, 0); - gtk_widget_show (open_options_label); - - gtk_widget_show (vbox); - gtk_widget_show (frame); - - /* pack the containing open_options hbox into the open-dialog */ - gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (fileload)->main_vbox), - open_options, FALSE, FALSE, 0); - } - - gtk_frame_set_label (GTK_FRAME (open_options_frame), _("Preview")); - gtk_label_set_text (GTK_LABEL (open_options_label), _("No Selection.")); - - gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel)); - gtk_widget_hide (GTK_WIDGET (open_options_preview)); - gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), FALSE); - - gtk_widget_show (open_options); -} - -static void -file_save_dialog_create (void) -{ - filesave = gtk_file_selection_new (_("Save Image")); - gtk_window_set_wmclass (GTK_WINDOW (filesave), "save_image", "Gimp"); - gtk_window_set_position (GTK_WINDOW (filesave), GTK_WIN_POS_MOUSE); - - gtk_container_set_border_width (GTK_CONTAINER (filesave), 2); - gtk_container_set_border_width - (GTK_CONTAINER (GTK_FILE_SELECTION (filesave)->button_area), 2); - - gtk_signal_connect_object - (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->cancel_button), "clicked", - GTK_SIGNAL_FUNC (file_dialog_hide), - GTK_OBJECT (filesave)); - gtk_signal_connect (GTK_OBJECT (filesave), "delete_event", - GTK_SIGNAL_FUNC (file_dialog_hide), - NULL); - gtk_signal_connect - (GTK_OBJECT (GTK_FILE_SELECTION (filesave)->ok_button), "clicked", - GTK_SIGNAL_FUNC (file_save_ok_callback), - filesave); - gtk_quit_add_destroy (1, GTK_OBJECT (filesave)); - - /* Connect the "F1" help key */ - gimp_help_connect_help_accel (filesave, - gimp_standard_help_func, - "save/dialogs/file_save.html"); - - { - GtkWidget *frame; - GtkWidget *hbox; - GtkWidget *label; - GtkWidget *option_menu; - GtkWidget *save_menu; - - save_options = gtk_hbox_new (TRUE, 1); - - frame = gtk_frame_new (_("Save Options")); - gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN); - gtk_box_pack_start (GTK_BOX (save_options), frame, TRUE, TRUE, 4); - - hbox = gtk_hbox_new (FALSE, 4); - gtk_container_set_border_width (GTK_CONTAINER (hbox), 4); - gtk_container_add (GTK_CONTAINER (frame), hbox); - gtk_widget_show (hbox); - - label = gtk_label_new (_("Determine File Type:")); - gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); - gtk_widget_show (label); - - option_menu = gtk_option_menu_new (); - gtk_box_pack_start (GTK_BOX (hbox), option_menu, TRUE, TRUE, 0); - gtk_widget_show (option_menu); - - menus_get_save_menu (&save_menu, NULL); - gtk_option_menu_set_menu (GTK_OPTION_MENU (option_menu), save_menu); - - gtk_widget_show (frame); - - /* pack the containing save_options hbox into the save-dialog */ - gtk_box_pack_end (GTK_BOX (GTK_FILE_SELECTION (filesave)->main_vbox), - save_options, FALSE, FALSE, 0); - } - - gtk_widget_show (save_options); -} - -void -file_open_callback (GtkWidget *widget, - gpointer data) -{ - if (!fileload) - file_open_dialog_create (); - - gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE); - if (GTK_WIDGET_VISIBLE (fileload)) - return; - - gtk_file_selection_set_filename (GTK_FILE_SELECTION (fileload), - "." G_DIR_SEPARATOR_S); - gtk_window_set_title (GTK_WINDOW (fileload), _("Load Image")); - - file_dialog_show (fileload); -} - -void -file_save_callback (GtkWidget *widget, - gpointer data) -{ - GDisplay *gdisplay; - - gdisplay = gdisplay_active (); - if (! gdisplay) - return; - - if (! gimp_image_active_drawable (gdisplay->gimage)) - return; - - /* Only save if the gimage has been modified */ - if (!trust_dirty_flag || gdisplay->gimage->dirty != 0) - { - gchar *filename; - - filename = - g_strdup (gimp_object_get_name (GIMP_OBJECT (gdisplay->gimage))); - - if (! filename) - { - file_save_as_callback (widget, data); - } - else - { - gchar *raw_filename; - gint status; - - raw_filename = g_basename (filename); - - status = file_save (gdisplay->gimage, - filename, - raw_filename, - RUN_WITH_LAST_VALS, - TRUE); - - if (status != PDB_SUCCESS && - status != PDB_CANCEL) - { - g_message (_("Save failed.\n%s"), filename); - } - - } - - g_free (filename); - } -} - -void -file_save_as_callback (GtkWidget *widget, - gpointer data) -{ - GDisplay *gdisplay; - const gchar *filename; - - gdisplay = gdisplay_active (); - if (! gdisplay) - return; - - if (! gimp_image_active_drawable (gdisplay->gimage)) - return; - - the_gimage = gdisplay->gimage; - - set_filename = TRUE; - - filename = gimp_object_get_name (GIMP_OBJECT (the_gimage)); - - if (! filesave) - file_save_dialog_create (); - - gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); - if (GTK_WIDGET_VISIBLE (filesave)) - return; - - gtk_window_set_title (GTK_WINDOW (filesave), _("Save Image")); - - gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesave), - filename ? - filename : - "." G_DIR_SEPARATOR_S); - - switch (gimp_drawable_type (gimp_image_active_drawable (gdisplay->gimage))) - { - case RGB_GIMAGE: - file_update_menus (save_procs, PLUG_IN_RGB_IMAGE); - break; - case RGBA_GIMAGE: - file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE); - break; - case GRAY_GIMAGE: - file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE); - break; - case GRAYA_GIMAGE: - file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE); - break; - case INDEXED_GIMAGE: - file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE); - break; - case INDEXEDA_GIMAGE: - file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE); - break; - } - - file_dialog_show (filesave); -} - -void -file_save_a_copy_as_callback (GtkWidget *widget, - gpointer data) -{ - GDisplay *gdisplay; - const gchar *filename; - - gdisplay = gdisplay_active (); - if (! gdisplay) - return; - - if (! gimp_image_active_drawable (gdisplay->gimage)) - return; - - the_gimage = gdisplay->gimage; - - set_filename = FALSE; - - filename = gimp_object_get_name (GIMP_OBJECT (the_gimage)); - - if (!filesave) - file_save_dialog_create (); - - gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); - if (GTK_WIDGET_VISIBLE (filesave)) - return; - - gtk_window_set_title (GTK_WINDOW (filesave), _("Save a Copy of the Image")); - - gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesave), - filename ? - filename : - "." G_DIR_SEPARATOR_S); - - switch (gimp_drawable_type (gimp_image_active_drawable (gdisplay->gimage))) - { - case RGB_GIMAGE: - file_update_menus (save_procs, PLUG_IN_RGB_IMAGE); - break; - case RGBA_GIMAGE: - file_update_menus (save_procs, PLUG_IN_RGBA_IMAGE); - break; - case GRAY_GIMAGE: - file_update_menus (save_procs, PLUG_IN_GRAY_IMAGE); - break; - case GRAYA_GIMAGE: - file_update_menus (save_procs, PLUG_IN_GRAYA_IMAGE); - break; - case INDEXED_GIMAGE: - file_update_menus (save_procs, PLUG_IN_INDEXED_IMAGE); - break; - case INDEXEDA_GIMAGE: - file_update_menus (save_procs, PLUG_IN_INDEXEDA_IMAGE); - break; - } - - file_dialog_show (filesave); -} - -void -file_revert_callback (GtkWidget *widget, - gpointer data) -{ - GDisplay *gdisplay; - GimpImage *gimage; - GtkWidget *query_box; - const gchar *filename; - - gdisplay = gdisplay_active (); - if (!gdisplay || !gdisplay->gimage) - return; - - gimage = gdisplay->gimage; - - filename = gimp_object_get_name (GIMP_OBJECT (gimage)); - - query_box = gtk_object_get_data (GTK_OBJECT (gimage), REVERT_DATA_KEY); - - if (! filename) - { - g_message (_("Revert failed.\n" - "No filename associated with this image.")); - } - else if (query_box) - { - gdk_window_raise (query_box->window); - } - else - { - gchar *text; - - text = g_strdup_printf (_("Reverting %s to\n" - "%s\n\n" - "(You will lose all your changes\n" - "including all undo information)"), - g_basename (filename), - filename); - - query_box = gimp_query_boolean_box (_("Revert Image?"), - gimp_standard_help_func, - "file/revert.html", - FALSE, - text, - _("Yes"), _("No"), - GTK_OBJECT (gimage), "destroy", - file_revert_confirm_callback, - gimage); - - g_free (text); - - gtk_object_set_data (GTK_OBJECT (gimage), REVERT_DATA_KEY, query_box); - - gtk_widget_show (query_box); - } -} - -void -file_open_by_extension_callback (GtkWidget *widget, - gpointer data) -{ - load_file_proc = NULL; -} - -void -file_save_by_extension_callback (GtkWidget *widget, - gpointer data) -{ - save_file_proc = NULL; -} - -static void -file_update_name (PlugInProcDef *proc, - GtkWidget *filesel) -{ - if (proc->extensions_list) - { - gchar *text; - gchar *last_dot; - GString *s; - - text = gtk_entry_get_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry)); - last_dot = strrchr (text, '.'); - - if (last_dot == text || !text[0]) - return; - - s = g_string_new (text); - - if (last_dot) - g_string_truncate (s, last_dot-text); - - g_string_append (s, "."); - g_string_append (s, (gchar *) proc->extensions_list->data); - - gtk_entry_set_text (GTK_ENTRY (GTK_FILE_SELECTION (filesel)->selection_entry), s->str); - - g_string_free (s, TRUE); - } -} - -static void -file_open_type_callback (GtkWidget *widget, - gpointer data) -{ - PlugInProcDef *proc = (PlugInProcDef *) data; - - file_update_name (proc, fileload); - - load_file_proc = proc; -} - -static void -file_save_type_callback (GtkWidget *widget, - gpointer data) -{ - PlugInProcDef *proc = (PlugInProcDef *) data; - - file_update_name (proc, filesave); - - save_file_proc = proc; -} - -static GimpImage * -file_open_image (const gchar *filename, - const gchar *raw_filename, - const gchar *open_mode, - RunModeType run_mode, - gint *status) -{ - PlugInProcDef *file_proc; - ProcRecord *proc; - Argument *args; - Argument *return_vals; - gint gimage_id; - gint i; - struct stat statbuf; - - *status = PDB_CANCEL; /* inhibits error messages by caller */ - - file_proc = load_file_proc; - - if (!file_proc) - file_proc = file_proc_find (load_procs, filename); - - if (!file_proc) - { - /* no errors when making thumbnails */ - if (run_mode == RUN_INTERACTIVE) - g_message (_("%s failed.\n" - "%s: Unknown file type."), - open_mode, filename); - - return NULL; - } - - /* check if we are opening a file */ - if (stat (filename, &statbuf) == 0) - { - uid_t euid; - gid_t egid; - - if (! (statbuf.st_mode & S_IFREG)) - { - /* no errors when making thumbnails */ - if (run_mode == RUN_INTERACTIVE) - g_message (_("%s failed.\n" - "%s is not a regular file."), - open_mode, filename); - - return NULL; - } - - euid = geteuid (); - egid = getegid (); - - if (! ((statbuf.st_mode & S_IRUSR) || - - ((statbuf.st_mode & S_IRGRP) && - (statbuf.st_uid != euid)) || - - ((statbuf.st_mode & S_IROTH) && - (statbuf.st_uid != euid) && - (statbuf.st_gid != egid)))) - { - /* no errors when making thumbnails */ - if (run_mode == RUN_INTERACTIVE) - g_message (_("%s failed.\n" - "%s: Permission denied."), - open_mode, filename); - - return NULL; - } - } - - proc = &file_proc->db_info; - - args = g_new0 (Argument, proc->num_args); - - for (i = 0; i < proc->num_args; i++) - args[i].arg_type = proc->args[i].arg_type; - - args[0].value.pdb_int = run_mode; - args[1].value.pdb_pointer = (gchar *) filename; - args[2].value.pdb_pointer = (gchar *) raw_filename; - - return_vals = procedural_db_execute (proc->name, args); - - *status = return_vals[0].value.pdb_int; - gimage_id = return_vals[1].value.pdb_int; - - procedural_db_destroy_args (return_vals, proc->num_values); - g_free (args); - - if (*status == PDB_SUCCESS && gimage_id != -1) - { - GimpImage *gimage = pdb_id_to_image (gimage_id); - - if (gimage) - { - gimp_image_invalidate_layer_previews (gimage); - gimp_image_invalidate_channel_previews (gimage); - } - - return pdb_id_to_image (gimage_id); - } - - return NULL; -} - -gint -file_open (gchar *filename, - gchar *raw_filename) -{ - GimpImage *gimage; - GDisplay *gdisplay; - gchar *absolute; - gint status; - - if ((gimage = file_open_image (filename, - raw_filename, - _("Open"), - RUN_INTERACTIVE, - &status)) != NULL) - { - /* enable & clear all undo steps */ - gimp_image_undo_enable (gimage); - - /* set the image to clean */ - gimp_image_clean_all (gimage); - - /* display the image */ - gdisplay = gdisplay_new (gimage, 0x0101); - - /* always activate the first display */ - if (g_slist_length (display_list) == 1) - gimp_context_set_display (gimp_context_get_user (), gdisplay); - - absolute = file_absolute_filename (filename); - document_index_add (absolute); - menus_last_opened_add (absolute); - g_free (absolute); - } - - return status; -} - -TempBuf * -make_thumb_tempbuf (GimpImage *gimage) -{ - gint w, h; - - if (gimage->width<=80 && gimage->height<=60) - { - w = gimage->width; - h = gimage->height; - } - else - { - /* Ratio molesting to fit within .xvpic thumbnail size limits */ - if (60 * gimage->width < 80 * gimage->height) - { - h = 60; - w = (60 * gimage->width) / gimage->height; - if (w == 0) - w = 1; - } - else - { - w = 80; - h = (80 * gimage->height) / gimage->width; - if (h == 0) - h = 1; - } - } - - /*printf("tn: %d x %d -> ", w, h);fflush(stdout);*/ - - return gimp_viewable_get_preview (GIMP_VIEWABLE (gimage), w, h); -} - -static guchar * -make_RGBbuf_from_tempbuf (TempBuf *tempbuf, - gint *width_rtn, - gint *height_rtn) -{ - gint i, j, w, h; - guchar *tbd; - guchar *ptr; - guchar *rtn = NULL; - guchar alpha, r, g, b; - - w = (*width_rtn) = tempbuf->width; - h = (*height_rtn) = tempbuf->height; - tbd = temp_buf_data (tempbuf); - - switch (tempbuf->bytes) - { - case 4: - rtn = ptr = g_malloc (3 * w * h); - for (i=0; icomp_preview->width, - * gimage->comp_preview->height); - * } - */ - - pathname = g_dirname (full_source_filename); - filename = g_basename (full_source_filename); /* Don't free! */ - - xvpathname = g_strconcat (pathname, G_DIR_SEPARATOR_S, ".xvpics", - NULL); - - thumbnailname = g_strconcat (xvpathname, G_DIR_SEPARATOR_S, - filename, - NULL); - - tbd = temp_buf_data (tempbuf); - - w = tempbuf->width; - h = tempbuf->height; - /*printf("tn: %d x %d\n", w, h);fflush(stdout);*/ - - mkdir (xvpathname, 0755); - - fp = fopen (thumbnailname, "wb"); - g_free (pathname); - g_free (xvpathname); - g_free (thumbnailname); - - if (fp) - { - basetype = gimp_image_base_type (gimage); - - fprintf (fp, - "P7 332\n#IMGINFO:%dx%d %s (%d %s)\n" - "#END_OF_COMMENTS\n%d %d 255\n", - gimage->width, gimage->height, - (basetype == RGB) ? "RGB" : - (basetype == GRAY) ? "Greyscale" : - (basetype == INDEXED) ? "Indexed" : - "(UNKNOWN COLOUR TYPE)", - (int)statbuf.st_size, - (statbuf.st_size == 1) ? "byte" : "bytes", - w, h); - - switch (basetype) - { - case INDEXED: - case RGB: - for (i=0; i>5)<<5) | ((g>>5)<<2) | (b>>6), fp); - - rerr = r - ( (r>>5) * 255 ) / 7; - gerr = g - ( (g>>5) * 255 ) / 7; - berr = b - ( (b>>6) * 255 ) / 3; - } - } - break; - - case GRAY: - for (i=0; i>5)<<5) | ((b3>>5)<<2) | (b2>>6), fp); - - b2err = b2 - ( (b2>>6) * 255 ) / 3; - b3err = b3 - ( (b3>>5) * 255 ) / 7; - } - } - break; - - default: - g_warning("UNKNOWN GIMAGE TYPE IN THUMBNAIL SAVE"); - break; - } - - fclose (fp); - } - else /* Error writing thumbnail */ - { - return FALSE; - } - - return TRUE; -} - -static gint -file_save (GimpImage *gimage, - gchar *filename, - gchar *raw_filename, - RunModeType run_mode, - gboolean set_filename) -{ - PlugInProcDef *file_proc; - ProcRecord *proc; - Argument *args; - Argument *return_vals; - gint status; - gint i; - struct stat statbuf; - - if (gimp_image_active_drawable (gimage) == NULL) - return PDB_EXECUTION_ERROR; - - file_proc = gimp_image_get_save_proc (gimage); - - if (!file_proc) - file_proc = file_proc_find (save_procs, raw_filename); - - if (!file_proc) - { - g_message (_("Save failed.\n" - "%s: Unknown file type."), - filename); - - return PDB_CANCEL; /* inhibits error messages by caller */ - } - - /* check if we are saving to a file */ - if (stat (filename, &statbuf) == 0) - { - uid_t euid; - gid_t egid; - - if (! (statbuf.st_mode & S_IFREG)) - { - g_message (_("Save failed.\n" - "%s is not a regular file."), - filename); - - return PDB_CANCEL; /* inhibits error messages by caller */ - } - - euid = geteuid (); - egid = getegid (); - - if (! ((statbuf.st_mode & S_IWUSR) || - - ((statbuf.st_mode & S_IWGRP) && - (statbuf.st_uid != euid)) || - - ((statbuf.st_mode & S_IWOTH) && - (statbuf.st_uid != euid) && - (statbuf.st_gid != egid)))) - { - g_message (_("Save failed.\n" - "%s: Permission denied."), - filename); - - return PDB_CANCEL; /* inhibits error messages by caller */ - } - } - - /* ref the image, so it can't get deleted during save */ - gtk_object_ref (GTK_OBJECT (gimage)); - - proc = &file_proc->db_info; - - args = g_new0 (Argument, proc->num_args); - - for (i = 0; i < proc->num_args; i++) - args[i].arg_type = proc->args[i].arg_type; - - args[0].value.pdb_int = run_mode; - args[1].value.pdb_int = pdb_image_to_id (gimage); - args[2].value.pdb_int = gimp_drawable_get_ID (gimp_image_active_drawable (gimage)); - args[3].value.pdb_pointer = filename; - args[4].value.pdb_pointer = raw_filename; - - return_vals = procedural_db_execute (proc->name, args); - - status = return_vals[0].value.pdb_int; - - if (status == PDB_SUCCESS) - { - /* set this image to clean */ - gimp_image_clean_all (gimage); - - /* these calls must come before the call to gimage_set_filename */ - document_index_add (filename); - menus_last_opened_add (filename); - - /* use the same plug-in for this image next time */ - /* DISABLED - gets stuck on first saved format... needs - attention --Adam */ - /* gimage_set_save_proc(gimage, file_proc); */ - - /* Write a thumbnail for the saved image, where appropriate */ - switch (thumbnail_mode) - { - case 0: - break; - default: - { - TempBuf *tempbuf; - - tempbuf = make_thumb_tempbuf (gimage); - file_save_thumbnail (gimage, filename, tempbuf); - } - } - - if (set_filename) - { - /* set the image title */ - gimp_object_set_name (GIMP_OBJECT (gimage), filename); - /* note: 'filename' may have been free'd by above call! */ - } - } - - g_free (return_vals); - g_free (args); - - gtk_object_unref (GTK_OBJECT (gimage)); - - return status; -} - -/* The readXVThumb function source may be re-used under - the XFree86-style license. */ -guchar * -readXVThumb (const gchar *fnam, - gint *w, - gint *h, - gchar **imginfo /* caller frees if != NULL */) -{ - FILE *fp; - const gchar *P7_332 = "P7 332"; - gchar P7_buf[7]; - gchar linebuf[200]; - guchar *buf; - gint twofivefive; - void *ptr; - - *w = *h = 0; - *imginfo = NULL; - - fp = fopen (fnam, "rb"); - if (!fp) - return NULL; - - fread (P7_buf, 6, 1, fp); - - if (strncmp(P7_buf, P7_332, 6)!=0) - { - g_warning ("Thumbnail doesn't have the 'P7 332' header."); - fclose (fp); - return NULL; - } - - /*newline*/ - fread (P7_buf, 1, 1, fp); - - do - { - ptr = fgets(linebuf, 199, fp); - if ((strncmp(linebuf, "#IMGINFO:", 9) == 0) && - (linebuf[9] != '\0') && - (linebuf[9] != '\n')) - { - if (linebuf[strlen(linebuf)-1] == '\n') - linebuf[strlen(linebuf)-1] = '\0'; - - if (linebuf[9] != '\0') - { - if (*imginfo) - g_free(*imginfo); - *imginfo = g_strdup (&linebuf[9]); - } - } - } - while (ptr && linebuf[0]=='#'); /* keep throwing away comment lines */ - - if (!ptr) - { - /* g_warning("Thumbnail ended - not an image?"); */ - fclose (fp); - return NULL; - } - - sscanf(linebuf, "%d %d %d\n", w, h, &twofivefive); - - if (twofivefive!=255) - { - g_warning ("Thumbnail is of funky depth."); - fclose (fp); - return NULL; - } - - if ((*w)<1 || (*h)<1 || (*w)>80 || (*h)>60) - { - g_warning ("Thumbnail size bad. Corrupted?"); - fclose (fp); - return NULL; - } - - buf = g_malloc ((*w) * (*h)); - - fread (buf, (*w) * (*h), 1, fp); - - fclose (fp); - - return buf; -} - -/* don't call with preview_fullname as parameter! will be clobbered! */ -static void -set_preview (const gchar *fullfname, - guchar *RGB_source, - gint RGB_w, - gint RGB_h) -{ - guchar *thumb_rgb; - guchar *raw_thumb; - gint tnw,tnh, i; - gchar *pname; - gchar *fname; - gchar *tname; - gchar *imginfo = NULL; - struct stat file_stat; - struct stat thumb_stat; - gboolean thumb_may_be_outdated = FALSE; - gboolean show_generate_label = TRUE; - - pname = g_dirname (fullfname); - fname = g_basename (fullfname); /* Don't free this! */ - tname = g_strconcat (pname, G_DIR_SEPARATOR_S, - ".xvpics", G_DIR_SEPARATOR_S, - fname, NULL); - - g_free (pname); - - /* If the file is newer than its thumbnail, the thumbnail may - * be out of date. - */ - if ((stat (tname, &thumb_stat) == 0) && - (stat (fullfname, &file_stat ) == 0)) - { - if ((thumb_stat.st_mtime) < (file_stat.st_mtime)) - { - thumb_may_be_outdated = TRUE; - } - } - - raw_thumb = readXVThumb (tname, &tnw, &tnh, &imginfo); - - g_free (tname); - - gtk_frame_set_label (GTK_FRAME (open_options_frame), fname); - - g_free (preview_fullname); - preview_fullname = g_strdup (fullfname); - - if (RGB_source) - { - gtk_preview_size (open_options_preview, RGB_w, RGB_h); - - for (i = 0; i < RGB_h; i++) - { - gtk_preview_draw_row (open_options_preview, &RGB_source[3*i*RGB_w], - 0, i, - RGB_w); - } - } - else - { - if (raw_thumb) - { - thumb_rgb = g_malloc (3 * tnw * tnh); - - for (i = 0; i < tnw * tnh; i++) - { - thumb_rgb[i*3 ] = ((raw_thumb[i]>>5)*255)/7; - thumb_rgb[i*3+1] = (((raw_thumb[i]>>2)&7)*255)/7; - thumb_rgb[i*3+2] = (((raw_thumb[i])&3)*255)/3; - } - - gtk_preview_size (open_options_preview, tnw, tnh); - - for (i = 0; i < tnh; i++) - { - gtk_preview_draw_row (open_options_preview, &thumb_rgb[3*i*tnw], - 0, i, - tnw); - } - - g_free (thumb_rgb); - } - } - - if (raw_thumb || RGB_source) /* We can show *some* kind of preview. */ - { - if (raw_thumb) /* Managed to commit thumbnail file to disk */ - { - gtk_label_set_text (GTK_LABEL (open_options_label), - thumb_may_be_outdated ? - _("(This thumbnail may be out of date)") : - (imginfo ? imginfo : _("(No Information)"))); - if (imginfo) - g_free (imginfo); - } - else - { - switch (thumbnail_mode) - { - case 0: - gtk_label_set_text (GTK_LABEL(open_options_label), - _("(Thumbnail saving is disabled)")); - break; - case 1: - gtk_label_set_text (GTK_LABEL(open_options_label), - _("(Could not write thumbnail file)")); - break; - default: - gtk_label_set_text (GTK_LABEL(open_options_label), - _("(Thumbnail file not written)")); - } - } - - gtk_widget_show (GTK_WIDGET (open_options_preview)); - gtk_widget_queue_draw (GTK_WIDGET(open_options_preview)); - - show_generate_label = FALSE; - - g_free (raw_thumb); - } - else - { - if (imginfo) - g_free (imginfo); - - gtk_widget_hide (GTK_WIDGET (open_options_preview)); - gtk_label_set_text (GTK_LABEL (open_options_label), - _("No preview available")); - } - - if (show_generate_label) - { - gtk_widget_hide (GTK_WIDGET (open_options_preview)); - gtk_widget_show (GTK_WIDGET (open_options_genbuttonlabel)); - } - else - { - gtk_widget_hide (GTK_WIDGET (open_options_genbuttonlabel)); - gtk_widget_show (GTK_WIDGET (open_options_preview)); - } -} - -static void -file_open_clistrow_callback (GtkWidget *widget, - gint row) -{ - gchar *fullfname = NULL; - - fullfname = gtk_file_selection_get_filename (GTK_FILE_SELECTION (fileload)); - - gtk_widget_set_sensitive (GTK_WIDGET (open_options_frame), TRUE); - set_preview (fullfname, NULL, 0, 0); -} - -static void -file_open_genbutton_callback (GtkWidget *widget, - gpointer data) -{ - GimpImage *gimage_to_be_thumbed; - guchar *RGBbuf; - TempBuf *tempbuf; - gint RGBbuf_w; - gint RGBbuf_h; - - /* added for multi-file preview generation... */ - GtkFileSelection *fs; - gchar *full_filename = NULL; - gchar *filedirname; - struct stat buf; - gint err; - - fs = GTK_FILE_SELECTION (data); - - if (!preview_fullname) - { - g_warning ("Tried to generate thumbnail for NULL filename."); - return; - } - - gimp_add_busy_cursors (); - gtk_widget_set_sensitive (GTK_WIDGET (fileload), FALSE); - - /* new mult-file preview make: */ - { - GSList *list, *toplist; - - /* Have to read the clist before touching anything else */ - - list= clist_to_slist(GTK_CLIST(fs->file_list)); - toplist = list; - - /* Find a real base directory for the multiple selection */ - - gtk_file_selection_set_filename (fs, ""); - filedirname= gtk_file_selection_get_filename (fs); - if (filedirname[strlen (filedirname) - 1] == G_DIR_SEPARATOR) - filedirname[strlen (filedirname) - 1] = '\0'; - - while(list) - { - full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, - (gchar *) list->data, NULL); - - err = stat (full_filename, &buf); - - if (! (err == 0 && (buf.st_mode & S_IFDIR))) - { /* Is not directory. */ - gint dummy; - - gimage_to_be_thumbed = file_open_image (full_filename, - list->data, - NULL, - RUN_NONINTERACTIVE, - &dummy); - - if (gimage_to_be_thumbed) - { - tempbuf = make_thumb_tempbuf (gimage_to_be_thumbed); - RGBbuf = make_RGBbuf_from_tempbuf (tempbuf, - &RGBbuf_w, - &RGBbuf_h); - if (thumbnail_mode) - { - file_save_thumbnail (gimage_to_be_thumbed, - full_filename, tempbuf); - } - set_preview (full_filename, RGBbuf, RGBbuf_w, RGBbuf_h); - - gtk_object_unref (GTK_OBJECT (gimage_to_be_thumbed)); - - if (RGBbuf) - g_free (RGBbuf); - } - else - { - gtk_label_set_text (GTK_LABEL (open_options_label), - _("(could not make preview)")); - } - } - - g_free(full_filename); - list= g_slist_next(list); - } - - for (list = toplist; list; list = g_slist_next (list)) - { - if (!(g_slist_next (list))) - { - full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, - (gchar *) list->data, NULL); - gtk_file_selection_set_filename (fs, full_filename); - g_free (full_filename); - } - - g_free (list->data); - } - - g_slist_free (toplist); - toplist = NULL; - } - - gtk_widget_set_sensitive (GTK_WIDGET (fileload), TRUE); - gimp_remove_busy_cursors (NULL); -} - -static void -file_open_ok_callback (GtkWidget *widget, - gpointer data) -{ - GtkFileSelection *fs; - gchar *full_filename, *raw_filename; - gchar *filedirname; - struct stat buf; - gint err; - gint status; - - fs = GTK_FILE_SELECTION (data); - full_filename = gtk_file_selection_get_filename (fs); - raw_filename = gtk_entry_get_text (GTK_ENTRY(fs->selection_entry)); - - g_assert (full_filename && raw_filename); - - if (strlen (raw_filename) == 0) - return; - - err = stat (full_filename, &buf); - - if (err == 0 && (buf.st_mode & S_IFDIR)) - { - if (full_filename[strlen (full_filename) - 1] != G_DIR_SEPARATOR) - { - gchar *s = g_strconcat (full_filename, G_DIR_SEPARATOR_S, NULL); - gtk_file_selection_set_filename (fs, s); - g_free (s); - } - else - gtk_file_selection_set_filename (fs, full_filename); - - return; - } - - gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); - - if (err) /* e.g. http://server/filename.jpg */ - full_filename = raw_filename; - - status = file_open (full_filename, raw_filename); - - if (status == PDB_SUCCESS) - { - file_dialog_hide (data); - } - else if (status != PDB_CANCEL) - { - g_message (_("Open failed.\n%s"), full_filename); - } - - - /* - * Now deal with multiple selections from the filesel clist - */ - - { - GSList *list; - - /* Have to read the clist before touching anything else */ - - list = clist_to_slist (GTK_CLIST (fs->file_list)); - - /* Find a real base directory for the multiple selection */ - - raw_filename = g_strdup(raw_filename); - gtk_file_selection_set_filename (fs, ""); - filedirname = gtk_file_selection_get_filename (fs); - - while (list) - { - full_filename = g_strconcat (filedirname, G_DIR_SEPARATOR_S, - (gchar *) list->data, NULL); - - if (strcmp (list->data, raw_filename)) - { /* don't load current selection twice */ - - err = stat (full_filename, &buf); - - if (! (err == 0 && (buf.st_mode & S_IFDIR))) - { /* Is not directory. */ - - status = file_open (full_filename, (char *) list->data); - - if (status == PDB_SUCCESS) - { - file_dialog_hide (data); - } - else if (status != PDB_CANCEL) - { - g_message (_("Open failed.\n%s"), full_filename); - } - } - } - - g_free (full_filename); - g_free (list->data); - list = g_slist_next (list); - } - - g_slist_free (list); - list = NULL; - } - - gtk_file_selection_set_filename (fs, raw_filename); - g_free (raw_filename); - gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE); - -} - -static GSList * -clist_to_slist (GtkCList *file_list) -{ - GSList *list = NULL; - GList *row; - gint rownum; - gchar *temp; - - for (row = file_list->row_list, rownum = 0; - row; - row = g_list_next (row), rownum++) - { - if (GTK_CLIST_ROW (row)->state == GTK_STATE_SELECTED) - { - if (gtk_clist_get_cell_type (file_list, rownum, 0) == GTK_CELL_TEXT) - { - gtk_clist_get_text (file_list, rownum, 0, &temp); - list = g_slist_prepend (list, g_strdup (temp)); - } - } - } - - return list; -} - - -/* Set "gimage"s save handler to "save_proc", then save the image. - * Hide the dialog if all went well, otherwise make the user knows an - * error happened and leave the dialog up. Make sure it's sensitive. - */ -static void -file_save_with_proc (GimpImage *gimage, - gchar *full_filename, - gchar *raw_filename, - PlugInProcDef *save_proc, - gboolean set_filename) -{ - gint status = PDB_EXECUTION_ERROR; - - if (gimage != NULL) - { - gimp_image_set_save_proc (gimage, save_proc); - status = file_save (gimage, - full_filename, - raw_filename, - RUN_INTERACTIVE, - set_filename); - - /* hide the file save dialog on success */ - if (status == PDB_SUCCESS) - file_dialog_hide (filesave); - } - - /* If there was an error but file_save() didn't print an error - * message, then we'd better. */ - if (status != PDB_SUCCESS && status != PDB_CANCEL) - g_message (_("Save failed.\n%s"), full_filename); - - /* always make file save dialog sensitive */ - gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); -} - - -static void -file_save_ok_callback (GtkWidget *widget, - gpointer data) -{ - GtkFileSelection *fs; - gchar *filename; - gchar *raw_filename; - gchar *dot; - gint x; - struct stat buf; - gint err; - - fs = GTK_FILE_SELECTION (data); - filename = gtk_file_selection_get_filename (fs); - raw_filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)); - - g_assert (filename && raw_filename); - - for (dot = strrchr (filename, '.'), x = 0; dot && *(++dot);) - { - if (*dot != 'e' || ++x < 0) - break; - else if (x > 3 && !strcmp (dot + 1, "k")) - { - ProcRecord *proc_rec; - Argument *args; - GimpDrawable *the_drawable; - - the_drawable = gimp_image_active_drawable (the_gimage); - if (!the_drawable) - return; - - proc_rec = procedural_db_lookup ("plug_in_the_slimy_egg"); - if (!proc_rec) - break; - - file_dialog_hide (filesave); - - args = g_new (Argument, 3); - args[0].arg_type = PDB_INT32; - args[0].value.pdb_int = RUN_INTERACTIVE; - args[1].arg_type = PDB_IMAGE; - args[1].value.pdb_int = pdb_image_to_id (the_gimage); - args[2].arg_type = PDB_DRAWABLE; - args[2].value.pdb_int = the_drawable->ID; - - plug_in_run (proc_rec, args, 3, FALSE, TRUE, 0); - - g_free (args); - - return; - } - } - - err = stat (filename, &buf); - - if (err == 0) - { - if (buf.st_mode & S_IFDIR) - { - if (filename[strlen (filename) - 1] != G_DIR_SEPARATOR) - { - gchar *s = g_strconcat (filename, G_DIR_SEPARATOR_S, NULL); - gtk_file_selection_set_filename (fs, s); - g_free (s); - } - else - gtk_file_selection_set_filename (fs, filename); - } - else - { - gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); - file_overwrite (g_strdup (filename), g_strdup (raw_filename)); - } - } - else - { - gtk_widget_set_sensitive (GTK_WIDGET (fs), FALSE); - - file_save_with_proc (the_gimage, filename, raw_filename, save_file_proc, - set_filename); - - gtk_widget_set_sensitive (GTK_WIDGET (fs), TRUE); - } -} - -static void -file_dialog_show (GtkWidget *filesel) -{ - menus_set_sensitive ("/File/Open...", FALSE); - menus_set_sensitive ("/File/Open...", FALSE); - menus_set_sensitive ("/File/Save", FALSE); - menus_set_sensitive ("/File/Save as...", FALSE); - menus_set_sensitive ("/File/Save a Copy as...", FALSE); - - gtk_widget_grab_focus (GTK_FILE_SELECTION (filesel)->selection_entry); - gtk_widget_show (filesel); -} - -static int -file_dialog_hide (GtkWidget *filesel) -{ - gimp_dialog_hide (filesel); - - menus_set_sensitive ("/File/Open...", TRUE); - menus_set_sensitive ("/File/Open...", TRUE); - - if (gdisplay_active ()) - { - menus_set_sensitive ("/File/Save", TRUE); - menus_set_sensitive ("/File/Save as...", TRUE); - menus_set_sensitive ("/File/Save a Copy as...", TRUE); - } - - return TRUE; -} - -static void -file_overwrite (gchar *filename, - gchar *raw_filename) -{ - OverwriteData *overwrite_data; - GtkWidget *query_box; - gchar *overwrite_text; - - overwrite_data = g_new (OverwriteData, 1); - overwrite_data->full_filename = filename; - overwrite_data->raw_filename = raw_filename; - - overwrite_text = g_strdup_printf (_("%s exists, overwrite?"), filename); - - query_box = gimp_query_boolean_box (_("File Exists!"), - gimp_standard_help_func, - "save/file_exists.html", - FALSE, - overwrite_text, - _("Yes"), _("No"), - NULL, NULL, - file_overwrite_callback, - overwrite_data); - - g_free (overwrite_text); - - gtk_widget_show (query_box); -} - -static void -file_overwrite_callback (GtkWidget *widget, - gboolean overwrite, - gpointer data) -{ - OverwriteData *overwrite_data; - - overwrite_data = (OverwriteData *) data; - - if (overwrite) - { - file_save_with_proc (the_gimage, - overwrite_data->full_filename, - overwrite_data->raw_filename, - save_file_proc, - set_filename); - } - - /* always make file save dialog sensitive */ - gtk_widget_set_sensitive (GTK_WIDGET (filesave), TRUE); - - g_free (overwrite_data->full_filename); - g_free (overwrite_data->raw_filename); - g_free (overwrite_data); -} - -static void -file_revert_confirm_callback (GtkWidget *widget, - gboolean revert, - gpointer data) -{ - GimpImage *old_gimage; - - old_gimage = (GimpImage *) data; - - gtk_object_set_data (GTK_OBJECT (old_gimage), REVERT_DATA_KEY, NULL); - - if (revert) - { - GimpImage *new_gimage; - const gchar *filename; - gint status; - - filename = gimp_object_get_name (GIMP_OBJECT (old_gimage)); - - new_gimage = file_open_image (filename, filename, _("Revert"), - RUN_INTERACTIVE, &status); - - if (new_gimage != NULL) - { - undo_free (new_gimage); - gdisplays_reconnect (old_gimage, new_gimage); - gdisplays_resize_cursor_label (new_gimage); - gdisplays_update_full (new_gimage); - gdisplays_shrink_wrap (new_gimage); - gimp_image_clean_all (new_gimage); - } - else if (status != PDB_CANCEL) - { - g_message (_("Revert failed.\n%s"), filename); - } - } -} - -static PlugInProcDef * -file_proc_find_by_name (GSList *procs, - const gchar *filename, - gboolean skip_magic) -{ - GSList *p; - gchar *ext = strrchr (filename, '.'); - - if (ext) - ext++; - - for (p = procs; p; p = g_slist_next (p)) - { - PlugInProcDef *proc = p->data; - GSList *prefixes; - - if (skip_magic && proc->magics_list) - continue; - - for (prefixes = proc->prefixes_list; - prefixes; - prefixes = g_slist_next (prefixes)) - { - if (strncmp (filename, prefixes->data, strlen (prefixes->data)) == 0) - return proc; - } - } - - for (p = procs; p; p = g_slist_next (p)) - { - PlugInProcDef *proc = p->data; - GSList *extensions; - - for (extensions = proc->extensions_list; - ext && extensions; - extensions = g_slist_next (extensions)) - { - gchar *p1 = ext; - gchar *p2 = (gchar *) extensions->data; - - if (skip_magic && proc->magics_list) - continue; - - while (*p1 && *p2) - { - if (tolower (*p1) != tolower (*p2)) - break; - - p1++; - p2++; - } - - if (!(*p1) && !(*p2)) - return proc; - } - } - - return NULL; -} - -PlugInProcDef * -file_proc_find (GSList *procs, - const gchar *filename) -{ - PlugInProcDef *file_proc; - PlugInProcDef *size_matched_proc; - GSList *all_procs = procs; - FILE *ifp = NULL; - gint head_size = -2; - gint size_match_count = 0; - gint match_val; - guchar head[256]; - - size_matched_proc = NULL; - - /* First, check magicless prefixes/suffixes */ - if ( (file_proc = file_proc_find_by_name (all_procs, filename, TRUE)) != NULL) - return file_proc; - - /* Then look for magics */ - while (procs) - { - file_proc = procs->data; - procs = procs->next; - - if (file_proc->magics_list) - { - if (head_size == -2) - { - head_size = 0; - if ((ifp = fopen (filename, "rb")) != NULL) - head_size = fread ((gchar *) head, 1, sizeof (head), ifp); - } - if (head_size >= 4) - { - match_val = file_check_magic_list (file_proc->magics_list, - head_size, head, ifp); - if (match_val == 2) /* size match ? */ - { /* Use it only if no other magic matches */ - size_match_count++; - size_matched_proc = file_proc; - } - else if (match_val) - { - fclose (ifp); - return (file_proc); - } - } - } - } - if (ifp) fclose (ifp); - if (size_match_count == 1) - return (size_matched_proc); - - /* As a last ditch, try matching by name */ - return file_proc_find_by_name (all_procs, filename, FALSE); -} - -static void -file_convert_string (gchar *instr, - gchar *outmem, - gint maxmem, - gint *nmem) -{ - /* Convert a string in C-notation to array of char */ - guchar *uin = (guchar *) instr; - guchar *uout = (guchar *) outmem; - guchar tmp[5], *tmpptr; - gint k; - - while ((*uin != '\0') && ((((char *)uout) - outmem) < maxmem)) - { - if (*uin != '\\') /* Not an escaped character ? */ - { - *(uout++) = *(uin++); - continue; - } - if (*(++uin) == '\0') - { - *(uout++) = '\\'; - break; - } - switch (*uin) - { - case '0': case '1': case '2': case '3': /* octal */ - for (tmpptr = tmp; (tmpptr-tmp) <= 3;) - { - *(tmpptr++) = *(uin++); - if ( (*uin == '\0') || (!isdigit (*uin)) - || (*uin == '8') || (*uin == '9')) - break; - } - *tmpptr = '\0'; - sscanf ((char *)tmp, "%o", &k); - *(uout++) = k; - break; - - case 'a': *(uout++) = '\a'; uin++; break; - case 'b': *(uout++) = '\b'; uin++; break; - case 't': *(uout++) = '\t'; uin++; break; - case 'n': *(uout++) = '\n'; uin++; break; - case 'v': *(uout++) = '\v'; uin++; break; - case 'f': *(uout++) = '\f'; uin++; break; - case 'r': *(uout++) = '\r'; uin++; break; - - default : *(uout++) = *(uin++); break; - } - } - *nmem = ((gchar *) uout) - outmem; -} - -static gchar * -file_absolute_filename (gchar *name) -{ - PlugInProcDef *proc; - GSList *procs; - GSList *prefixes; - gchar *absolute; - gchar *current; - - g_return_val_if_fail (name != NULL, NULL); - - /* check for prefixes like http or ftp */ - for (procs = load_procs; procs; procs = g_slist_next (procs)) - { - proc = (PlugInProcDef *)procs->data; - - for (prefixes = proc->prefixes_list; - prefixes; - prefixes = g_slist_next (prefixes)) - { - if (strncmp (name, prefixes->data, strlen (prefixes->data)) == 0) - return g_strdup (name); - } - } - - if (g_path_is_absolute (name)) - return g_strdup (name); - - current = g_get_current_dir (); - absolute = g_strconcat (current, G_DIR_SEPARATOR_S, name, NULL); - g_free (current); - - return absolute; -} - -static gint -file_check_single_magic (gchar *offset, - gchar *type, - gchar *value, - gint headsize, - guchar *file_head, - FILE *ifp) - -{ - /* Return values are 0: no match, 1: magic match, 2: size match */ - glong offs; - gulong num_testval, num_operatorval; - gulong fileval; - gint numbytes, k, c = 0, found = 0; - gchar *num_operator_ptr, num_operator, num_test; - guchar mem_testval[256]; - - /* Check offset */ - if (sscanf (offset, "%ld", &offs) != 1) return (0); - if (offs < 0) return (0); - - /* Check type of test */ - num_operator_ptr = NULL; - num_operator = '\0'; - num_test = '='; - if (strncmp (type, "byte", 4) == 0) - { - numbytes = 1; - num_operator_ptr = type+4; - } - else if (strncmp (type, "short", 5) == 0) - { - numbytes = 2; - num_operator_ptr = type+5; - } - else if (strncmp (type, "long", 4) == 0) - { - numbytes = 4; - num_operator_ptr = type+4; - } - else if (strncmp (type, "size", 4) == 0) - { - numbytes = 5; - } - else if (strcmp (type, "string") == 0) - { - numbytes = 0; - } - else return (0); - - /* Check numerical operator value if present */ - if (num_operator_ptr && (*num_operator_ptr == '&')) - { - if (isdigit (num_operator_ptr[1])) - { - if (num_operator_ptr[1] != '0') /* decimal */ - sscanf (num_operator_ptr+1, "%ld", &num_operatorval); - else if (num_operator_ptr[2] == 'x') /* hexadecimal */ - sscanf (num_operator_ptr+3, "%lx", &num_operatorval); - else /* octal */ - sscanf (num_operator_ptr+2, "%lo", &num_operatorval); - num_operator = *num_operator_ptr; - } - } - - if (numbytes > 0) /* Numerical test ? */ - { - /* Check test value */ - if ((value[0] == '=') || (value[0] == '>') || (value[0] == '<')) - { - num_test = value[0]; - value++; - } - if (!isdigit (value[0])) return (0); - - /* - * to anybody reading this: is strtol's parsing behaviour (e.g. "0x" prefix) - * broken on some systems or why do we do the base detection ourselves? - * */ - if (value[0] != '0') /* decimal */ - num_testval = strtol(value, NULL, 10); - else if (value[1] == 'x') /* hexadecimal */ - num_testval = (unsigned long)strtoul(value+2, NULL, 16); - else /* octal */ - num_testval = strtol(value+1, NULL, 8); - - fileval = 0; - if (numbytes == 5) /* Check for file size ? */ - { - struct stat buf; - - if (fstat (fileno (ifp), &buf) < 0) return (0); - fileval = buf.st_size; - } - else if (offs + numbytes <= headsize) /* We have it in memory ? */ - { - for (k = 0; k < numbytes; k++) - fileval = (fileval << 8) | (long)file_head[offs+k]; - } - else /* Read it from file */ - { - if (fseek (ifp, offs, SEEK_SET) < 0) return (0); - for (k = 0; k < numbytes; k++) - fileval = (fileval << 8) | (c = getc (ifp)); - if (c == EOF) return (0); - } - if (num_operator == '&') - fileval &= num_operatorval; - - if (num_test == '<') - found = (fileval < num_testval); - else if (num_test == '>') - found = (fileval > num_testval); - else - found = (fileval == num_testval); - - if (found && (numbytes == 5)) found = 2; - } - else if (numbytes == 0) /* String test */ - { - file_convert_string ((char *)value, (char *)mem_testval, - sizeof (mem_testval), &numbytes); - if (numbytes <= 0) return (0); - - if (offs + numbytes <= headsize) /* We have it in memory ? */ - { - found = (memcmp (mem_testval, file_head+offs, numbytes) == 0); - } - else /* Read it from file */ - { - if (fseek (ifp, offs, SEEK_SET) < 0) return (0); - found = 1; - for (k = 0; found && (k < numbytes); k++) - { - c = getc (ifp); - found = (c != EOF) && (c == (int)mem_testval[k]); - } - } - } - - return (found); -} - -static int -file_check_magic_list (GSList *magics_list, - gint headsize, - guchar *head, - FILE *ifp) - -{ - /* Return values are 0: no match, 1: magic match, 2: size match */ - gchar *offset, *type, *value; - gint and = 0; - gint found = 0, match_val; - - while (magics_list) - { - if ((offset = (char *)magics_list->data) == NULL) break; - if ((magics_list = magics_list->next) == NULL) break; - if ((type = (char *)magics_list->data) == NULL) break; - if ((magics_list = magics_list->next) == NULL) break; - if ((value = (char *)magics_list->data) == NULL) break; - magics_list = magics_list->next; - - match_val = file_check_single_magic (offset, type, value, - headsize, head, ifp); - if (and) - found = found && match_val; - else - found = match_val; - - and = (strchr (offset, '&') != NULL); - if ((!and) && found) return (match_val); - } - return (0); -} - -static void -file_update_menus (GSList *procs, - gint image_type) -{ - PlugInProcDef *file_proc; - - while (procs) - { - file_proc = procs->data; - procs = procs->next; - - if (file_proc->db_info.proc_type != PDB_EXTENSION) - menus_set_sensitive (file_proc->menu_path, - (file_proc->image_types_val & image_type)); - } -} diff --git a/app/fileops.h b/app/fileops.h deleted file mode 100644 index b25214b32b..0000000000 --- a/app/fileops.h +++ /dev/null @@ -1,55 +0,0 @@ -/* The GIMP -- an image manipulation program - * Copyright (C) 1995 Spencer Kimball and Peter Mattis - * - * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef __FILEOPS_H__ -#define __FILEOPS_H__ - - -extern GSList *load_procs; -extern GSList *save_procs; - - -void file_ops_pre_init (void); -void file_ops_post_init (void); - -void file_open_callback (GtkWidget *widget, - gpointer data); - -void file_save_callback (GtkWidget *widget, - gpointer data); -void file_save_as_callback (GtkWidget *widget, - gpointer data); -void file_save_a_copy_as_callback (GtkWidget *widget, - gpointer data); - -void file_revert_callback (GtkWidget *widget, - gpointer data); - -void file_open_by_extension_callback (GtkWidget *widget, - gpointer data); -void file_save_by_extension_callback (GtkWidget *widget, - gpointer data); - -gint file_open (gchar *filename, - gchar *raw_filename); - -PlugInProcDef * file_proc_find (GSList *procs, - const gchar *filename); - - -#endif /* __FILEOPS_H__ */ diff --git a/app/gimpdnd.c b/app/gimpdnd.c index 9a5dfff372..bf188aa1bd 100644 --- a/app/gimpdnd.c +++ b/app/gimpdnd.c @@ -33,7 +33,7 @@ #include "tools/tool_manager.h" #include "context_manager.h" -#include "fileops.h" +#include "file-open.h" #include "gimpimage.h" #include "gimpbrush.h" #include "gimpchannel.h" diff --git a/app/gimprc.c b/app/gimprc.c index 8fccf4f651..d724244b02 100644 --- a/app/gimprc.c +++ b/app/gimprc.c @@ -46,7 +46,6 @@ #include "cursorutil.h" #include "devices.h" #include "errors.h" -#include "fileops.h" #include "general.h" #include "gimphelp.h" #include "gimpparasite.h" diff --git a/app/gui/commands.c b/app/gui/commands.c index adb85f0a30..e062989745 100644 --- a/app/gui/commands.c +++ b/app/gui/commands.c @@ -48,7 +48,8 @@ #include "channel_ops.h" #include "equalize.h" #include "errorconsole.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "floating_sel.h" #include "gdisplay_ops.h" #include "gimage_mask.h" diff --git a/app/gui/help-commands.c b/app/gui/help-commands.c index adb85f0a30..e062989745 100644 --- a/app/gui/help-commands.c +++ b/app/gui/help-commands.c @@ -48,7 +48,8 @@ #include "channel_ops.h" #include "equalize.h" #include "errorconsole.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "floating_sel.h" #include "gdisplay_ops.h" #include "gimage_mask.h" diff --git a/app/gui/menus.c b/app/gui/menus.c index 990388e190..b2efa77b84 100644 --- a/app/gui/menus.c +++ b/app/gui/menus.c @@ -34,7 +34,8 @@ #include "channels_dialog.h" #include "commands.h" #include "dialog_handler.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "gdisplay.h" #include "gimphelp.h" #include "gimplist.h" diff --git a/app/menus.c b/app/menus.c index 990388e190..b2efa77b84 100644 --- a/app/menus.c +++ b/app/menus.c @@ -34,7 +34,8 @@ #include "channels_dialog.h" #include "commands.h" #include "dialog_handler.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "gdisplay.h" #include "gimphelp.h" #include "gimplist.h" diff --git a/app/menus/menus.c b/app/menus/menus.c index 990388e190..b2efa77b84 100644 --- a/app/menus/menus.c +++ b/app/menus/menus.c @@ -34,7 +34,8 @@ #include "channels_dialog.h" #include "commands.h" #include "dialog_handler.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "gdisplay.h" #include "gimphelp.h" #include "gimplist.h" diff --git a/app/pdb/fileops_cmds.c b/app/pdb/fileops_cmds.c index 80c09d3c8a..6ed0e1627d 100644 --- a/app/pdb/fileops_cmds.c +++ b/app/pdb/fileops_cmds.c @@ -39,8 +39,9 @@ #include "apptypes.h" #include "procedural_db.h" -#include "fileops.h" -#include "fileopsP.h" +#include "file-open.h" +#include "file-save.h" +#include "file-utils.h" #include "gimprc.h" #include "plug_in.h" diff --git a/app/widgets/gimpdnd.c b/app/widgets/gimpdnd.c index 9a5dfff372..bf188aa1bd 100644 --- a/app/widgets/gimpdnd.c +++ b/app/widgets/gimpdnd.c @@ -33,7 +33,7 @@ #include "tools/tool_manager.h" #include "context_manager.h" -#include "fileops.h" +#include "file-open.h" #include "gimpimage.h" #include "gimpbrush.h" #include "gimpchannel.h" diff --git a/app/widgets/gimpitemfactory.c b/app/widgets/gimpitemfactory.c index 990388e190..b2efa77b84 100644 --- a/app/widgets/gimpitemfactory.c +++ b/app/widgets/gimpitemfactory.c @@ -34,7 +34,8 @@ #include "channels_dialog.h" #include "commands.h" #include "dialog_handler.h" -#include "fileops.h" +#include "file-open.h" +#include "file-save.h" #include "gdisplay.h" #include "gimphelp.h" #include "gimplist.h" diff --git a/po/POTFILES.in b/po/POTFILES.in index e5214c0e08..5ce95fdd94 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -20,7 +20,8 @@ app/docindex.c app/equalize.c app/errorconsole.c app/file_new_dialog.c -app/fileops.c +app/file-open.c +app/file-save.c app/floating_sel.c app/gdisplay.c app/gdisplay_ops.c diff --git a/tools/pdbgen/pdb/fileops.pdb b/tools/pdbgen/pdb/fileops.pdb index dc58f3b848..561f92776d 100644 --- a/tools/pdbgen/pdb/fileops.pdb +++ b/tools/pdbgen/pdb/fileops.pdb @@ -187,7 +187,6 @@ HELP ); %invoke = ( - headers => [ qw("fileopsP.h") ], vars => [ 'gchar *pname', 'gchar *fname', 'gchar *tname', 'guchar *raw_thumb', 'gchar *imginfo = NULL', 'gint i' ], code => <<'CODE' @@ -242,7 +241,6 @@ HELP ); %invoke = ( - headers => [ qw("fileopsP.h") ], vars => [ 'TempBuf *thumb' ], code => <<'CODE' { @@ -412,7 +410,8 @@ CODE ); } -@headers = qw("fileops.h" "plug_in.h" ); +@headers = qw("file-open.h" "file-save.h" "file-utils.h" "plug_in.h" + ); @procs = qw(file_load file_save file_load_thumbnail file_save_thumbnail temp_name register_magic_load_handler register_load_handler