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 #include "config.h"
19 
20 #include <string.h>
21 #include <stdlib.h>
22 
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 
30 #include <glib/gstdio.h>
31 
32 #include <libgimp/gimp.h>
33 #include <libgimp/gimpui.h>
34 
35 #include "gimpressionist.h"
36 #include "presets.h"
37 
38 #include "color.h"
39 #include "general.h"
40 #include "orientation.h"
41 #include "placement.h"
42 #include "size.h"
43 
44 
45 #include "libgimp/stdplugins-intl.h"
46 
47 #ifdef G_OS_WIN32
48 #include "libgimpbase/gimpwin32-io.h"
49 #endif
50 
51 #define PRESETMAGIC "Preset"
52 
53 static GtkWidget    *presetnameentry  = NULL;
54 static GtkWidget    *presetlist       = NULL;
55 static GtkWidget    *presetdesclabel  = NULL;
56 static GtkWidget    *presetsavebutton = NULL;
57 static GtkWidget    *delete_button    = NULL;
58 static GtkListStore *store            = NULL;
59 static gchar        *selected_preset_orig_name = NULL;
60 static gchar        *selected_preset_filename  = NULL;
61 
62 static gboolean
can_delete_preset(const gchar * abs)63 can_delete_preset (const gchar *abs)
64 {
65   gchar *user_data_dir;
66   gboolean ret;
67 
68   user_data_dir = g_strconcat (gimp_directory (),
69                                G_DIR_SEPARATOR_S,
70                                NULL);
71 
72 
73   ret = (!strncmp (abs, user_data_dir, strlen (user_data_dir)));
74 
75   g_free (user_data_dir);
76 
77   return ret;
78 }
79 
80 void
preset_save_button_set_sensitive(gboolean s)81 preset_save_button_set_sensitive (gboolean s)
82 {
83   if (GTK_IS_WIDGET (presetsavebutton))
84     gtk_widget_set_sensitive (GTK_WIDGET (presetsavebutton), s);
85 }
86 
87 void
preset_free(void)88 preset_free (void)
89 {
90   g_free (selected_preset_orig_name);
91   g_free (selected_preset_filename);
92 }
93 
set_preset_description_text(const gchar * text)94 static void set_preset_description_text (const gchar *text)
95 {
96   gtk_label_set_text (GTK_LABEL (presetdesclabel), text);
97 }
98 
99 static char presetdesc[4096] = "";
100 
101 static const char *factory_defaults = "<Factory defaults>";
102 
103 static gchar *
get_early_line_from_preset(gchar * full_path,const gchar * prefix)104 get_early_line_from_preset (gchar *full_path, const gchar *prefix)
105 {
106   FILE *f;
107   gchar line[4096];
108   gint prefix_len, line_idx;
109 
110   prefix_len = strlen (prefix);
111   f = g_fopen (full_path, "rt");
112   if (f)
113     {
114       /* Skip the preset magic. */
115       fgets (line, 10, f);
116       if (!strncmp (line, PRESETMAGIC, 4))
117         {
118           for (line_idx = 0; line_idx < 5; line_idx++)
119             {
120               if (!fgets (line, sizeof (line), f))
121                 break;
122               g_strchomp (line);
123               if (!strncmp (line, prefix, prefix_len))
124                 {
125                   fclose(f);
126                   return g_strdup (line+prefix_len);
127                 }
128             }
129         }
130       fclose (f);
131     }
132   return NULL;
133 }
134 
135 static gchar *
get_object_name(const gchar * dir,gchar * filename,void * context)136 get_object_name (const gchar *dir,
137                  gchar       *filename,
138                  void        *context)
139 {
140   gchar *ret = NULL, *unprocessed_line = NULL;
141   gchar *full_path = NULL;
142 
143   /* First try to extract the object's name (= user-friendly description)
144    * from the preset file
145    * */
146 
147   full_path = g_build_filename (dir, filename, NULL);
148 
149   unprocessed_line = get_early_line_from_preset (full_path, "name=");
150   if (unprocessed_line)
151     {
152       ret = g_strcompress (unprocessed_line);
153       g_free (unprocessed_line);
154     }
155   else
156     {
157       /* The object name defaults to a filename-derived description */
158       ret = g_filename_display_basename (full_path);
159     }
160 
161   g_free (full_path);
162 
163   return ret;
164 }
165 
166 
167 static void
preset_read_dir_into_list(void)168 preset_read_dir_into_list (void)
169 {
170   readdirintolist_extended ("Presets", presetlist, NULL, TRUE,
171                             get_object_name, NULL);
172 }
173 
174 static gchar *
preset_create_filename(const gchar * basename,const gchar * dest_dir)175 preset_create_filename (const gchar *basename,
176                         const gchar *dest_dir)
177 {
178   gchar *fullpath;
179   gchar *safe_name;
180   gint   i;
181   gint   unum = 1;
182 
183   g_return_val_if_fail (basename != NULL, NULL);
184   g_return_val_if_fail (dest_dir != NULL, NULL);
185   g_return_val_if_fail (g_path_is_absolute (dest_dir), NULL);
186 
187   safe_name = g_filename_from_utf8 (basename, -1, NULL, NULL, NULL);
188 
189   if (safe_name[0] == '.')
190     safe_name[0] = '-';
191 
192   for (i = 0; safe_name[i]; i++)
193     if (safe_name[i] == G_DIR_SEPARATOR || g_ascii_isspace (safe_name[i]))
194       safe_name[i] = '-';
195 
196   fullpath = g_build_filename (dest_dir, safe_name, NULL);
197 
198   while (g_file_test (fullpath, G_FILE_TEST_EXISTS))
199     {
200       gchar *filename;
201 
202       g_free (fullpath);
203 
204       filename = g_strdup_printf ("%s-%d", safe_name, unum++);
205 
206       fullpath = g_build_filename (dest_dir, filename, NULL);
207 
208       g_free (filename);
209     }
210 
211   g_free (safe_name);
212 
213   return fullpath;
214 }
215 
216 
217 static void
add_factory_defaults(void)218 add_factory_defaults (void)
219 {
220   GtkTreeIter iter;
221 
222   gtk_list_store_append (store, &iter);
223   /* Set the filename. */
224   gtk_list_store_set (store, &iter, PRESETS_LIST_COLUMN_FILENAME,
225                       factory_defaults, -1);
226   /* Set the object name. */
227   gtk_list_store_set (store, &iter, PRESETS_LIST_COLUMN_OBJECT_NAME,
228                       factory_defaults, -1);
229 
230 }
231 
232 static void
preset_refresh_presets(void)233 preset_refresh_presets (void)
234 {
235   gtk_list_store_clear (store);
236   add_factory_defaults ();
237   preset_read_dir_into_list ();
238 }
239 
240 static int
load_old_preset(const gchar * fname)241 load_old_preset (const gchar *fname)
242 {
243   FILE *f;
244   int   len;
245 
246   f = g_fopen (fname, "rb");
247   if (!f)
248     {
249       g_printerr ("Error opening file \"%s\" for reading!\n",
250                   gimp_filename_to_utf8 (fname));
251       return -1;
252     }
253   len = fread (&pcvals, 1, sizeof (pcvals), f);
254   fclose (f);
255 
256   return (len != sizeof (pcvals)) ? -1 : 0;
257 }
258 
259 static unsigned int
hexval(char c)260 hexval (char c)
261 {
262   c = g_ascii_tolower (c);
263   if ((c >= 'a') && (c <= 'f'))
264     return c - 'a' + 10;
265   if ((c >= '0') && (c <= '9'))
266     return c - '0';
267   return 0;
268 }
269 
270 static char *
parse_rgb_string(const gchar * s)271 parse_rgb_string (const gchar *s)
272 {
273   static char col[3];
274   col[0] = (hexval (s[0]) << 4) | hexval (s[1]);
275   col[1] = (hexval (s[2]) << 4) | hexval (s[3]);
276   col[2] = (hexval (s[4]) << 4) | hexval (s[5]);
277   return col;
278 }
279 
280 static void
set_orient_vector(const gchar * str)281 set_orient_vector (const gchar *str)
282 {
283   const gchar *tmps = str;
284   int n;
285 
286   n = atoi (tmps);
287 
288   if (!(tmps = strchr (tmps, ',')))
289     return;
290   pcvals.orient_vectors[n].x = g_ascii_strtod (++tmps, NULL);
291 
292   if (!(tmps = strchr (tmps, ',')))
293     return;
294   pcvals.orient_vectors[n].y = g_ascii_strtod (++tmps, NULL);
295 
296   if (!(tmps = strchr (tmps, ',')))
297     return;
298   pcvals.orient_vectors[n].dir = g_ascii_strtod (++tmps, NULL);
299 
300   if (!(tmps = strchr (tmps, ',')))
301     return;
302   pcvals.orient_vectors[n].dx = g_ascii_strtod (++tmps, NULL);
303 
304   if (!(tmps = strchr (tmps, ',')))
305     return;
306   pcvals.orient_vectors[n].dy = g_ascii_strtod (++tmps, NULL);
307 
308   if (!(tmps = strchr (tmps, ',')))
309     return;
310   pcvals.orient_vectors[n].str = g_ascii_strtod (++tmps, NULL);
311 
312   if (!(tmps = strchr (tmps, ',')))
313     return;
314   pcvals.orient_vectors[n].type = atoi (++tmps);
315 
316 }
317 
set_size_vector(const gchar * str)318 static void set_size_vector (const gchar *str)
319 {
320   const gchar *tmps = str;
321   int          n;
322 
323   n = atoi (tmps);
324 
325   if (!(tmps = strchr (tmps, ',')))
326     return;
327   pcvals.size_vectors[n].x = g_ascii_strtod (++tmps, NULL);
328 
329   if (!(tmps = strchr (tmps, ',')))
330     return;
331   pcvals.size_vectors[n].y = g_ascii_strtod (++tmps, NULL);
332 
333   if (!(tmps = strchr (tmps, ',')))
334     return;
335   pcvals.size_vectors[n].siz = g_ascii_strtod (++tmps, NULL);
336 
337   if (!(tmps = strchr (tmps, ',')))
338     return;
339   pcvals.size_vectors[n].str = g_ascii_strtod (++tmps, NULL);
340 
341 }
342 
343 static void
parse_desc(const gchar * str,gchar * d,gssize d_len)344 parse_desc (const gchar *str, gchar *d, gssize d_len)
345 {
346   gchar *dest = g_strcompress (str);
347 
348   g_strlcpy (d, dest, d_len);
349 
350   g_free (dest);
351 }
352 
353 static void
set_values(const gchar * key,const gchar * val)354 set_values (const gchar *key, const gchar *val)
355 {
356   if (!strcmp (key, "desc"))
357     parse_desc (val, presetdesc, sizeof (presetdesc));
358   else if (!strcmp (key, "orientnum"))
359     pcvals.orient_num = atoi (val);
360   else if (!strcmp (key, "orientfirst"))
361     pcvals.orient_first = g_ascii_strtod (val, NULL);
362   else if (!strcmp (key, "orientlast"))
363     pcvals.orient_last = g_ascii_strtod (val, NULL);
364   else if (!strcmp (key, "orienttype"))
365    pcvals.orient_type = orientation_type_input (atoi (val));
366 
367   else if (!strcmp (key, "sizenum"))
368     pcvals.size_num = atoi (val);
369   else if (!strcmp (key, "sizefirst"))
370     pcvals.size_first = g_ascii_strtod (val, NULL);
371   else if (!strcmp (key, "sizelast"))
372     pcvals.size_last = g_ascii_strtod (val, NULL);
373   else if (!strcmp (key, "sizetype"))
374    pcvals.size_type = size_type_input (atoi (val));
375 
376   else if (!strcmp (key, "brushrelief"))
377     pcvals.brush_relief = g_ascii_strtod (val, NULL);
378   else if (!strcmp (key, "brushscale"))
379     {
380       /* For compatibility */
381       pcvals.size_num = 1;
382       pcvals.size_first = pcvals.size_last = g_ascii_strtod (val, NULL);
383     }
384   else if (!strcmp (key, "brushdensity"))
385     pcvals.brush_density = g_ascii_strtod (val, NULL);
386   else if (!strcmp (key, "brushgamma"))
387     pcvals.brushgamma = g_ascii_strtod (val, NULL);
388   else if (!strcmp (key, "brushaspect"))
389     pcvals.brush_aspect = g_ascii_strtod (val, NULL);
390 
391   else if (!strcmp (key, "generalbgtype"))
392     pcvals.general_background_type = general_bg_type_input (atoi (val));
393   else if (!strcmp (key, "generaldarkedge"))
394     pcvals.general_dark_edge = g_ascii_strtod (val, NULL);
395   else if (!strcmp (key, "generalpaintedges"))
396     pcvals.general_paint_edges = atoi (val);
397   else if (!strcmp (key, "generaltileable"))
398     pcvals.general_tileable = atoi (val);
399   else if (!strcmp (key, "generaldropshadow"))
400     pcvals.general_drop_shadow = atoi (val);
401   else if (!strcmp (key, "generalshadowdarkness"))
402     pcvals.general_shadow_darkness = g_ascii_strtod (val, NULL);
403   else if (!strcmp (key, "generalshadowdepth"))
404     pcvals.general_shadow_depth = atoi (val);
405   else if (!strcmp (key, "generalshadowblur"))
406     pcvals.general_shadow_blur = atoi (val);
407   else if (!strcmp (key, "devthresh"))
408     pcvals.devthresh = g_ascii_strtod (val, NULL);
409 
410   else if (!strcmp (key, "paperrelief"))
411     pcvals.paper_relief = g_ascii_strtod (val, NULL);
412   else if (!strcmp (key, "paperscale"))
413     pcvals.paper_scale = g_ascii_strtod (val, NULL);
414   else if (!strcmp (key, "paperinvert"))
415     pcvals.paper_invert = atoi (val);
416   else if (!strcmp (key, "paperoverlay"))
417     pcvals.paper_overlay = atoi (val);
418 
419   else if (!strcmp (key, "placetype"))
420     pcvals.place_type = place_type_input (atoi (val));
421   else if (!strcmp (key, "placecenter"))
422     pcvals.placement_center = atoi (val);
423 
424   else if (!strcmp (key, "selectedbrush"))
425     g_strlcpy (pcvals.selected_brush, val, sizeof (pcvals.selected_brush));
426   else if (!strcmp (key, "selectedpaper"))
427     g_strlcpy (pcvals.selected_paper, val, sizeof (pcvals.selected_paper));
428 
429   else if (!strcmp (key, "color"))
430     {
431       char *c = parse_rgb_string (val);
432       gimp_rgba_set_uchar (&pcvals.color, c[0], c[1], c[2], 255);
433     }
434 
435   else if (!strcmp (key, "numorientvector"))
436     pcvals.num_orient_vectors = atoi (val);
437   else if (!strcmp (key, "orientvector"))
438     set_orient_vector (val);
439   else if (!strcmp (key, "orientangoff"))
440    pcvals.orient_angle_offset = g_ascii_strtod (val, NULL);
441   else if (!strcmp (key, "orientstrexp"))
442    pcvals.orient_strength_exponent = g_ascii_strtod (val, NULL);
443   else if (!strcmp (key, "orientvoronoi"))
444    pcvals.orient_voronoi = atoi (val);
445 
446   else if (!strcmp (key, "numsizevector"))
447     pcvals.num_size_vectors = atoi (val);
448   else if (!strcmp (key, "sizevector"))
449     set_size_vector (val);
450   else if (!strcmp (key, "sizestrexp"))
451    pcvals.size_strength_exponent = g_ascii_strtod (val, NULL);
452   else if (!strcmp (key, "sizevoronoi"))
453    pcvals.size_voronoi = atoi (val);
454 
455   else if (!strcmp (key, "colortype"))
456     pcvals.color_type = color_type_input (atoi (val));
457   else if (!strcmp (key, "colornoise"))
458     pcvals.color_noise = g_ascii_strtod (val, NULL);
459 }
460 
461 static int
load_preset(const gchar * fn)462 load_preset (const gchar *fn)
463 {
464   char  line[1024] = "";
465   FILE *f;
466 
467   f = g_fopen (fn, "rt");
468   if (!f)
469     {
470       g_printerr ("Error opening file \"%s\" for reading!\n",
471                   gimp_filename_to_utf8 (fn));
472       return -1;
473     }
474   fgets (line, 10, f);
475   if (strncmp (line, PRESETMAGIC, 4))
476     {
477       fclose (f);
478       return load_old_preset (fn);
479     }
480 
481   restore_default_values ();
482 
483   while (!feof (f))
484     {
485       char *tmps;
486 
487       if (!fgets (line, 1024, f))
488         break;
489       g_strchomp (line);
490       tmps = strchr (line, '=');
491       if (!tmps)
492         continue;
493       *tmps = '\0';
494       tmps++;
495       set_values (line, tmps);
496     }
497   fclose (f);
498   return 0;
499 }
500 
501 int
select_preset(const gchar * preset)502 select_preset (const gchar *preset)
503 {
504   int ret = SELECT_PRESET_OK;
505   /* I'm copying this behavior as is. As it seems applying the
506    * factory_defaults preset does nothing, which I'm not sure
507    * if that was what the author intended.
508    *              -- Shlomi Fish
509    */
510   if (strcmp (preset, factory_defaults))
511     {
512       gchar *rel = g_build_filename ("Presets", preset, NULL);
513       gchar *abs = findfile (rel);
514 
515       g_free (rel);
516 
517       if (abs)
518         {
519           if (load_preset (abs))
520             ret = SELECT_PRESET_LOAD_FAILED;
521 
522           g_free (abs);
523         }
524       else
525         {
526           ret = SELECT_PRESET_FILE_NOT_FOUND;
527         }
528     }
529   if (ret == SELECT_PRESET_OK)
530     {
531       /* This is so the colorbrushes param (that is not stored in the
532        * preset will be set correctly upon the preset loading.
533        * */
534       set_colorbrushes (pcvals.selected_brush);
535     }
536 
537   return ret;
538 }
539 
540 static void
apply_preset(GtkWidget * w,GtkTreeSelection * selection)541 apply_preset (GtkWidget *w, GtkTreeSelection *selection)
542 {
543   GtkTreeIter   iter;
544   GtkTreeModel *model;
545 
546   if (gtk_tree_selection_get_selected (selection, &model, &iter))
547     {
548       gchar *preset;
549 
550       gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_FILENAME,
551                           &preset, -1);
552 
553       select_preset (preset);
554 
555       restore_values ();
556 
557       /* g_free (preset); */
558       g_free (selected_preset_filename);
559       selected_preset_filename = preset;
560     }
561 }
562 
563 static void
delete_preset(GtkWidget * w,GtkTreeSelection * selection)564 delete_preset (GtkWidget *w, GtkTreeSelection *selection)
565 {
566   GtkTreeIter iter;
567   GtkTreeModel *model;
568 
569   if (gtk_tree_selection_get_selected (selection, &model, &iter))
570     {
571       gchar *preset;
572 
573       gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_FILENAME,
574                           &preset, -1);
575 
576       if (preset)
577         {
578           gchar *rel = g_build_filename ("Presets", preset, NULL);
579           gchar *abs = findfile (rel);
580 
581           g_free (rel);
582 
583           if (abs)
584             {
585               /* Don't delete global presets - bug # 147483 */
586               if (can_delete_preset (abs))
587                 g_unlink (abs);
588 
589               g_free (abs);
590             }
591 
592           preset_refresh_presets ();
593 
594           g_free (preset);
595         }
596     }
597 }
598 
599 static void save_preset (void);
600 
601 static void
preset_desc_callback(GtkTextBuffer * buffer,gpointer data)602 preset_desc_callback (GtkTextBuffer *buffer, gpointer data)
603 {
604   char        *str;
605   GtkTextIter  start, end;
606 
607   gtk_text_buffer_get_bounds (buffer, &start, &end);
608   str = gtk_text_buffer_get_text (buffer, &start, &end, FALSE);
609   g_strlcpy (presetdesc, str, sizeof (presetdesc));
610   g_free (str);
611 }
612 
613 static void
save_preset_response(GtkWidget * widget,gint response_id,gpointer data)614 save_preset_response (GtkWidget *widget,
615                       gint       response_id,
616                       gpointer   data)
617 {
618   gtk_widget_destroy (widget);
619 
620   if (response_id == GTK_RESPONSE_OK)
621     save_preset ();
622 }
623 
624 static void
create_save_preset(GtkWidget * parent)625 create_save_preset (GtkWidget *parent)
626 {
627   static GtkWidget *window = NULL;
628   GtkWidget        *box, *label;
629   GtkWidget        *swin, *text;
630   GtkTextBuffer    *buffer;
631 
632   if (window)
633     {
634       gtk_window_present (GTK_WINDOW (window));
635       return;
636     }
637 
638   window = gimp_dialog_new (_("Save Current"), PLUG_IN_ROLE,
639                             gtk_widget_get_toplevel (parent), 0,
640                             gimp_standard_help_func, PLUG_IN_PROC,
641 
642                             _("_Cancel"), GTK_RESPONSE_CANCEL,
643                             _("_OK"),     GTK_RESPONSE_OK,
644 
645                             NULL);
646 
647   gtk_dialog_set_alternative_button_order (GTK_DIALOG (window),
648                                            GTK_RESPONSE_OK,
649                                            GTK_RESPONSE_CANCEL,
650                                            -1);
651 
652   g_signal_connect (window, "response",
653                     G_CALLBACK (save_preset_response),
654                     NULL);
655   g_signal_connect (window, "destroy",
656                     G_CALLBACK (gtk_widget_destroyed),
657                     &window);
658 
659   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
660   gtk_container_set_border_width (GTK_CONTAINER (box), 12);
661   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (window))),
662                       box, TRUE, TRUE, 0);
663   gtk_widget_show (box);
664 
665   label = gtk_label_new (_("Description:"));
666   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
667   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
668   gtk_widget_show (label);
669 
670   swin = gtk_scrolled_window_new (NULL, NULL);
671   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
672                                        GTK_SHADOW_IN);
673   gtk_box_pack_start (GTK_BOX (box), swin, TRUE, TRUE, 0);
674   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
675                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
676   gtk_widget_show (swin);
677 
678   buffer = gtk_text_buffer_new (NULL);
679   g_signal_connect (buffer, "changed",
680                     G_CALLBACK (preset_desc_callback), NULL);
681   gtk_text_buffer_set_text (buffer, presetdesc, -1);
682 
683   text = gtk_text_view_new_with_buffer (buffer);
684   gtk_widget_set_size_request (text, -1, 192);
685   gtk_container_add (GTK_CONTAINER (swin), text);
686   gtk_widget_show (text);
687 
688   gtk_widget_show (window);
689 }
690 
691 static void
save_preset(void)692 save_preset (void)
693 {
694   const gchar *preset_name;
695 
696   gchar *fname, *presets_dir_path;
697   FILE  *f;
698   GList *thispath;
699   gchar  buf[G_ASCII_DTOSTR_BUF_SIZE];
700   gchar  vbuf[6][G_ASCII_DTOSTR_BUF_SIZE];
701   guchar color[3];
702   gint   i;
703   gchar *desc_escaped, *preset_name_escaped;
704 
705   preset_name = gtk_entry_get_text (GTK_ENTRY (presetnameentry));
706   thispath = parsepath ();
707   store_values ();
708 
709   if (!thispath)
710     {
711       g_printerr ("Internal error: (save_preset) thispath == NULL\n");
712       return;
713     }
714 
715   /* Create the ~/.gimp-$VER/gimpressionist/Presets directory if
716    * it doesn't already exists.
717    *
718    */
719   presets_dir_path = g_build_filename ((const gchar *) thispath->data,
720                                        "Presets",
721                                        NULL);
722 
723   if (! g_file_test (presets_dir_path, G_FILE_TEST_IS_DIR))
724     {
725       if (g_mkdir (presets_dir_path,
726                    S_IRUSR | S_IWUSR | S_IXUSR |
727                    S_IRGRP | S_IXGRP |
728                    S_IROTH | S_IXOTH) == -1)
729         {
730           g_printerr ("Error creating folder \"%s\"!\n",
731                       gimp_filename_to_utf8 (presets_dir_path));
732           g_free (presets_dir_path);
733           return;
734         }
735     }
736 
737   /* Check if the user-friendly name has changed. If so, then save it
738    * under a new file. If not - use the same file name.
739    */
740   if (selected_preset_orig_name &&
741       strcmp (preset_name, selected_preset_orig_name) == 0)
742     {
743       fname = g_build_filename (presets_dir_path,
744                                 selected_preset_filename,
745                                 NULL);
746     }
747   else
748     {
749       fname = preset_create_filename (preset_name, presets_dir_path);
750     }
751 
752   g_free (presets_dir_path);
753 
754   if (!fname)
755     {
756       g_printerr ("Error building a filename for preset \"%s\"!\n",
757                   preset_name);
758       return;
759     }
760 
761   f = g_fopen (fname, "wt");
762   if (!f)
763     {
764       g_printerr ("Error opening file \"%s\" for writing!\n",
765                   gimp_filename_to_utf8 (fname));
766       g_free (fname);
767       return;
768     }
769 
770   fprintf (f, "%s\n", PRESETMAGIC);
771   desc_escaped = g_strescape (presetdesc, NULL);
772   fprintf (f, "desc=%s\n", desc_escaped);
773   g_free (desc_escaped);
774   preset_name_escaped = g_strescape (preset_name, NULL);
775   fprintf (f, "name=%s\n", preset_name_escaped);
776   g_free (preset_name_escaped);
777   fprintf (f, "orientnum=%d\n", pcvals.orient_num);
778   fprintf (f, "orientfirst=%s\n",
779            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_first));
780   fprintf (f, "orientlast=%s\n",
781            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_last));
782   fprintf (f, "orienttype=%d\n", pcvals.orient_type);
783 
784   fprintf (f, "sizenum=%d\n", pcvals.size_num);
785   fprintf (f, "sizefirst=%s\n",
786            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_first));
787   fprintf (f, "sizelast=%s\n",
788            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_last));
789   fprintf (f, "sizetype=%d\n", pcvals.size_type);
790 
791   fprintf (f, "brushrelief=%s\n",
792            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brush_relief));
793   fprintf (f, "brushdensity=%s\n",
794            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brush_density));
795   fprintf (f, "brushgamma=%s\n",
796            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brushgamma));
797   fprintf (f, "brushaspect=%s\n",
798            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.brush_aspect));
799 
800   fprintf (f, "generalbgtype=%d\n", pcvals.general_background_type);
801   fprintf (f, "generaldarkedge=%s\n",
802            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.general_dark_edge));
803   fprintf (f, "generalpaintedges=%d\n", pcvals.general_paint_edges);
804   fprintf (f, "generaltileable=%d\n", pcvals.general_tileable);
805   fprintf (f, "generaldropshadow=%d\n", pcvals.general_drop_shadow);
806   fprintf (f, "generalshadowdarkness=%s\n",
807            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.general_shadow_darkness));
808   fprintf (f, "generalshadowdepth=%d\n", pcvals.general_shadow_depth);
809   fprintf (f, "generalshadowblur=%d\n", pcvals.general_shadow_blur);
810   fprintf (f, "devthresh=%s\n",
811            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.devthresh));
812 
813   fprintf (f, "paperrelief=%s\n",
814            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.paper_relief));
815   fprintf (f, "paperscale=%s\n",
816            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.paper_scale));
817   fprintf (f, "paperinvert=%d\n", pcvals.paper_invert);
818   fprintf (f, "paperoverlay=%d\n", pcvals.paper_overlay);
819 
820   fprintf (f, "selectedbrush=%s\n", pcvals.selected_brush);
821   fprintf (f, "selectedpaper=%s\n", pcvals.selected_paper);
822 
823   gimp_rgb_get_uchar (&pcvals.color, &color[0], &color[1], &color[2]);
824   fprintf (f, "color=%02x%02x%02x\n", color[0], color[1], color[2]);
825 
826   fprintf (f, "placetype=%d\n", pcvals.place_type);
827   fprintf (f, "placecenter=%d\n", pcvals.placement_center);
828 
829   fprintf (f, "numorientvector=%d\n", pcvals.num_orient_vectors);
830   for (i = 0; i < pcvals.num_orient_vectors; i++)
831     {
832       g_ascii_dtostr (vbuf[0], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].x);
833       g_ascii_dtostr (vbuf[1], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].y);
834       g_ascii_dtostr (vbuf[2], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].dir);
835       g_ascii_dtostr (vbuf[3], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].dx);
836       g_ascii_dtostr (vbuf[4], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].dy);
837       g_ascii_dtostr (vbuf[5], G_ASCII_DTOSTR_BUF_SIZE, pcvals.orient_vectors[i].str);
838 
839       fprintf (f, "orientvector=%d,%s,%s,%s,%s,%s,%s,%d\n", i,
840                vbuf[0], vbuf[1], vbuf[2], vbuf[3], vbuf[4], vbuf[5],
841                pcvals.orient_vectors[i].type);
842     }
843 
844   fprintf (f, "orientangoff=%s\n",
845            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
846                            pcvals.orient_angle_offset));
847   fprintf (f, "orientstrexp=%s\n",
848            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE,
849                            pcvals.orient_strength_exponent));
850   fprintf (f, "orientvoronoi=%d\n", pcvals.orient_voronoi);
851 
852   fprintf (f, "numsizevector=%d\n", pcvals.num_size_vectors);
853   for (i = 0; i < pcvals.num_size_vectors; i++)
854     {
855       g_ascii_dtostr (vbuf[0], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].x);
856       g_ascii_dtostr (vbuf[1], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].y);
857       g_ascii_dtostr (vbuf[2], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].siz);
858       g_ascii_dtostr (vbuf[3], G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_vectors[i].str);
859       fprintf (f, "sizevector=%d,%s,%s,%s,%s\n", i,
860                vbuf[0], vbuf[1], vbuf[2], vbuf[3]);
861     }
862   fprintf (f, "sizestrexp=%s\n",
863            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.size_strength_exponent));
864   fprintf (f, "sizevoronoi=%d\n", pcvals.size_voronoi);
865 
866   fprintf (f, "colortype=%d\n", pcvals.color_type);
867   fprintf (f, "colornoise=%s\n",
868            g_ascii_dtostr (buf, G_ASCII_DTOSTR_BUF_SIZE, pcvals.color_noise));
869 
870   fclose (f);
871   preset_refresh_presets ();
872   reselect (presetlist, fname);
873 
874   g_free (fname);
875 }
876 
877 static void
read_description(const char * fn)878 read_description (const char *fn)
879 {
880   char *rel_fname;
881   char *fname;
882   gchar *unprocessed_line;
883 
884   rel_fname = g_build_filename ("Presets", fn, NULL);
885   fname   = findfile (rel_fname);
886   g_free (rel_fname);
887 
888   if (!fname)
889     {
890       if (!strcmp (fn, factory_defaults))
891         {
892           gtk_widget_set_sensitive (delete_button, FALSE);
893           set_preset_description_text (_("Gimpressionist Defaults"));
894         }
895       else
896         {
897           set_preset_description_text ("");
898         }
899       return;
900     }
901 
902   /* Don't delete global presets - bug # 147483 */
903   gtk_widget_set_sensitive (delete_button, can_delete_preset (fname));
904 
905   unprocessed_line = get_early_line_from_preset (fname, "desc=");
906   g_free (fname);
907 
908   if (unprocessed_line)
909     {
910       char tmplabel[4096];
911       parse_desc (unprocessed_line, tmplabel, sizeof (tmplabel));
912       g_free (unprocessed_line);
913       set_preset_description_text (tmplabel);
914     }
915   else
916     {
917       set_preset_description_text ("");
918     }
919 }
920 
921 static void
presets_list_select_preset(GtkTreeSelection * selection,gpointer data)922 presets_list_select_preset (GtkTreeSelection *selection,
923                             gpointer          data)
924 {
925   GtkTreeIter iter;
926   GtkTreeModel *model;
927 
928   if (gtk_tree_selection_get_selected (selection, &model, &iter))
929     {
930       gchar *preset_name;
931       gchar *preset_filename;
932 
933       gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_OBJECT_NAME,
934                           &preset_name, -1);
935       gtk_tree_model_get (model, &iter, PRESETS_LIST_COLUMN_FILENAME,
936                           &preset_filename, -1);
937 
938       /* TODO : Maybe make the factory defaults behavior in regards
939        * to the preset's object name and filename more robust?
940        *
941        */
942       if (strcmp (preset_filename, factory_defaults))
943         {
944           gtk_entry_set_text (GTK_ENTRY (presetnameentry), preset_name);
945           g_free (selected_preset_orig_name);
946           g_free (selected_preset_filename);
947           selected_preset_orig_name = g_strdup (preset_name);
948           selected_preset_filename = g_strdup (selected_preset_filename);
949         }
950 
951       read_description (preset_filename);
952 
953       g_free (preset_name);
954       g_free (preset_filename);
955     }
956 }
957 
958 static GtkWidget *
create_presets_list(GtkWidget * parent)959 create_presets_list (GtkWidget *parent)
960 {
961   GtkListStore      *store;
962   GtkTreeSelection  *selection;
963   GtkCellRenderer   *renderer;
964   GtkTreeViewColumn *column;
965   GtkWidget         *swin, *view;
966 
967   swin = gtk_scrolled_window_new (NULL, NULL);
968   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swin),
969                                   GTK_POLICY_AUTOMATIC,
970                                   GTK_POLICY_AUTOMATIC);
971   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (swin),
972                                        GTK_SHADOW_IN);
973   gtk_box_pack_start (GTK_BOX (parent), swin, FALSE, FALSE, 0);
974   gtk_widget_show (swin);
975   gtk_widget_set_size_request (swin, 200, -1);
976 
977   store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
978   view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
979 
980   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (view), FALSE);
981   g_object_unref (store);
982   gtk_widget_show (view);
983 
984   renderer = gtk_cell_renderer_text_new ();
985 
986   column =
987       gtk_tree_view_column_new_with_attributes ("Preset", renderer,
988                                                 "text",
989                                                 PRESETS_LIST_COLUMN_OBJECT_NAME,
990                                                 NULL);
991   gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
992 
993 
994   gtk_container_add (GTK_CONTAINER (swin), view);
995 
996   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
997   gtk_tree_selection_set_mode (selection, GTK_SELECTION_BROWSE);
998   g_signal_connect (selection, "changed",
999                     G_CALLBACK (presets_list_select_preset),
1000                     NULL);
1001 
1002   return view;
1003 }
1004 
1005 void
create_presetpage(GtkNotebook * notebook)1006 create_presetpage (GtkNotebook *notebook)
1007 {
1008   GtkWidget *vbox, *hbox, *box1, *box2, *thispage;
1009   GtkWidget *view;
1010   GtkWidget *tmpw;
1011   GtkWidget *label;
1012   GtkTreeSelection *selection;
1013 
1014   label = gtk_label_new_with_mnemonic (_("_Presets"));
1015 
1016   thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1017   gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
1018   gtk_widget_show (thispage);
1019 
1020   box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
1021   gtk_box_pack_start (GTK_BOX (thispage), box1, FALSE, FALSE, 0);
1022   gtk_widget_show (box1);
1023 
1024   presetnameentry = tmpw = gtk_entry_new ();
1025   gtk_box_pack_start (GTK_BOX (box1), tmpw, FALSE, FALSE, 0);
1026   gtk_widget_set_size_request (tmpw, 200, -1);
1027   gtk_widget_show (tmpw);
1028 
1029   presetsavebutton = tmpw = gtk_button_new_with_label ( _("Save Current..."));
1030   gtk_button_set_image (GTK_BUTTON (presetsavebutton),
1031                         gtk_image_new_from_icon_name (GIMP_ICON_DOCUMENT_SAVE,
1032                                                       GTK_ICON_SIZE_BUTTON));
1033   gtk_box_pack_start (GTK_BOX (box1), tmpw, FALSE, FALSE, 0);
1034   gtk_widget_show (tmpw);
1035   g_signal_connect (tmpw, "clicked", G_CALLBACK (create_save_preset), NULL);
1036   gimp_help_set_help_data
1037     (tmpw, _("Save the current settings to the specified file"), NULL);
1038 
1039   box1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
1040   gtk_box_pack_start (GTK_BOX (thispage), box1, TRUE, TRUE, 0);
1041   gtk_widget_show (box1);
1042 
1043   presetlist = view = create_presets_list (box1);
1044   store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
1045   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
1046   add_factory_defaults ();
1047 
1048   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1049   gtk_box_pack_start (GTK_BOX (box1), vbox, FALSE, FALSE, 0);
1050   gtk_widget_show (vbox);
1051 
1052   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1053   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1054   gtk_widget_show (hbox);
1055 
1056   box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
1057   gtk_box_pack_start (GTK_BOX (hbox), box2, FALSE, FALSE, 0);
1058   gtk_widget_show (box2);
1059 
1060   tmpw = gtk_button_new_with_mnemonic (_("_Apply"));
1061   gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE, 0);
1062   gtk_widget_show (tmpw);
1063   g_signal_connect (tmpw, "clicked", G_CALLBACK (apply_preset), selection);
1064   gimp_help_set_help_data
1065     (tmpw, _("Reads the selected Preset into memory"), NULL);
1066 
1067   tmpw = delete_button = gtk_button_new_with_mnemonic (_("_Delete"));
1068   gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE,0);
1069   gtk_widget_show (tmpw);
1070   g_signal_connect (tmpw, "clicked", G_CALLBACK (delete_preset), selection);
1071   gimp_help_set_help_data (tmpw, _("Deletes the selected Preset"), NULL);
1072 
1073   tmpw = gtk_button_new_with_mnemonic (_("_Refresh"));
1074   gtk_box_pack_start (GTK_BOX (box2), tmpw, FALSE, FALSE,0);
1075   gtk_widget_show (tmpw);
1076   g_signal_connect (tmpw, "clicked", G_CALLBACK (preset_refresh_presets), NULL);
1077   gimp_help_set_help_data (tmpw, _("Reread the folder of Presets"), NULL);
1078 
1079   presetdesclabel = tmpw = gtk_label_new (NULL);
1080   gimp_label_set_attributes (GTK_LABEL (tmpw),
1081                              PANGO_ATTR_STYLE, PANGO_STYLE_ITALIC,
1082                              -1);
1083   gtk_label_set_line_wrap (GTK_LABEL (tmpw), TRUE);
1084   /*
1085    * Make sure the label's width is reasonable and it won't stretch
1086    * the dialog more than its width.
1087    * */
1088   gtk_widget_set_size_request (tmpw, 240, -1);
1089 
1090   gtk_label_set_xalign (GTK_LABEL (tmpw), 0.0);
1091   gtk_label_set_yalign (GTK_LABEL (tmpw), 0.0);
1092   gtk_box_pack_start (GTK_BOX (vbox), tmpw, TRUE, TRUE, 0);
1093   gtk_widget_show (tmpw);
1094 
1095   preset_read_dir_into_list ();
1096 
1097   gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
1098 }
1099