1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
16  */
17 
18 /*
19  * gbr plug-in version 1.00
20  * Loads/exports version 2 GIMP .gbr files, by Tim Newsome <drz@frody.bloke.com>
21  * Some bits stolen from the .99.7 source tree.
22  *
23  * Added in GBR version 1 support after learning that there wasn't a
24  * tool to read them.
25  * July 6, 1998 by Seth Burgess <sjburges@gimp.org>
26  *
27  * Dec 17, 2000
28  * Load and save GIMP brushes in GRAY or RGBA.  jtl + neo
29  *
30  *
31  * TODO: Give some better error reporting on not opening files/bad headers
32  *       etc.
33  */
34 
35 #include "config.h"
36 
37 #include <libgimp/gimp.h>
38 #include <libgimp/gimpui.h>
39 
40 #include "libgimp/stdplugins-intl.h"
41 
42 
43 #define SAVE_PROC      "file-gbr-save"
44 #define PLUG_IN_BINARY "file-gbr"
45 #define PLUG_IN_ROLE   "gimp-file-gbr"
46 
47 
48 typedef struct
49 {
50   gchar description[256];
51   gint  spacing;
52 } BrushInfo;
53 
54 
55 /*  local function prototypes  */
56 
57 static void       query          (void);
58 static void       run            (const gchar      *name,
59                                   gint              nparams,
60                                   const GimpParam  *param,
61                                   gint             *nreturn_vals,
62                                   GimpParam       **return_vals);
63 
64 static gboolean   save_dialog    (void);
65 static void       entry_callback (GtkWidget        *widget,
66                                   gpointer          data);
67 
68 
69 const GimpPlugInInfo PLUG_IN_INFO =
70 {
71   NULL,  /* init_proc  */
72   NULL,  /* quit_proc  */
73   query, /* query_proc */
74   run,   /* run_proc   */
75 };
76 
77 
78 /*  private variables  */
79 
80 static BrushInfo info =
81 {
82   "GIMP Brush",
83   10
84 };
85 
86 
MAIN()87 MAIN ()
88 
89 static void
90 query (void)
91 {
92   static const GimpParamDef save_args[] =
93   {
94     { GIMP_PDB_INT32,    "run-mode",    "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
95     { GIMP_PDB_IMAGE,    "image",       "Input image" },
96     { GIMP_PDB_DRAWABLE, "drawable",    "Drawable to export" },
97     { GIMP_PDB_STRING,   "uri",         "The URI of the file to export the image in" },
98     { GIMP_PDB_STRING,   "raw-uri",     "The URI of the file to export the image in" },
99     { GIMP_PDB_INT32,    "spacing",     "Spacing of the brush" },
100     { GIMP_PDB_STRING,   "description", "Short description of the brush" }
101   };
102 
103   gimp_install_procedure (SAVE_PROC,
104                           "Exports files in the GIMP brush file format",
105                           "Exports files in the GIMP brush file format",
106                           "Tim Newsome, Jens Lautenbacher, Sven Neumann",
107                           "Tim Newsome, Jens Lautenbacher, Sven Neumann",
108                           "1997-2000",
109                           N_("GIMP brush"),
110                           "RGB*, GRAY*, INDEXED*",
111                           GIMP_PLUGIN,
112                           G_N_ELEMENTS (save_args), 0,
113                           save_args, NULL);
114 
115   gimp_plugin_icon_register (SAVE_PROC, GIMP_ICON_TYPE_ICON_NAME,
116                              (const guint8 *) GIMP_ICON_BRUSH);
117   gimp_register_file_handler_mime (SAVE_PROC, "image/x-gimp-gbr");
118   gimp_register_file_handler_uri (SAVE_PROC);
119   gimp_register_save_handler (SAVE_PROC, "gbr", "");
120 }
121 
122 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)123 run (const gchar      *name,
124      gint              nparams,
125      const GimpParam  *param,
126      gint             *nreturn_vals,
127      GimpParam       **return_vals)
128 {
129   static GimpParam   values[2];
130   GimpRunMode        run_mode;
131   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
132   gint32             image_ID;
133   gint32             drawable_ID;
134   GimpExportReturn   export = GIMP_EXPORT_CANCEL;
135   GError            *error  = NULL;
136 
137   INIT_I18N ();
138 
139   run_mode = param[0].data.d_int32;
140 
141   *nreturn_vals = 1;
142   *return_vals  = values;
143 
144   values[0].type          = GIMP_PDB_STATUS;
145   values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
146 
147   if (strcmp (name, SAVE_PROC) == 0)
148     {
149       GFile        *file;
150       GimpParasite *parasite;
151       gint32        orig_image_ID;
152 
153       image_ID    = param[1].data.d_int32;
154       drawable_ID = param[2].data.d_int32;
155       file        = g_file_new_for_uri (param[3].data.d_string);
156 
157       orig_image_ID = image_ID;
158 
159       switch (run_mode)
160         {
161         case GIMP_RUN_INTERACTIVE:
162         case GIMP_RUN_WITH_LAST_VALS:
163           gimp_ui_init (PLUG_IN_BINARY, FALSE);
164 
165           export = gimp_export_image (&image_ID, &drawable_ID, "GBR",
166                                       GIMP_EXPORT_CAN_HANDLE_GRAY    |
167                                       GIMP_EXPORT_CAN_HANDLE_RGB     |
168                                       GIMP_EXPORT_CAN_HANDLE_INDEXED |
169                                       GIMP_EXPORT_CAN_HANDLE_ALPHA);
170 
171           if (export == GIMP_EXPORT_CANCEL)
172             {
173               values[0].data.d_status = GIMP_PDB_CANCEL;
174               return;
175             }
176 
177           /*  Possibly retrieve data  */
178           gimp_get_data (SAVE_PROC, &info);
179 
180           parasite = gimp_image_get_parasite (orig_image_ID,
181                                               "gimp-brush-name");
182           if (parasite)
183             {
184               strncpy (info.description,
185                        gimp_parasite_data (parasite),
186                        MIN (sizeof (info.description),
187                             gimp_parasite_data_size (parasite)));
188               info.description[sizeof (info.description) - 1] = '\0';
189 
190               gimp_parasite_free (parasite);
191             }
192           else
193             {
194               gchar *name = g_path_get_basename (gimp_file_get_utf8_name (file));
195 
196               if (g_str_has_suffix (name, ".gbr"))
197                 name[strlen (name) - 4] = '\0';
198 
199               if (strlen (name))
200                 {
201                   strncpy (info.description, name, sizeof (info.description));
202                   info.description[sizeof (info.description) - 1] = '\0';
203                 }
204 
205               g_free (name);
206             }
207           break;
208 
209         default:
210           break;
211         }
212 
213       switch (run_mode)
214         {
215         case GIMP_RUN_INTERACTIVE:
216           if (! save_dialog ())
217             status = GIMP_PDB_CANCEL;
218           break;
219 
220         case GIMP_RUN_NONINTERACTIVE:
221           if (nparams != 7)
222             {
223               status = GIMP_PDB_CALLING_ERROR;
224             }
225           else
226             {
227               info.spacing = (param[5].data.d_int32);
228               strncpy (info.description, param[6].data.d_string,
229                        sizeof (info.description));
230               info.description[sizeof (info.description) - 1] = '\0';
231             }
232           break;
233 
234         default:
235           break;
236         }
237 
238       if (status == GIMP_PDB_SUCCESS)
239         {
240           GimpParam *save_retvals;
241           gint       n_save_retvals;
242 
243           save_retvals =
244             gimp_run_procedure ("file-gbr-save-internal",
245                                 &n_save_retvals,
246                                 GIMP_PDB_INT32,    GIMP_RUN_NONINTERACTIVE,
247                                 GIMP_PDB_IMAGE,    image_ID,
248                                 GIMP_PDB_DRAWABLE, drawable_ID,
249                                 GIMP_PDB_STRING,   param[3].data.d_string,
250                                 GIMP_PDB_STRING,   param[4].data.d_string,
251                                 GIMP_PDB_INT32,    info.spacing,
252                                 GIMP_PDB_STRING,   info.description,
253                                 GIMP_PDB_END);
254 
255           if (save_retvals[0].data.d_status == GIMP_PDB_SUCCESS)
256             {
257               gimp_set_data (SAVE_PROC, &info, sizeof (info));
258             }
259           else
260             {
261               g_set_error (&error, 0, 0,
262                            "Running procedure 'file-gbr-save-internal' "
263                            "failed: %s",
264                            gimp_get_pdb_error ());
265 
266               status = GIMP_PDB_EXECUTION_ERROR;
267             }
268 
269           gimp_destroy_params (save_retvals, n_save_retvals);
270         }
271 
272       if (export == GIMP_EXPORT_EXPORT)
273         gimp_image_delete (image_ID);
274 
275       if (strlen (info.description))
276         {
277           GimpParasite *parasite;
278 
279           parasite = gimp_parasite_new ("gimp-brush-name",
280                                         GIMP_PARASITE_PERSISTENT,
281                                         strlen (info.description) + 1,
282                                         info.description);
283           gimp_image_attach_parasite (orig_image_ID, parasite);
284           gimp_parasite_free (parasite);
285         }
286       else
287         {
288           gimp_image_detach_parasite (orig_image_ID, "gimp-brush-name");
289         }
290     }
291   else
292     {
293       status = GIMP_PDB_CALLING_ERROR;
294     }
295 
296   if (status != GIMP_PDB_SUCCESS && error)
297     {
298       *nreturn_vals = 2;
299       values[1].type          = GIMP_PDB_STRING;
300       values[1].data.d_string = error->message;
301     }
302 
303   values[0].data.d_status = status;
304 }
305 
306 static gboolean
save_dialog(void)307 save_dialog (void)
308 {
309   GtkWidget     *dialog;
310   GtkWidget     *table;
311   GtkWidget     *entry;
312   GtkWidget     *spinbutton;
313   GtkAdjustment *adj;
314   gboolean       run;
315 
316   dialog = gimp_export_dialog_new (_("Brush"), PLUG_IN_BINARY, SAVE_PROC);
317 
318   /* The main table */
319   table = gtk_table_new (2, 2, FALSE);
320   gtk_container_set_border_width (GTK_CONTAINER (table), 12);
321   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
322   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
323   gtk_box_pack_start (GTK_BOX (gimp_export_dialog_get_content_area (dialog)),
324                       table, TRUE, TRUE, 0);
325   gtk_widget_show (table);
326 
327   entry = gtk_entry_new ();
328   gtk_entry_set_width_chars (GTK_ENTRY (entry), 20);
329   gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
330   gtk_entry_set_text (GTK_ENTRY (entry), info.description);
331   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
332                              _("_Description:"), 1.0, 0.5,
333                              entry, 1, FALSE);
334 
335   g_signal_connect (entry, "changed",
336                     G_CALLBACK (entry_callback),
337                     info.description);
338 
339   adj = (GtkAdjustment *) gtk_adjustment_new (info.spacing, 1, 1000, 1, 10, 0);
340   spinbutton = gimp_spin_button_new (adj, 1.0, 0);
341   gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbutton), TRUE);
342   gtk_entry_set_activates_default (GTK_ENTRY (spinbutton), TRUE);
343   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
344                              _("_Spacing:"), 1.0, 0.5,
345                              spinbutton, 1, TRUE);
346 
347   g_signal_connect (adj, "value-changed",
348                     G_CALLBACK (gimp_int_adjustment_update),
349                     &info.spacing);
350 
351   gtk_widget_show (dialog);
352 
353   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
354 
355   gtk_widget_destroy (dialog);
356 
357   return run;
358 }
359 
360 static void
entry_callback(GtkWidget * widget,gpointer data)361 entry_callback (GtkWidget *widget,
362                 gpointer   data)
363 {
364   strncpy (info.description, gtk_entry_get_text (GTK_ENTRY (widget)),
365            sizeof (info.description));
366   info.description[sizeof (info.description) - 1] = '\0';
367 }
368