1 /**
2  * Copyright (C) 2012 hejian <hejian.he@gmail.com>
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 <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <libgimp/gimp.h>
19 #include <libgimp/gimpui.h>
20 
21 /* fix gimp_context_set_pattern ("Clipboard") only works on English versions of Gimp */
22 #include <glib/gi18n.h>
23 #define GETTEXT_PACKAGE "gimp20"
24 #define INIT_I18N()	G_STMT_START{                                \
25   bindtextdomain (GETTEXT_PACKAGE,                    \
26                   gimp_locale_directory ());                         \
27   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); \
28   textdomain (GETTEXT_PACKAGE);		             \
29 }G_STMT_END
30 
31 #include "simple-border-textures.h"
32 
33 #define PLUG_IN_PROC   "plug-in-simple-border"
34 #define PLUG_IN_BINARY "border"
35 #define PLUG_IN_ROLE   "gimp-border"
36 
37 #define TEXTURE_PATH     "simple-border"
38 
39 #define PREVIEW_SIZE  480
40 #define THUMBNAIL_SIZE  80
41 
42 typedef struct
43 {
44   const gchar  *id;
45   gint32        top;
46   gint32        bottom;
47   gint32        left;
48   gint32        right;
49   gint32        length;
50   const guint8 *texture;
51 } Border;
52 
53 typedef struct
54 {
55   const Border* border;
56   const gchar *custom_texture;
57 } BorderValues;
58 
59 static void     query    (void);
60 static void     run      (const gchar      *name,
61                           gint              nparams,
62                           const GimpParam  *param,
63                           gint             *nreturn_vals,
64                           GimpParam       **return_vals);
65 
66 static void     border (gint32     image_ID);
67 
68 static gboolean border_dialog (gint32 image_ID,
69                                GimpDrawable *drawable);
70 
71 static void     create_texture_page (GtkNotebook *notebook, const gchar *category, const Border* textures, guint n_textures);
72 static gboolean create_custom_texture_pages (GtkNotebook *notebook, const gchar *texture_path);
73 static void     create_custom_texture_page (GtkNotebook *notebook, const gchar *category, const gchar *path);
74 static void     textures_switch_page (GtkNotebook *notebook, GtkWidget *page, guint page_num, const gchar *texture_path);
75 static GtkWidget* effect_icon_new (const Border *border);
76 
77 static gboolean texture_press (GtkWidget *event_box, GdkEventButton *event, const Border *border);
78 static gboolean custom_texture_press (GtkWidget *event_box, GdkEventButton *event, const gchar *texture);
79 static void     do_texture_press ();
80 
81 static inline gboolean
is_hidden(const gchar * filename)82 is_hidden (const gchar *filename)
83 {
84   /* skip files starting with '.' so we don't try to parse
85    * stuff like .DS_Store or other metadata storage files
86    */
87   return (filename[0] == '.');
88 }
89 
90 const GimpPlugInInfo PLUG_IN_INFO =
91 {
92   NULL,  /* init_proc  */
93   NULL,  /* quit_proc  */
94   query, /* query_proc */
95   run,   /* run_proc   */
96 };
97 
98 static BorderValues bvals =
99 {
100   NULL,                 /* border */
101 };
102 
103 static gint32     image_ID         = 0;
104 static gint       width;
105 static gint       height;
106 
107 static GtkWidget *preview          = NULL;
108 static gint32     preview_image    = 0;
109 
110 static const Border textures[] =
111 {
112   {"15356",   0, 0, 0, 0, 10, texture_15356},
113   {"15327",   24,37,29,41,10, texture_15327},
114   {"201544",  12,12,12,12,10, texture_201544},
115   {"15352",   9, 35,7, 6, 10, texture_15352},
116   {"201365",  24,23,23,23,10, texture_201365},
117   {"15349",   56,63,54,56,10, texture_15349},
118   {"24252",   40,43,35,38,90, texture_24252},
119   {"114844",  14,13,14,14,10, texture_114844},
120   {"200832",  14,14,14,14,10, texture_200832},
121   {"200878",  12,12,12,12,10, texture_200878},
122   {"201017",  0, 0, 0, 0, 10, texture_201017},
123   {"114568",  0, 0, 0, 0, 10, texture_114568},
124   {"111026",  0, 0, 0, 0, 10, texture_111026},
125   {"200377",  20,166,20,20,10,texture_200377},
126   {"201385",  11,12,12,12,10, texture_201385},
127   {"111025",  25,56,16,16,10, texture_111025},
128   {"200547",  21,21,17,17,10, texture_200547},
129   {"200776",  16,16,16,16,10, texture_200776},
130   {"200646",  18,18, 8, 8,18, texture_200646},
131   {"200282",  0, 0, 0, 0, 16, texture_200282},
132   {"111020",  16,18,17,19,10, texture_111020},
133   {"113579",  28,28,25,25,42, texture_113579},
134   {"111569",  0, 0, 0, 0, 10, texture_111569},
135   {"200031",  0, 0, 0, 0, 10, texture_200031},
136   {"113576",  39,39,40,40,36, texture_113576},
137   {"114907",  0, 0, 0, 0, 16, texture_114907},
138   {"114577",  0, 0, 0, 0, 44, texture_114577},
139   {"113575",  38,38,38,38,34, texture_113575},
140   {"113573",  27,22,26,24,10, texture_113573},
141   {"114576",  0, 0, 0, 0, 10, texture_114576},
142   {"111013",  35,35,35,35,10, texture_111013},
143   {"114575",  0, 0, 0, 0, 10, texture_114575},
144   {"111017",  31,31,31,31,10, texture_111017},
145   {"114573",  10,10,10,10,10, texture_114573},
146   {"111027",  17,16,16,19,10, texture_111027},
147   {"111021",  18,18,21,22,22, texture_111021},
148   {"114572",  16,16,14,14,10, texture_114572},
149   {"114570",  20,22,21,19,21, texture_114570},
150   {"22753",   34,36,34,36,53, texture_22753},
151   {"20130",   59,59,0, 0, 10, texture_20130},
152   {"114569",  20,20,20,19,21, texture_114569},
153   {"20129",   83,82,0, 0, 10, texture_20129},
154   {"15354",   30,45,47,58,10, texture_15354},
155   {"15357",   45,45,0, 0, 32, texture_15357},
156   {"15353",   13,48,10,11,10, texture_15353},
157   {"15351",   15,73,16,17,10, texture_15351},
158   {"15328",   7, 7, 7, 7, 16, texture_15328},
159   {"15350",   0, 0, 0, 0, 10, texture_15350},
160   {"15329",   13,14,20,19,10, texture_15329},
161   {"15330",   26,23,25,27,10, texture_15330},
162   {"114567",  14,13,13,13,10, texture_114567},
163   {"15870",   39,50,41,192,63,texture_15870},
164   {"21926",   43,53,43,212,429,texture_21926},
165   {"21959",   42,50,44,216,74,texture_21959},
166   {"21927",   43,53,43,212,23,texture_21927},
167   {"15871",   39,50,41,210,16,texture_15871},
168 };
169 
170 static GArray *textures_timestamps = NULL;
171 
172 /* compatable with gtk2 */
173 #if GTK_MAJOR_VERSION < 3
174 GtkWidget *
gtk_box_new(GtkOrientation orientation,gint spacing)175 gtk_box_new (GtkOrientation  orientation,
176              gint            spacing)
177 {
178   if (orientation == GTK_ORIENTATION_HORIZONTAL)
179     return gtk_hbox_new (FALSE, spacing);
180   else
181     return gtk_vbox_new (FALSE, spacing);
182 }
183 #endif
184 
MAIN()185 MAIN ()
186 
187 static void
188 query (void)
189 {
190   static const GimpParamDef args[] =
191   {
192     { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
193     { GIMP_PDB_IMAGE,    "image",      "Input image" },
194     { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable" },
195   };
196 
197   gimp_install_procedure (PLUG_IN_PROC,
198                           "Create an simple border effect",
199                           "Create an simple border effect",
200                           "Hejian <hejian.he@gmail.com>",
201                           "Hejian <hejian.he@gmail.com>",
202                           "2012",
203                           "_Simple Border...",
204                           "RGB*, GRAY*",
205                           GIMP_PLUGIN,
206                           G_N_ELEMENTS (args), 0,
207                           args, NULL);
208 
209   gimp_plugin_menu_register (PLUG_IN_PROC,     "<Image>/Filters/Beautify/Border");
210 }
211 
212 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)213 run (const gchar      *name,
214      gint              nparams,
215      const GimpParam  *param,
216      gint             *nreturn_vals,
217      GimpParam       **return_vals)
218 {
219   static GimpParam   values[2];
220   GimpDrawable      *drawable;
221   GimpRunMode        run_mode;
222   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
223 
224   run_mode = param[0].data.d_int32;
225 
226   *nreturn_vals = 1;
227   *return_vals  = values;
228 
229   values[0].type          = GIMP_PDB_STATUS;
230   values[0].data.d_status = status;
231 
232   image_ID = param[1].data.d_image;
233   drawable = gimp_drawable_get (param[2].data.d_drawable);
234 
235   width = gimp_image_width (image_ID);
236   height = gimp_image_height (image_ID);
237 
238     switch (run_mode)
239     {
240       case GIMP_RUN_INTERACTIVE:
241         if (! border_dialog (image_ID, drawable))
242           return;
243         break;
244 
245       case GIMP_RUN_NONINTERACTIVE:
246         break;
247 
248       case GIMP_RUN_WITH_LAST_VALS:
249         break;
250 
251       default:
252         break;
253     }
254 
255   if ((status == GIMP_PDB_SUCCESS) &&
256       (gimp_drawable_is_rgb(drawable->drawable_id) ||
257        gimp_drawable_is_gray(drawable->drawable_id)))
258     {
259       /* Run! */
260       gimp_image_undo_group_start (image_ID);
261       border (image_ID);
262       gimp_image_undo_group_end (image_ID);
263 
264       /* If run mode is interactive, flush displays */
265       if (run_mode != GIMP_RUN_NONINTERACTIVE)
266         gimp_displays_flush ();
267 
268       /* Store data */
269       /*if (run_mode == GIMP_RUN_INTERACTIVE)
270         gimp_set_data (PLUG_IN_PROC, &rbvals, sizeof (BorderValues));*/
271 
272     }
273 
274   gimp_drawable_detach (drawable);
275 }
276 
277 static void
border(gint32 image_ID)278 border (gint32 image_ID)
279 {
280   GdkPixbuf  *pixbuf = NULL;
281   int         texture_width;
282   int         texture_height;
283   gdouble     margin_top;
284   gdouble     margin_bottom;
285   gdouble     margin_left;
286   gdouble     margin_right;
287 
288   pixbuf = gdk_pixbuf_new_from_inline (-1, bvals.border->texture, FALSE, NULL);
289 
290   if (pixbuf)
291   {
292     texture_width = gdk_pixbuf_get_width (pixbuf);
293     texture_height = gdk_pixbuf_get_height (pixbuf);
294 
295     gint32 texture_image = gimp_image_new (texture_width, texture_height, GIMP_RGB);
296     gint32 texture_layer = gimp_layer_new_from_pixbuf (texture_image, "texture", pixbuf, 100, GIMP_NORMAL_MODE, 0, 0);
297     gimp_image_add_layer (texture_image, texture_layer, -1);
298 
299     gint width = gimp_image_width (image_ID);
300     gint height = gimp_image_height (image_ID);
301 
302     if (bvals.border->top || bvals.border->bottom || bvals.border->left || bvals.border->right)
303     {
304       width += bvals.border->left + bvals.border->right;
305       height += bvals.border->top + bvals.border->bottom;
306       gimp_image_resize (image_ID,
307                          width,
308                          height,
309                          bvals.border->left,
310                          bvals.border->top);
311     }
312 
313     gint32 layer = gimp_layer_new (image_ID, "border",
314                                    width, height,
315                                    GIMP_RGBA_IMAGE,
316                                    100,
317                                    GIMP_NORMAL_MODE);
318     gimp_image_add_layer (image_ID, layer, -1);
319 
320     if (width > texture_width - bvals.border->length)
321     {
322       margin_left = ((gdouble) texture_width - bvals.border->left - bvals.border->right - bvals.border->length) / 2;
323       margin_right = margin_left;
324     }
325     else
326     {
327       margin_left = ((gdouble) width - bvals.border->left - bvals.border->right) / 2;
328       margin_right = margin_left;
329     }
330     margin_left += bvals.border->left;
331     margin_right += bvals.border->right;
332 
333     if (height > texture_height - bvals.border->length)
334     {
335       margin_top = ((gdouble) texture_height - bvals.border->top - bvals.border->bottom - bvals.border->length) / 2;
336       margin_bottom = margin_top;
337     }
338     else
339     {
340       margin_top = ((gdouble) height - bvals.border->top - bvals.border->bottom) / 2;
341       margin_bottom = margin_top;
342     }
343     margin_top += bvals.border->top;
344     margin_bottom += bvals.border->bottom;
345 
346     /* fix gimp_context_set_pattern ("Clipboard") only works on English versions of Gimp */
347     //gimp_context_set_pattern ("Clipboard");
348     INIT_I18N ();
349     gimp_context_set_pattern (_("Clipboard"));
350 
351     if (width > margin_left + margin_right) {
352       /* top */
353       gimp_rect_select (texture_image,
354                         margin_left,
355                         0,
356                         (gdouble) texture_width - margin_left - margin_right,
357                         margin_top,
358                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
359       gimp_edit_copy (texture_layer);
360       gimp_rect_select (image_ID,
361                         margin_left,
362                         0,
363                         (gdouble) width - margin_left - margin_right,
364                         margin_top,
365                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
366       gimp_edit_fill (layer, GIMP_PATTERN_FILL);
367 
368       /* bottom */
369       gimp_rect_select (texture_image,
370                         margin_left,
371                         (gdouble) texture_height - margin_bottom,
372                         (gdouble) texture_width - margin_left - margin_right,
373                         margin_bottom,
374                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
375       gimp_edit_copy (texture_layer);
376       gimp_rect_select (image_ID,
377                         margin_left,
378                         (gdouble) height - margin_bottom,
379                         (gdouble) width - margin_left - margin_right,
380                         margin_bottom,
381                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
382       gimp_edit_fill (layer, GIMP_PATTERN_FILL);
383     }
384 
385     if (height > margin_top + margin_bottom) {
386       /* left */
387       gimp_rect_select (texture_image,
388                         0,
389                         margin_top,
390                         margin_left,
391                         (gdouble) texture_height - margin_top - margin_bottom,
392                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
393       gimp_edit_copy (texture_layer);
394       gimp_rect_select (image_ID,
395                         0,
396                         margin_top,
397                         margin_left,
398                         (gdouble) height - margin_top - margin_bottom,
399                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
400       gimp_edit_fill (layer, GIMP_PATTERN_FILL);
401 
402       /* right */
403       gimp_rect_select (texture_image,
404                         (gdouble) texture_width - margin_right,
405                         margin_top,
406                         margin_right,
407                         (gdouble) texture_height - margin_top - margin_bottom,
408                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
409       gimp_edit_copy (texture_layer);
410       gimp_rect_select (image_ID,
411                         (gdouble) width - margin_right,
412                         margin_top,
413                         margin_right,
414                         (gdouble) height - margin_top - margin_bottom,
415                         GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
416       gimp_edit_fill (layer, GIMP_PATTERN_FILL);
417     }
418 
419     /* top left */
420     gimp_rect_select (texture_image,
421                       0,
422                       0,
423                       margin_left,
424                       margin_top,
425                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
426     gimp_edit_copy (texture_layer);
427     gimp_rect_select (image_ID,
428                       0,
429                       0,
430                       margin_left,
431                       margin_top,
432                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
433     gimp_edit_fill (layer, GIMP_PATTERN_FILL);
434 
435     /* top right */
436     gimp_rect_select (texture_image,
437                       (gdouble) texture_width - margin_right,
438                       0,
439                       margin_right,
440                       margin_top,
441                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
442     gimp_edit_copy (texture_layer);
443     gimp_rect_select (image_ID,
444                       (gdouble) width - margin_right,
445                       0,
446                       margin_right,
447                       margin_top,
448                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
449     gimp_edit_fill (layer, GIMP_PATTERN_FILL);
450 
451     /* bottom left */
452     gimp_rect_select (texture_image,
453                       0,
454                       (gdouble) texture_height - margin_bottom,
455                       margin_left,
456                       margin_bottom,
457                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
458     gimp_edit_copy (texture_layer);
459     gimp_rect_select (image_ID,
460                       0,
461                       (gdouble) height - margin_bottom,
462                       margin_left,
463                       margin_bottom,
464                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
465     gimp_edit_fill (layer, GIMP_PATTERN_FILL);
466 
467     /* bottom right */
468     gimp_rect_select (texture_image,
469                       (gdouble) texture_width - margin_right,
470                       (gdouble) texture_height - margin_bottom,
471                       margin_right,
472                       margin_bottom,
473                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
474     gimp_edit_copy (texture_layer);
475     gimp_rect_select (image_ID,
476                       (gdouble) width - margin_right,
477                       (gdouble) height - margin_bottom,
478                       margin_right,
479                       margin_bottom,
480                       GIMP_CHANNEL_OP_REPLACE, FALSE, 0);
481     gimp_edit_fill (layer, GIMP_PATTERN_FILL);
482 
483     gimp_image_merge_down(image_ID, layer, GIMP_CLIP_TO_IMAGE);
484 
485     gimp_selection_none (image_ID);
486   }
487 }
488 
489 static void
preview_update(GtkWidget * preview)490 preview_update (GtkWidget *preview)
491 {
492   gint preview_size = PREVIEW_SIZE;
493   gint max_size = height;
494   if (height < width)
495     max_size = width;
496   if (preview_size > max_size)
497     preview_size = max_size;
498   GdkPixbuf *pixbuf = gimp_image_get_thumbnail (preview_image, preview_size, preview_size, GIMP_PIXBUF_SMALL_CHECKS);
499   gtk_image_set_from_pixbuf (GTK_IMAGE(preview), pixbuf);
500 }
501 
502 static gboolean
border_dialog(gint32 image_ID,GimpDrawable * drawable)503 border_dialog (gint32        image_ID,
504                GimpDrawable *drawable)
505 {
506   GtkWidget *dialog;
507   GtkWidget *main_hbox;
508   GtkWidget *middle_vbox;
509   GtkWidget *right_vbox;
510   GtkWidget *label;
511 
512   gboolean   run;
513 
514   gimp_ui_init (PLUG_IN_BINARY, FALSE);
515 
516   dialog = gimp_dialog_new ("Simple Border", PLUG_IN_ROLE,
517                             NULL, 0,
518                             gimp_standard_help_func, PLUG_IN_PROC,
519 
520                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
521                             GTK_STOCK_OK,     GTK_RESPONSE_OK,
522 
523                             NULL);
524 
525   gimp_window_set_transient (GTK_WINDOW (dialog));
526 
527   gtk_widget_show (dialog);
528 
529   main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
530   gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
531   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
532                       main_hbox, TRUE, TRUE, 0);
533   gtk_widget_show (main_hbox);
534 
535   middle_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
536   gtk_container_set_border_width (GTK_CONTAINER (middle_vbox), 12);
537   gtk_box_pack_start (GTK_BOX (main_hbox), middle_vbox, TRUE, TRUE, 0);
538   gtk_widget_show (middle_vbox);
539 
540   right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
541   gtk_container_set_border_width (GTK_CONTAINER (right_vbox), 12);
542   gtk_widget_set_size_request (right_vbox, 320, -1);
543   gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, TRUE, TRUE, 0);
544   gtk_widget_show (right_vbox);
545 
546   /* preview */
547   label = gtk_label_new ("Preview");
548   gtk_box_pack_start (GTK_BOX (middle_vbox), label, FALSE, FALSE, 0);
549   gtk_widget_show (label);
550 
551   preview_image = gimp_image_duplicate(image_ID);
552 
553   preview = gtk_image_new();
554   preview_update (preview);
555 
556   gtk_box_pack_start (GTK_BOX (middle_vbox), preview, TRUE, TRUE, 0);
557   gtk_widget_show (preview);
558 
559   /* textures */
560   label = gtk_label_new ("Textures");
561   gtk_box_pack_start (GTK_BOX (right_vbox), label, FALSE, FALSE, 0);
562   gtk_widget_show (label);
563 
564   GtkWidget *notebook = gtk_notebook_new ();
565   gtk_box_pack_start (GTK_BOX (right_vbox), notebook, FALSE, FALSE, 0);
566   gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
567   gtk_widget_show (notebook);
568 
569   create_texture_page (GTK_NOTEBOOK (notebook), "Top", textures, G_N_ELEMENTS (textures));
570 
571   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
572 
573   gtk_widget_destroy (dialog);
574 
575   return run;
576 }
577 
578 static void
create_texture_page(GtkNotebook * notebook,const gchar * category,const Border * textures,guint n_textures)579 create_texture_page (GtkNotebook *notebook, const gchar* category, const Border* textures, guint n_textures) {
580   GtkWidget *label = gtk_label_new (category);
581 
582   GtkWidget *thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
583   gtk_container_set_border_width (GTK_CONTAINER (thispage), 0);
584   gtk_widget_set_size_request (thispage, -1, 480);
585   gtk_widget_show (thispage);
586 
587   /* scrolled window */
588   GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
589   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
590   gtk_box_pack_start (GTK_BOX (thispage), scrolled_window, TRUE, TRUE, 0);
591   gtk_widget_show (scrolled_window);
592 
593   /* table */
594   gint rows = 5;
595   gint cols = 3;
596   GtkWidget *table = gtk_table_new (rows, cols, FALSE);
597   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
598   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
599   //gtk_box_pack_start (GTK_BOX (thispage), table, FALSE, FALSE, 0);
600   gtk_container_set_border_width (GTK_CONTAINER (table), 10);
601   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), table);
602   gtk_widget_show (table);
603 
604   gint row = 1;
605   gint col = 1;
606 
607   gint i;
608   for (i = 0; i < n_textures; i++)
609   {
610     GtkWidget *icon = effect_icon_new (&textures[i]);
611     gtk_table_attach_defaults (GTK_TABLE (table), icon, col - 1, col, row - 1, row);
612     gtk_widget_show (icon);
613 
614     col++;
615     if (col > cols)
616     {
617       row++;
618       col = 1;
619     }
620   }
621 
622   gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
623 }
624 
625 static gboolean
create_custom_texture_pages(GtkNotebook * notebook,const gchar * texture_path)626 create_custom_texture_pages (GtkNotebook *notebook, const gchar *texture_path)
627 {
628   gboolean has_custom_texture = FALSE;
629 
630   const gchar *gimp_dir = gimp_directory ();
631   const gchar *texture_dir = g_build_filename (gimp_dir, texture_path, NULL);
632   GDir *dir = g_dir_open (texture_dir, 0, NULL);
633   if (dir)
634   {
635     const gchar *dir_ent;
636     while (dir_ent = g_dir_read_name (dir))
637     {
638       if (is_hidden (dir_ent))
639         continue;
640 
641       gchar *filename = g_build_filename (texture_dir, dir_ent, NULL);
642       if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
643         create_custom_texture_page (GTK_NOTEBOOK (notebook), dir_ent, filename);
644         has_custom_texture = TRUE;
645       }
646     }
647   }
648 
649   return has_custom_texture;
650 }
651 
652 static void
create_custom_texture_page(GtkNotebook * notebook,const gchar * category,const gchar * path)653 create_custom_texture_page (GtkNotebook *notebook, const gchar* category, const gchar* path) {
654   GtkWidget *label = gtk_label_new (category);
655 
656   GtkWidget *thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
657   gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
658   gtk_widget_show (thispage);
659 
660   gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
661 }
662 
663 static GtkWidget *
effect_icon_new(const Border * border)664 effect_icon_new (const Border *border)
665 {
666   GtkWidget *box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 2);
667 
668   /* image */
669   GdkPixbuf *pixbuf = gdk_pixbuf_new_from_inline (-1, border->texture, FALSE, NULL);
670   pixbuf = gdk_pixbuf_scale_simple (pixbuf, THUMBNAIL_SIZE, THUMBNAIL_SIZE, GDK_INTERP_BILINEAR);
671   GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
672   GtkWidget *event_box = gtk_event_box_new ();
673   gtk_container_add (GTK_CONTAINER (event_box), image);
674   gtk_widget_show (image);
675   gtk_box_pack_start (GTK_BOX (box), event_box, FALSE, FALSE, 0);
676   gtk_widget_show (event_box);
677 
678   g_signal_connect (event_box, "button_press_event", G_CALLBACK (texture_press), (gpointer)border);
679 
680   /* label */
681   GtkWidget *label = gtk_label_new (border->id);
682   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
683   gtk_widget_show (label);
684 
685   return box;
686 }
687 
688 static void
textures_switch_page(GtkNotebook * notebook,GtkWidget * page,guint page_num,const gchar * texture_path)689 textures_switch_page (GtkNotebook *notebook, GtkWidget *page, guint page_num, const gchar *texture_path)
690 {
691   if (page_num == 0 || g_array_index (textures_timestamps, time_t, page_num) > 0)
692     return;
693 
694   // fix gtk2
695   page = gtk_notebook_get_nth_page (notebook, page_num);
696 
697   gtk_container_set_border_width (GTK_CONTAINER (page), 0);
698   gtk_widget_set_size_request (page, -1, 480);
699 
700   const gchar *category = gtk_notebook_get_tab_label_text(notebook, page);
701 
702   /* scrolled window */
703   GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
704   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
705   gtk_box_pack_start (GTK_BOX (page), scrolled_window, TRUE, TRUE, 0);
706   gtk_widget_show (scrolled_window);
707 
708   /* table */
709   gint rows = 5;
710   gint cols = 3;
711   GtkWidget *table = gtk_table_new (rows, cols, FALSE);
712   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
713   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
714   gtk_container_set_border_width (GTK_CONTAINER (table), 10);
715   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), table);
716   gtk_widget_show (table);
717 
718   gint row = 1;
719   gint col = 1;
720 
721   const gchar *gimp_dir = gimp_directory ();
722   const gchar *texture_dir = g_build_filename (gimp_dir, texture_path, NULL);
723   const gchar *path = g_build_filename (texture_dir, category, NULL);
724 
725   GDir *dir = g_dir_open (path, 0, NULL);
726   if (dir)
727   {
728     const gchar *dir_ent;
729     while (dir_ent = g_dir_read_name (dir))
730     {
731       if (is_hidden (dir_ent))
732         continue;
733 
734       gchar *filename = g_build_filename (path, dir_ent, NULL);
735       GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
736       pixbuf = gdk_pixbuf_scale_simple (pixbuf, THUMBNAIL_SIZE, THUMBNAIL_SIZE, GDK_INTERP_BILINEAR);
737       GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
738       GtkWidget *event_box = gtk_event_box_new ();
739       gtk_container_add (GTK_CONTAINER (event_box), image);
740       gtk_widget_show (image);
741       gtk_table_attach_defaults (GTK_TABLE (table), event_box, col - 1, col, row - 1, row);
742       gtk_widget_show (event_box);
743 
744       col++;
745       if (col > cols)
746       {
747         row++;
748         col = 1;
749       }
750 
751       g_signal_connect (event_box, "button_press_event", G_CALLBACK (custom_texture_press), filename);
752     }
753   }
754 
755   g_array_index (textures_timestamps, time_t, page_num) = time (NULL);
756 }
757 
758 static gboolean
texture_press(GtkWidget * event_box,GdkEventButton * event,const Border * texture)759 texture_press (GtkWidget *event_box, GdkEventButton *event, const Border* texture)
760 {
761   bvals.border = texture;
762   bvals.custom_texture = NULL;
763 
764   do_texture_press ();
765 }
766 
767 static gboolean
custom_texture_press(GtkWidget * event_box,GdkEventButton * event,const gchar * texture)768 custom_texture_press (GtkWidget *event_box, GdkEventButton *event, const gchar *texture)
769 {
770   bvals.border = NULL;
771   bvals.custom_texture = texture;
772 
773   do_texture_press ();
774 }
775 
776 static void
do_texture_press()777 do_texture_press ()
778 {
779   gimp_image_delete (preview_image);
780   preview_image = gimp_image_duplicate(image_ID);
781 
782   border (preview_image);
783 
784   preview_update (preview);
785 }
786 
787