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 #include "border-textures.h"
22 
23 #define RIP_BORDER_PROC     "plug-in-rip-border"
24 #define TEXTURE_BORDER_PROC "plug-in-texture-border"
25 #define PLUG_IN_BINARY "border"
26 #define PLUG_IN_ROLE   "gimp-border"
27 
28 #define RIP_BORDER_TEXTURE_PATH     "rip-border"
29 #define TEXTURE_BORDER_TEXTURE_PATH "texture-border"
30 
31 #define PREVIEW_SIZE  480
32 #define THUMBNAIL_SIZE  80
33 
34 typedef struct
35 {
36   const guint8* texture;
37   GimpRGB  color;
38   gdouble  opacity;
39   const gchar *custom_texture;
40 } BorderValues;
41 
42 static void     query    (void);
43 static void     run      (const gchar      *name,
44                           gint              nparams,
45                           const GimpParam  *param,
46                           gint             *nreturn_vals,
47                           GimpParam       **return_vals);
48 
49 static void     border (gint32     image_ID);
50 
51 static gboolean border_dialog (const gchar *title, const gchar *help_id, const guint8** textures, guint n_textures, const gchar *texture_path);
52 
53 static void     create_texture_page (GtkNotebook *notebook, const gchar *category, const guint8** textures, guint n_textures);
54 static gboolean create_custom_texture_pages (GtkNotebook *notebook, const gchar *texture_path);
55 static void     create_custom_texture_page (GtkNotebook *notebook, const gchar *category, const gchar *path);
56 static void     textures_switch_page (GtkNotebook *notebook, GtkWidget *page, guint page_num, const gchar *texture_path);
57 
58 static gboolean texture_press (GtkWidget *event_box, GdkEventButton *event, const guint8 *texture);
59 static gboolean custom_texture_press (GtkWidget *event_box, GdkEventButton *event, const gchar *texture);
60 static void     do_texture_press ();
61 
62 static inline gboolean
is_hidden(const gchar * filename)63 is_hidden (const gchar *filename)
64 {
65   /* skip files starting with '.' so we don't try to parse
66    * stuff like .DS_Store or other metadata storage files
67    */
68   return (filename[0] == '.');
69 }
70 
71 const GimpPlugInInfo PLUG_IN_INFO =
72 {
73   NULL,  /* init_proc  */
74   NULL,  /* quit_proc  */
75   query, /* query_proc */
76   run,   /* run_proc   */
77 };
78 
79 static BorderValues bvals =
80 {
81   NULL,                 /* texture */
82   {1.0, 1.0, 1.0, 1.0}, /* color */
83   100,                  /* opacity */
84   NULL,                 /* custom_texture */
85 };
86 
87 static gint32     image_ID         = 0;
88 static gint       width;
89 static gint       height;
90 
91 static GtkWidget *preview          = NULL;
92 static gint32     preview_image    = 0;
93 static gint32     color_layer      = 0;
94 static gint32     texture_mask     = 0;
95 
96 static const guint8* rip_border_textures[] =
97 {
98   texture_14911,
99   texture_12847,
100   texture_201301,
101   texture_201185,
102   texture_111601,
103   texture_201072,
104   texture_15614,
105   texture_201106,
106   texture_12858,
107   texture_200836,
108   texture_15618,
109   texture_200773,
110   texture_200623,
111   texture_200549,
112   texture_200442,
113 };
114 
115 static const guint8* texture_border_textures[] =
116 {
117   texture_12854,
118   texture_200542,
119   texture_15315,
120   texture_13039,
121   texture_201367,
122   texture_15311,
123   texture_200835,
124   texture_201122,
125   texture_114549,
126   texture_200650,
127   texture_15319,
128   texture_200622,
129   texture_200763,
130   texture_200285,
131   texture_200543,
132 };
133 
134 static GArray *textures_timestamps = NULL;
135 
136 /* compatable with gtk2 */
137 #if GTK_MAJOR_VERSION < 3
138 GtkWidget *
gtk_box_new(GtkOrientation orientation,gint spacing)139 gtk_box_new (GtkOrientation  orientation,
140              gint            spacing)
141 {
142   if (orientation == GTK_ORIENTATION_HORIZONTAL)
143     return gtk_hbox_new (FALSE, spacing);
144   else
145     return gtk_vbox_new (FALSE, spacing);
146 }
147 #endif
148 
MAIN()149 MAIN ()
150 
151 static void
152 query (void)
153 {
154   static const GimpParamDef args[] =
155   {
156     { GIMP_PDB_INT32,    "run-mode",   "The run mode { RUN-INTERACTIVE (0), RUN-NONINTERACTIVE (1) }" },
157     { GIMP_PDB_IMAGE,    "image",      "Input image" },
158     { GIMP_PDB_DRAWABLE, "drawable",   "Input drawable" },
159   };
160 
161   gimp_install_procedure (RIP_BORDER_PROC,
162                           "Create an rip border effect",
163                           "Rip border is one of the beautify serial plugin.",
164                           "Hejian <hejian.he@gmail.com>",
165                           "Hejian <hejian.he@gmail.com>",
166                           "2012",
167                           "_Rip Border...",
168                           "RGB*, GRAY*",
169                           GIMP_PLUGIN,
170                           G_N_ELEMENTS (args), 0,
171                           args, NULL);
172 
173   gimp_install_procedure (TEXTURE_BORDER_PROC,
174                           "Create an texture border effect",
175                           "Texture border is one of the beautify serial plugin.",
176                           "Hejian <hejian.he@gmail.com>",
177                           "Hejian <hejian.he@gmail.com>",
178                           "2012",
179                           "_Texture Border...",
180                           "RGB*, GRAY*",
181                           GIMP_PLUGIN,
182                           G_N_ELEMENTS (args), 0,
183                           args, NULL);
184 
185   gimp_plugin_menu_register (RIP_BORDER_PROC,     "<Image>/Filters/Beautify/Border");
186   gimp_plugin_menu_register (TEXTURE_BORDER_PROC, "<Image>/Filters/Beautify/Border");
187 }
188 
189 static void
run(const gchar * name,gint nparams,const GimpParam * param,gint * nreturn_vals,GimpParam ** return_vals)190 run (const gchar      *name,
191      gint              nparams,
192      const GimpParam  *param,
193      gint             *nreturn_vals,
194      GimpParam       **return_vals)
195 {
196   static GimpParam   values[2];
197   GimpDrawable      *drawable;
198   GimpRunMode        run_mode;
199   GimpPDBStatusType  status = GIMP_PDB_SUCCESS;
200 
201   run_mode = param[0].data.d_int32;
202 
203   *nreturn_vals = 1;
204   *return_vals  = values;
205 
206   values[0].type          = GIMP_PDB_STATUS;
207   values[0].data.d_status = status;
208 
209   image_ID = param[1].data.d_image;
210   drawable = gimp_drawable_get (param[2].data.d_drawable);
211 
212   width = gimp_image_width (image_ID);
213   height = gimp_image_height (image_ID);
214 
215   if (strcmp (name, RIP_BORDER_PROC) == 0)
216   {
217     switch (run_mode)
218     {
219       case GIMP_RUN_INTERACTIVE:
220         if (! border_dialog ("Rip Border", RIP_BORDER_PROC, rip_border_textures, G_N_ELEMENTS (rip_border_textures), RIP_BORDER_TEXTURE_PATH))
221           return;
222         break;
223 
224       case GIMP_RUN_NONINTERACTIVE:
225         break;
226 
227       case GIMP_RUN_WITH_LAST_VALS:
228         break;
229 
230       default:
231         break;
232     }
233   }
234   else if (strcmp (name, TEXTURE_BORDER_PROC) == 0)
235   {
236     switch (run_mode)
237     {
238       case GIMP_RUN_INTERACTIVE:
239         if (! border_dialog ("Texture Border", TEXTURE_BORDER_PROC, texture_border_textures, G_N_ELEMENTS (texture_border_textures), TEXTURE_BORDER_TEXTURE_PATH))
240           return;
241         break;
242 
243       case GIMP_RUN_NONINTERACTIVE:
244         break;
245 
246       case GIMP_RUN_WITH_LAST_VALS:
247         break;
248 
249       default:
250         break;
251     }
252   }
253 
254   if ((status == GIMP_PDB_SUCCESS) &&
255       (gimp_drawable_is_rgb(drawable->drawable_id) ||
256        gimp_drawable_is_gray(drawable->drawable_id)))
257     {
258       /* Run! */
259       gimp_image_undo_group_start (image_ID);
260       border (image_ID);
261       gimp_image_undo_group_end (image_ID);
262 
263       /* If run mode is interactive, flush displays */
264       if (run_mode != GIMP_RUN_NONINTERACTIVE)
265         gimp_displays_flush ();
266 
267       /* Store data */
268       /*if (run_mode == GIMP_RUN_INTERACTIVE)
269         gimp_set_data (PLUG_IN_PROC, &rbvals, sizeof (BorderValues));*/
270 
271     }
272 
273   gimp_drawable_detach (drawable);
274 }
275 
276 static void
border(gint32 image_ID)277 border (gint32 image_ID)
278 {
279   GdkPixbuf *pixbuf = NULL;
280 
281   if (bvals.texture)
282     pixbuf = gdk_pixbuf_new_from_inline (-1, bvals.texture, FALSE, NULL);
283   else if (bvals.custom_texture)
284     pixbuf = gdk_pixbuf_new_from_file (bvals.custom_texture, NULL);
285 
286   if (pixbuf)
287   {
288     gint32 color_layer = gimp_layer_new (image_ID, "color",
289                                          width, height,
290                                          GIMP_RGBA_IMAGE,
291                                          bvals.opacity,
292                                          GIMP_NORMAL_MODE);
293     gimp_image_add_layer (image_ID, color_layer, -1);
294 
295     gimp_context_set_foreground (&bvals.color);
296     gimp_edit_fill (color_layer, GIMP_FOREGROUND_FILL);
297 
298     gint32 texture_mask = gimp_layer_create_mask (color_layer,
299                                                   GIMP_ADD_WHITE_MASK);
300     gimp_layer_add_mask (color_layer, texture_mask);
301 
302     gint32 texture = gimp_layer_new_from_pixbuf (image_ID, "texture", pixbuf, bvals.opacity, GIMP_NORMAL_MODE, 0, 0);
303     gimp_image_add_layer (image_ID, texture, -1);
304     gimp_layer_scale (texture, width, height, FALSE);
305     gimp_invert (texture);
306     gimp_edit_copy (texture);
307     gint32 floating_sel_ID = gimp_edit_paste (texture_mask, TRUE);
308     gimp_image_remove_layer (image_ID, texture);
309 
310     gimp_floating_sel_anchor (floating_sel_ID);
311 
312     gimp_image_merge_down(image_ID, color_layer, GIMP_CLIP_TO_BOTTOM_LAYER);
313   }
314 }
315 
316 static void
preview_update(GtkWidget * preview)317 preview_update (GtkWidget *preview)
318 {
319   gint preview_size = PREVIEW_SIZE;
320   gint max_size = height;
321   if (height < width)
322     max_size = width;
323   if (preview_size > max_size)
324     preview_size = max_size;
325   GdkPixbuf *pixbuf = gimp_image_get_thumbnail (preview_image, preview_size, preview_size, GIMP_PIXBUF_SMALL_CHECKS);
326   gtk_image_set_from_pixbuf (GTK_IMAGE(preview), pixbuf);
327 }
328 
329 static void
color_update(GtkWidget * preview)330 color_update (GtkWidget *preview)
331 {
332   gimp_context_set_foreground (&bvals.color);
333   gimp_edit_fill (color_layer, GIMP_FOREGROUND_FILL);
334 
335   preview_update (preview);
336 }
337 
338 static void
opacity_update(GtkRange * range,gpointer data)339 opacity_update (GtkRange *range, gpointer data) {
340   bvals.opacity = gtk_range_get_value (range);
341   gimp_layer_set_opacity (color_layer, bvals.opacity);
342   preview_update (preview);
343 }
344 
345 static gboolean
border_dialog(const gchar * title,const gchar * help_id,const guint8 ** textures,guint n_textures,const gchar * texture_path)346 border_dialog (const gchar *title, const gchar *help_id, const guint8** textures, guint n_textures, const gchar *texture_path)
347 {
348   GtkWidget *dialog;
349   GtkWidget *main_hbox;
350   GtkWidget *left_vbox;
351   GtkWidget *middle_vbox;
352   GtkWidget *right_vbox;
353   GtkWidget *label;
354 
355   gboolean   run;
356 
357   gimp_ui_init (PLUG_IN_BINARY, FALSE);
358 
359   dialog = gimp_dialog_new (title, PLUG_IN_ROLE,
360                             NULL, 0,
361                             gimp_standard_help_func, help_id,
362 
363                             GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
364                             GTK_STOCK_OK,     GTK_RESPONSE_OK,
365 
366                             NULL);
367 
368   gimp_window_set_transient (GTK_WINDOW (dialog));
369 
370   gtk_widget_show (dialog);
371 
372   main_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
373   gtk_container_set_border_width (GTK_CONTAINER (main_hbox), 12);
374   gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))),
375                       main_hbox, TRUE, TRUE, 0);
376   gtk_widget_show (main_hbox);
377 
378   left_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
379   gtk_container_set_border_width (GTK_CONTAINER (left_vbox), 12);
380   gtk_box_pack_start (GTK_BOX (main_hbox), left_vbox, TRUE, TRUE, 0);
381   gtk_widget_show (left_vbox);
382 
383   middle_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
384   gtk_container_set_border_width (GTK_CONTAINER (middle_vbox), 12);
385   gtk_box_pack_start (GTK_BOX (main_hbox), middle_vbox, TRUE, TRUE, 0);
386   gtk_widget_show (middle_vbox);
387 
388   right_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
389   gtk_container_set_border_width (GTK_CONTAINER (right_vbox), 12);
390   gtk_widget_set_size_request (right_vbox, 320, -1);
391   gtk_box_pack_start (GTK_BOX (main_hbox), right_vbox, TRUE, TRUE, 0);
392   gtk_widget_show (right_vbox);
393 
394   /* color select */
395   label = gtk_label_new ("Border color");
396   gtk_box_pack_start (GTK_BOX (left_vbox), label, FALSE, FALSE, 0);
397   gtk_widget_show (label);
398 
399   GtkWidget *button = gimp_color_button_new ("Border color", 40, 20, &bvals.color, GIMP_COLOR_AREA_FLAT);
400   gimp_color_button_set_update (GIMP_COLOR_BUTTON (button), TRUE);
401   gtk_box_pack_start (GTK_BOX (left_vbox), button, FALSE, FALSE, 0);
402   gtk_widget_show (button);
403 
404   g_signal_connect (button, "color-changed",
405                    G_CALLBACK (gimp_color_button_get_color),
406                    &bvals.color);
407 
408   /* opacity */
409   label = gtk_label_new ("Border opacity");
410   gtk_box_pack_start (GTK_BOX (left_vbox), label, FALSE, FALSE, 0);
411   gtk_widget_show (label);
412 
413   GtkWidget *hscale = gtk_hscale_new_with_range (0, 100, 1);
414   gtk_range_set_value (GTK_RANGE (hscale), bvals.opacity);
415   gtk_scale_set_value_pos (GTK_SCALE (hscale), GTK_POS_BOTTOM);
416   gtk_box_pack_start (GTK_BOX (left_vbox), hscale, FALSE, FALSE, 0);
417   gtk_widget_show (hscale);
418 
419   g_signal_connect (hscale, "value-changed",
420                    G_CALLBACK (opacity_update),
421                    NULL);
422 
423   /* preview */
424   label = gtk_label_new ("Preview");
425   gtk_box_pack_start (GTK_BOX (middle_vbox), label, FALSE, FALSE, 0);
426   gtk_widget_show (label);
427 
428   preview_image = gimp_image_duplicate(image_ID);
429   color_layer = gimp_layer_new (preview_image, "color",
430                                          width, height,
431                                          GIMP_RGBA_IMAGE,
432                                          bvals.opacity,
433                                          GIMP_NORMAL_MODE);
434   gimp_image_add_layer (preview_image, color_layer, -1);
435 
436   gimp_context_set_foreground (&bvals.color);
437   gimp_edit_fill (color_layer, GIMP_FOREGROUND_FILL);
438 
439   texture_mask = gimp_layer_create_mask (color_layer,
440                                          GIMP_ADD_BLACK_MASK);
441   gimp_layer_add_mask (color_layer, texture_mask);
442 
443   preview = gtk_image_new();
444   preview_update (preview);
445 
446   gtk_box_pack_start (GTK_BOX (middle_vbox), preview, TRUE, TRUE, 0);
447   gtk_widget_show (preview);
448 
449   g_signal_connect_swapped (button, "color-changed",
450                            G_CALLBACK (color_update),
451                            preview);
452 
453   /* textures */
454   label = gtk_label_new ("Textures");
455   gtk_box_pack_start (GTK_BOX (right_vbox), label, FALSE, FALSE, 0);
456   gtk_widget_show (label);
457 
458   GtkWidget *notebook = gtk_notebook_new ();
459   gtk_box_pack_start (GTK_BOX (right_vbox), notebook, FALSE, FALSE, 0);
460   gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE);
461   gtk_widget_show (notebook);
462 
463   create_texture_page (GTK_NOTEBOOK (notebook), "Top", textures, n_textures);
464 
465   if (create_custom_texture_pages (GTK_NOTEBOOK (notebook), texture_path))
466   {
467     textures_timestamps = g_array_new (FALSE, TRUE, sizeof (time_t));
468     g_array_set_size (textures_timestamps, gtk_notebook_get_n_pages (GTK_NOTEBOOK (notebook)));
469     g_signal_connect (notebook, "switch-page", G_CALLBACK (textures_switch_page), (gpointer) texture_path);
470   }
471   else
472   {
473     label = gtk_label_new ("You can download more textures at https://github.com/hejiann/beautify/wiki/Textures-Download");
474     gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
475     gtk_box_pack_start (GTK_BOX (right_vbox), label, FALSE, FALSE, 0);
476     gtk_widget_show (label);
477   }
478 
479   run = (gimp_dialog_run (GIMP_DIALOG (dialog)) == GTK_RESPONSE_OK);
480 
481   gtk_widget_destroy (dialog);
482 
483   return run;
484 }
485 
486 static void
create_texture_page(GtkNotebook * notebook,const gchar * category,const guint8 ** textures,guint n_textures)487 create_texture_page (GtkNotebook *notebook, const gchar* category, const guint8** textures, guint n_textures) {
488   GtkWidget *label = gtk_label_new (category);
489 
490   GtkWidget *thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
491   gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
492   gtk_widget_show (thispage);
493 
494   /* table */
495   gint rows = 5;
496   gint cols = 3;
497   GtkWidget *table = gtk_table_new (rows, cols, FALSE);
498   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
499   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
500   gtk_box_pack_start (GTK_BOX (thispage), table, FALSE, FALSE, 0);
501   gtk_widget_show (table);
502 
503   gint row = 1;
504   gint col = 1;
505 
506   gint i;
507   for (i = 0; i < n_textures; i++)
508   {
509     GdkPixbuf *pixbuf = gdk_pixbuf_new_from_inline (-1, textures[i], FALSE, NULL);
510     pixbuf = gdk_pixbuf_scale_simple (pixbuf, THUMBNAIL_SIZE, THUMBNAIL_SIZE, GDK_INTERP_BILINEAR);
511     GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
512     GtkWidget *event_box = gtk_event_box_new ();
513     gtk_container_add (GTK_CONTAINER (event_box), image);
514     gtk_widget_show (image);
515     gtk_table_attach_defaults (GTK_TABLE (table), event_box, col - 1, col, row - 1, row);
516     gtk_widget_show (event_box);
517 
518     col++;
519     if (col > cols)
520     {
521       row++;
522       col = 1;
523     }
524 
525     g_signal_connect (event_box, "button_press_event", G_CALLBACK (texture_press), (gpointer)textures[i]);
526   }
527 
528   gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
529 }
530 
531 static gboolean
create_custom_texture_pages(GtkNotebook * notebook,const gchar * texture_path)532 create_custom_texture_pages (GtkNotebook *notebook, const gchar *texture_path)
533 {
534   gboolean has_custom_texture = FALSE;
535 
536   const gchar *gimp_dir = gimp_directory ();
537   const gchar *texture_dir = g_build_filename (gimp_dir, texture_path, NULL);
538   GDir *dir = g_dir_open (texture_dir, 0, NULL);
539   if (dir)
540   {
541     const gchar *dir_ent;
542     while (dir_ent = g_dir_read_name (dir))
543     {
544       if (is_hidden (dir_ent))
545         continue;
546 
547       gchar *filename = g_build_filename (texture_dir, dir_ent, NULL);
548       if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
549         create_custom_texture_page (GTK_NOTEBOOK (notebook), dir_ent, filename);
550         has_custom_texture = TRUE;
551       }
552     }
553   }
554 
555   return has_custom_texture;
556 }
557 
558 static void
create_custom_texture_page(GtkNotebook * notebook,const gchar * category,const gchar * path)559 create_custom_texture_page (GtkNotebook *notebook, const gchar* category, const gchar* path) {
560   GtkWidget *label = gtk_label_new (category);
561 
562   GtkWidget *thispage = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
563   gtk_container_set_border_width (GTK_CONTAINER (thispage), 12);
564   gtk_widget_show (thispage);
565 
566   gtk_notebook_append_page_menu (notebook, thispage, label, NULL);
567 }
568 
569 static void
textures_switch_page(GtkNotebook * notebook,GtkWidget * page,guint page_num,const gchar * texture_path)570 textures_switch_page (GtkNotebook *notebook, GtkWidget *page, guint page_num, const gchar *texture_path)
571 {
572   if (page_num == 0 || g_array_index (textures_timestamps, time_t, page_num) > 0)
573     return;
574 
575   // fix gtk2
576   page = gtk_notebook_get_nth_page (notebook, page_num);
577 
578   gtk_container_set_border_width (GTK_CONTAINER (page), 0);
579   gtk_widget_set_size_request (page, -1, 480);
580 
581   const gchar *category = gtk_notebook_get_tab_label_text(notebook, page);
582 
583   /* scrolled window */
584   GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
585   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
586   gtk_box_pack_start (GTK_BOX (page), scrolled_window, TRUE, TRUE, 0);
587   gtk_widget_show (scrolled_window);
588 
589   /* table */
590   gint rows = 5;
591   gint cols = 3;
592   GtkWidget *table = gtk_table_new (rows, cols, FALSE);
593   gtk_table_set_col_spacings (GTK_TABLE (table), 6);
594   gtk_table_set_row_spacings (GTK_TABLE (table), 6);
595   gtk_container_set_border_width (GTK_CONTAINER (table), 10);
596   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), table);
597   gtk_widget_show (table);
598 
599   gint row = 1;
600   gint col = 1;
601 
602   const gchar *gimp_dir = gimp_directory ();
603   const gchar *texture_dir = g_build_filename (gimp_dir, texture_path, NULL);
604   const gchar *path = g_build_filename (texture_dir, category, NULL);
605 
606   GDir *dir = g_dir_open (path, 0, NULL);
607   if (dir)
608   {
609     const gchar *dir_ent;
610     while (dir_ent = g_dir_read_name (dir))
611     {
612       if (is_hidden (dir_ent))
613         continue;
614 
615       gchar *filename = g_build_filename (path, dir_ent, NULL);
616       GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, NULL);
617       pixbuf = gdk_pixbuf_scale_simple (pixbuf, THUMBNAIL_SIZE, THUMBNAIL_SIZE, GDK_INTERP_BILINEAR);
618       GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
619       GtkWidget *event_box = gtk_event_box_new ();
620       gtk_container_add (GTK_CONTAINER (event_box), image);
621       gtk_widget_show (image);
622       gtk_table_attach_defaults (GTK_TABLE (table), event_box, col - 1, col, row - 1, row);
623       gtk_widget_show (event_box);
624 
625       col++;
626       if (col > cols)
627       {
628         row++;
629         col = 1;
630       }
631 
632       g_signal_connect (event_box, "button_press_event", G_CALLBACK (custom_texture_press), filename);
633     }
634   }
635 
636   g_array_index (textures_timestamps, time_t, page_num) = time (NULL);
637 }
638 
639 static gboolean
texture_press(GtkWidget * event_box,GdkEventButton * event,const guint8 * texture)640 texture_press (GtkWidget *event_box, GdkEventButton *event, const guint8* texture)
641 {
642   bvals.texture = texture;
643   bvals.custom_texture = NULL;
644 
645   do_texture_press ();
646 }
647 
648 static gboolean
custom_texture_press(GtkWidget * event_box,GdkEventButton * event,const gchar * texture)649 custom_texture_press (GtkWidget *event_box, GdkEventButton *event, const gchar *texture)
650 {
651   bvals.texture = NULL;
652   bvals.custom_texture = texture;
653 
654   do_texture_press ();
655 }
656 
657 static void
do_texture_press()658 do_texture_press ()
659 {
660   GdkPixbuf *pixbuf;
661   if (bvals.texture)
662     pixbuf = gdk_pixbuf_new_from_inline (-1, bvals.texture, FALSE, NULL);
663   else
664     pixbuf = gdk_pixbuf_new_from_file (bvals.custom_texture, NULL);
665 
666   gint32 texture_layer = gimp_layer_new_from_pixbuf (preview_image,
667                                               "texture",
668                                               pixbuf,
669                                               bvals.opacity,
670                                               GIMP_NORMAL_MODE, 0, 0);
671   gimp_image_add_layer (preview_image, texture_layer, -1);
672   gimp_layer_scale (texture_layer, width, height, FALSE);
673   gimp_invert (texture_layer);
674   gimp_edit_copy (texture_layer);
675   gimp_edit_paste (texture_mask, TRUE);
676   gimp_image_remove_layer (preview_image, texture_layer);
677 
678   preview_update (preview);
679 }
680 
681