home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / app / pattern_select.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-12-17  |  28.5 KB  |  1,065 lines

  1. /* The GIMP -- an 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 2 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, write to the Free Software
  16.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17.  */
  18. #include "config.h"
  19.  
  20. #include <string.h>
  21.  
  22. #include <gtk/gtk.h>
  23.  
  24. #include "apptypes.h"
  25.  
  26. #include "appenv.h"
  27. #include "dialog_handler.h"
  28. #include "gimpcontext.h"
  29. #include "gimpdnd.h"
  30. #include "gimpui.h"
  31. #include "patterns.h"
  32. #include "pattern_select.h"
  33. #include "session.h"
  34.  
  35. #include "config.h"
  36. #include "libgimp/gimpintl.h"
  37.  
  38. #define MIN_CELL_SIZE       32
  39. #define MAX_CELL_SIZE       45
  40.  
  41. #define STD_PATTERN_COLUMNS 6
  42. #define STD_PATTERN_ROWS    5 
  43.  
  44. /* how long to wait after mouse-down before showing pattern popup */
  45. #define POPUP_DELAY_MS      150
  46.  
  47. #define MAX_WIN_WIDTH(psp)     (MIN_CELL_SIZE * (psp)->NUM_PATTERN_COLUMNS)
  48. #define MAX_WIN_HEIGHT(psp)    (MIN_CELL_SIZE * (psp)->NUM_PATTERN_ROWS)
  49. #define MARGIN_WIDTH      1
  50. #define MARGIN_HEIGHT     1
  51.  
  52. #define PATTERN_EVENT_MASK GDK_BUTTON1_MOTION_MASK | \
  53.                            GDK_EXPOSURE_MASK | \
  54.                            GDK_BUTTON_PRESS_MASK | \
  55.                GDK_ENTER_NOTIFY_MASK
  56.  
  57. /*  local function prototypes  */
  58. static void     pattern_change_callbacks        (PatternSelect *psp,
  59.                          gboolean       closing);
  60.  
  61. static GPattern * pattern_select_drag_pattern   (GtkWidget     *widget,
  62.                          gpointer       data);
  63. static void     pattern_select_drop_pattern     (GtkWidget     *widget,
  64.                          GPattern      *pattern,
  65.                          gpointer       data);
  66. static void     pattern_select_pattern_changed  (GimpContext   *context,
  67.                          GPattern      *pattern,
  68.                          PatternSelect *psp);
  69. static void     pattern_select_select           (PatternSelect *psp,
  70.                          GPattern      *pattern);
  71.  
  72. static gboolean pattern_popup_timeout           (gpointer       data);
  73. static void     pattern_popup_open              (PatternSelect *psp,
  74.                          gint, gint, GPattern *);
  75. static void     pattern_popup_close             (PatternSelect *);
  76.  
  77. static void     display_setup                   (PatternSelect *);
  78. static void     display_pattern                 (PatternSelect *, GPattern *,
  79.                          gint, gint);
  80. static void     display_patterns                (PatternSelect *);
  81.  
  82. static void     pattern_select_show_selected    (PatternSelect *, gint, gint);
  83. static void     update_active_pattern_field     (PatternSelect *);
  84. static void     preview_calc_scrollbar          (PatternSelect *);
  85.  
  86. static gint     pattern_select_resize           (GtkWidget *, GdkEvent *,
  87.                          PatternSelect *);
  88. static gint     pattern_select_events           (GtkWidget *, GdkEvent *,
  89.                          PatternSelect *);
  90.  
  91. static void     pattern_select_scroll_update    (GtkAdjustment *, gpointer);
  92.  
  93. static void     pattern_select_close_callback   (GtkWidget *, gpointer);
  94. static void     pattern_select_refresh_callback (GtkWidget *, gpointer);
  95.  
  96. /*  dnd stuff  */
  97. static GtkTargetEntry preview_target_table[] =
  98. {
  99.   GIMP_TARGET_PATTERN
  100. };
  101. static guint preview_n_targets = (sizeof (preview_target_table) /
  102.                                   sizeof (preview_target_table[0]));
  103.  
  104. /*  The main pattern selection dialog  */
  105. PatternSelect   *pattern_select_dialog = NULL;
  106.  
  107. /*  local variables  */
  108.  
  109. /*  List of active dialogs  */
  110. GSList *pattern_active_dialogs = NULL;
  111.  
  112. void
  113. pattern_dialog_create (void)
  114. {
  115.   if (! pattern_select_dialog)
  116.     {
  117.       pattern_select_dialog = pattern_select_new (NULL, NULL);
  118.     }
  119.   else
  120.     {
  121.       if (!GTK_WIDGET_VISIBLE (pattern_select_dialog->shell))
  122.     gtk_widget_show (pattern_select_dialog->shell);
  123.       else
  124.     gdk_window_raise (pattern_select_dialog->shell->window);
  125.     }
  126. }
  127.  
  128. void
  129. pattern_dialog_free ()
  130. {
  131.   if (pattern_select_dialog)
  132.     {
  133.       session_get_window_info (pattern_select_dialog->shell,
  134.                    &pattern_select_session_info);
  135.  
  136.       pattern_select_free (pattern_select_dialog);
  137.       pattern_select_dialog = NULL;
  138.     }
  139. }
  140.  
  141. /*  If title == NULL then it is the main pattern dialog  */
  142. PatternSelect *
  143. pattern_select_new (gchar *title,
  144.             gchar *initial_pattern)
  145. {
  146.   PatternSelect *psp;
  147.   GtkWidget *vbox;
  148.   GtkWidget *hbox;
  149.   GtkWidget *frame;
  150.   GtkWidget *sbar;
  151.   GtkWidget *label_box;
  152.  
  153.   GPattern *active = NULL;
  154.  
  155.   static gboolean first_call = TRUE;
  156.  
  157.   psp = g_new (PatternSelect, 1);
  158.   psp->preview             = NULL;
  159.   psp->callback_name       = NULL;
  160.   psp->dnd_pattern         = NULL;
  161.   psp->pattern_popup       = NULL;
  162.   psp->popup_timeout_tag   = 0;
  163.   psp->old_col             = 0;
  164.   psp->old_row             = 0;
  165.   psp->NUM_PATTERN_COLUMNS = STD_PATTERN_COLUMNS;
  166.   psp->NUM_PATTERN_ROWS    = STD_PATTERN_COLUMNS;
  167.  
  168.   /*  The shell  */
  169.   psp->shell =     gimp_dialog_new (title ? title : _("Pattern Selection"),
  170.                  "pattern_selection",
  171.                  gimp_standard_help_func,
  172.                  "dialogs/pattern_selection.html",
  173.                  title ? GTK_WIN_POS_MOUSE : GTK_WIN_POS_NONE,
  174.                  FALSE, TRUE, FALSE,
  175.  
  176.                  _("Refresh"), pattern_select_refresh_callback,
  177.                  psp, NULL, NULL, FALSE, FALSE,
  178.                  _("Close"), pattern_select_close_callback,
  179.                  psp, NULL, NULL, TRUE, TRUE,
  180.  
  181.                  NULL);
  182.  
  183.   if (title)
  184.     {
  185.       psp->context = gimp_context_new (title, NULL);
  186.     }
  187.   else
  188.     {
  189.       psp->context = gimp_context_get_user ();
  190.  
  191.       session_set_window_geometry (psp->shell, &pattern_select_session_info,
  192.                    TRUE);
  193.       dialog_register (psp->shell);
  194.     }
  195.  
  196.   if (no_data && first_call)
  197.     patterns_init (FALSE);
  198.  
  199.   first_call = FALSE;
  200.  
  201.   if (title && initial_pattern && strlen (initial_pattern))
  202.     {
  203.       active = pattern_list_get_pattern (pattern_list, initial_pattern);
  204.     }
  205.   else
  206.     {
  207.       active = gimp_context_get_pattern (gimp_context_get_user ());
  208.     }
  209.  
  210.   if (!active)
  211.     {
  212.       active = gimp_context_get_pattern (gimp_context_get_standard ());
  213.     }
  214.  
  215.   if (title)
  216.     {
  217.       gimp_context_set_pattern (psp->context, active);
  218.     }
  219.  
  220.   /*  The main vbox  */
  221.   vbox = gtk_vbox_new (FALSE, 0);
  222.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 2);
  223.   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (psp->shell)->vbox), vbox);
  224.  
  225.   /*  Options box  */
  226.   psp->options_box = gtk_vbox_new (FALSE, 0);
  227.   gtk_box_pack_start (GTK_BOX (vbox), psp->options_box, FALSE, FALSE, 0);
  228.  
  229.   /*  Create the active pattern label  */
  230.   label_box = gtk_hbox_new (FALSE, 0);
  231.   gtk_box_pack_start (GTK_BOX (psp->options_box), label_box, FALSE, FALSE, 2);
  232.  
  233.   psp->pattern_name = gtk_label_new (_("No Patterns available"));
  234.   gtk_box_pack_start (GTK_BOX (label_box), psp->pattern_name, FALSE, FALSE, 4);
  235.   psp->pattern_size = gtk_label_new ("(0 X 0)");
  236.   gtk_box_pack_start (GTK_BOX (label_box), psp->pattern_size, FALSE, FALSE, 2);
  237.  
  238.   gtk_widget_show (psp->pattern_name);
  239.   gtk_widget_show (psp->pattern_size);
  240.   gtk_widget_show (label_box);
  241.  
  242.   /*  The horizontal box containing preview & scrollbar  */
  243.   hbox = gtk_hbox_new (FALSE, 2);
  244.   gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
  245.  
  246.   frame = gtk_frame_new (NULL);
  247.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
  248.   gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
  249.  
  250.   psp->sbar_data =
  251.     GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, MAX_WIN_HEIGHT(psp),
  252.                     1, 1, MAX_WIN_HEIGHT(psp)));
  253.   sbar = gtk_vscrollbar_new (psp->sbar_data);
  254.   gtk_signal_connect (GTK_OBJECT (psp->sbar_data), "value_changed",
  255.               GTK_SIGNAL_FUNC (pattern_select_scroll_update),
  256.               psp);
  257.   gtk_box_pack_start (GTK_BOX (hbox), sbar, FALSE, FALSE, 0);
  258.  
  259.   /*  Create the pattern preview window and the underlying image  */
  260.  
  261.   /*  Get the initial pattern extents  */
  262.   psp->cell_width  = MIN_CELL_SIZE;
  263.   psp->cell_height = MIN_CELL_SIZE;
  264.  
  265.   psp->preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  266.   gtk_preview_size (GTK_PREVIEW (psp->preview),
  267.             MAX_WIN_WIDTH (psp), MAX_WIN_HEIGHT (psp));
  268.   gtk_preview_set_expand (GTK_PREVIEW (psp->preview), TRUE);
  269.   gtk_widget_set_events (psp->preview, PATTERN_EVENT_MASK);
  270.  
  271.   gtk_signal_connect (GTK_OBJECT (psp->preview), "event",
  272.               GTK_SIGNAL_FUNC (pattern_select_events),
  273.               psp);
  274.   gtk_signal_connect (GTK_OBJECT (psp->preview), "size_allocate",
  275.               GTK_SIGNAL_FUNC (pattern_select_resize),
  276.               psp);
  277.  
  278.   /*  dnd stuff  */
  279.   gtk_drag_source_set (psp->preview,
  280.                GDK_BUTTON2_MASK,
  281.                preview_target_table, preview_n_targets,
  282.                GDK_ACTION_COPY);
  283.   gimp_dnd_pattern_source_set (psp->preview, pattern_select_drag_pattern, psp);
  284.  
  285.   gtk_drag_dest_set (psp->preview,
  286.                      GTK_DEST_DEFAULT_ALL,
  287.                      preview_target_table, preview_n_targets,
  288.                      GDK_ACTION_COPY);
  289.   gimp_dnd_pattern_dest_set (psp->preview, pattern_select_drop_pattern, psp);
  290.  
  291.   gtk_container_add (GTK_CONTAINER (frame), psp->preview);
  292.   gtk_widget_show (psp->preview);
  293.  
  294.   gtk_widget_show (sbar);
  295.   gtk_widget_show (hbox);
  296.   gtk_widget_show (frame);
  297.  
  298.   gtk_widget_show (psp->options_box);
  299.   gtk_widget_show (vbox);
  300.   gtk_widget_show (psp->shell);
  301.  
  302.   preview_calc_scrollbar (psp);
  303.  
  304.   gtk_signal_connect (GTK_OBJECT (psp->context), "pattern_changed",
  305.               GTK_SIGNAL_FUNC (pattern_select_pattern_changed),
  306.               psp);
  307.  
  308.   if (active)
  309.     pattern_select_select (psp, active);
  310.  
  311.   /*  Add to active pattern dialogs list  */
  312.   pattern_active_dialogs = g_slist_append (pattern_active_dialogs, psp);
  313.  
  314.   return psp;
  315. }
  316.  
  317. void
  318. pattern_select_free (PatternSelect *psp)
  319. {
  320.   if (!psp)
  321.     return;
  322.  
  323.   /* remove from active list */
  324.   pattern_active_dialogs = g_slist_remove (pattern_active_dialogs, psp);
  325.  
  326.   gtk_signal_disconnect_by_data (GTK_OBJECT (psp->context), psp);
  327.  
  328.   if (psp->pattern_popup != NULL)
  329.     gtk_widget_destroy (psp->pattern_popup);
  330.  
  331.   if (psp->popup_timeout_tag != 0)
  332.     gtk_timeout_remove (psp->popup_timeout_tag);
  333.  
  334.   if (psp->callback_name)
  335.     {
  336.       g_free (psp->callback_name);
  337.       gtk_object_unref (GTK_OBJECT (psp->context));
  338.     }
  339.  
  340.   g_free (psp);
  341. }
  342.  
  343. /*  Call this dialog's PDB callback  */
  344.  
  345. static void
  346. pattern_change_callbacks (PatternSelect *psp,
  347.               gboolean      closing)
  348. {
  349.   gchar *name;
  350.   ProcRecord *prec = NULL;
  351.   GPattern *pattern;
  352.   gint nreturn_vals;
  353.   static gboolean busy = FALSE;
  354.  
  355.   /* Any procs registered to callback? */
  356.   Argument *return_vals; 
  357.  
  358.   if (!psp || !psp->callback_name || busy)
  359.     return;
  360.  
  361.   busy = TRUE;
  362.   name = psp->callback_name;
  363.   pattern = gimp_context_get_pattern (psp->context);
  364.  
  365.   /* If its still registered run it */
  366.   prec = procedural_db_lookup (name);
  367.  
  368.   if (prec && pattern)
  369.     {
  370.       return_vals =
  371.     procedural_db_run_proc (name,
  372.                 &nreturn_vals,
  373.                 PDB_STRING,    pattern->name,
  374.                 PDB_INT32,     pattern->mask->width,
  375.                 PDB_INT32,     pattern->mask->height,
  376.                 PDB_INT32,     pattern->mask->bytes,
  377.                 PDB_INT32,     pattern->mask->bytes*pattern->mask->height*pattern->mask->width,
  378.                 PDB_INT8ARRAY, temp_buf_data (pattern->mask),
  379.                 PDB_INT32,     (gint) closing,
  380.                 PDB_END);
  381.  
  382.       if (!return_vals || return_vals[0].value.pdb_int != PDB_SUCCESS)
  383.     g_warning ("failed to run pattern callback function");
  384.       
  385.       procedural_db_destroy_args (return_vals, nreturn_vals);
  386.     }
  387.   busy = FALSE;
  388. }
  389.  
  390. /*  Close active dialogs that no longer have PDB registered for them  */
  391.  
  392. void
  393. patterns_check_dialogs (void)
  394. {
  395.   PatternSelect *psp;
  396.   GSList *list;
  397.   gchar *name;
  398.   ProcRecord *prec = NULL;
  399.  
  400.   list = pattern_active_dialogs;
  401.  
  402.   while (list)
  403.     {
  404.       psp = (PatternSelect *) list->data;
  405.       list = g_slist_next (list);
  406.  
  407.       name = psp->callback_name;
  408.  
  409.       if (name)
  410.     {
  411.       prec = procedural_db_lookup (name);
  412.  
  413.       if (!prec)
  414.         {
  415.           /*  Can alter pattern_active_dialogs list  */
  416.           pattern_select_close_callback (NULL, psp); 
  417.         }
  418.     }
  419.     }
  420. }
  421.  
  422. /*
  423.  *  Local functions
  424.  */
  425.  
  426. static GPattern *
  427. pattern_select_drag_pattern (GtkWidget *widget,
  428.                  gpointer   data)
  429. {
  430.   PatternSelect *psp;
  431.  
  432.   psp = (PatternSelect *) data;
  433.  
  434.   return psp->dnd_pattern;
  435. }
  436.  
  437. static void
  438. pattern_select_drop_pattern (GtkWidget *widget,
  439.                  GPattern  *pattern,
  440.                  gpointer   data)
  441. {
  442.   PatternSelect *psp;
  443.  
  444.   psp = (PatternSelect *) data;
  445.  
  446.   gimp_context_set_pattern (psp->context, pattern);
  447. }
  448.  
  449. static void
  450. pattern_select_pattern_changed (GimpContext   *context,
  451.                 GPattern      *pattern,
  452.                 PatternSelect *psp)
  453. {
  454.   if (pattern)
  455.     {
  456.       pattern_select_select (psp, pattern);
  457.  
  458.       if (psp->callback_name)
  459.     pattern_change_callbacks (psp, FALSE);
  460.     }
  461. }
  462.  
  463. static void
  464. pattern_select_select (PatternSelect *psp,
  465.                GPattern      *pattern)
  466. {
  467.   gint index;
  468.   gint row, col;
  469.   gint scroll_offset = 0;
  470.  
  471.   index = pattern->index;
  472.  
  473.   if (index < 0)
  474.     return;
  475.  
  476.   update_active_pattern_field (psp);
  477.  
  478.   row = index / psp->NUM_PATTERN_COLUMNS;
  479.   col = index - row * psp->NUM_PATTERN_COLUMNS;
  480.  
  481.   /*  check if the new active pattern is already in the preview  */
  482.   if (((row + 1) * psp->cell_height) >
  483.       (psp->preview->allocation.height + psp->scroll_offset))
  484.     {
  485.       scroll_offset = (((row + 1) * psp->cell_height) -
  486.                        (psp->scroll_offset + psp->preview->allocation.height));
  487.     }
  488.   else if ((row * psp->cell_height) < psp->scroll_offset)
  489.     {
  490.       scroll_offset = (row * psp->cell_height) - psp->scroll_offset;
  491.     }
  492.   else
  493.     {
  494.       pattern_select_show_selected (psp, row, col);
  495.     }
  496.  
  497.   gtk_adjustment_set_value (psp->sbar_data, psp->scroll_offset + scroll_offset);
  498. }
  499.  
  500. typedef struct
  501. {
  502.   PatternSelect *psp;
  503.   int            x;
  504.   int            y;
  505.   GPattern      *pattern;
  506. } popup_timeout_args_t;
  507.  
  508.  
  509. static gboolean
  510. pattern_popup_timeout (gpointer data)
  511. {
  512.   popup_timeout_args_t *args = data;
  513.   PatternSelect *psp = args->psp;
  514.   GPattern *pattern = args->pattern;
  515.   gint x, y;
  516.   gint x_org, y_org;
  517.   gint scr_w, scr_h;
  518.   gchar *src, *buf;
  519.  
  520.   /* timeout has gone off so our tag is now invalid  */
  521.   psp->popup_timeout_tag = 0;
  522.  
  523.   /* make sure the popup exists and is not visible */
  524.   if (psp->pattern_popup == NULL)
  525.     {
  526.       GtkWidget *frame;
  527.       psp->pattern_popup = gtk_window_new (GTK_WINDOW_POPUP);
  528.       gtk_window_set_policy (GTK_WINDOW (psp->pattern_popup),
  529.                  FALSE, FALSE, TRUE);
  530.       frame = gtk_frame_new (NULL);
  531.       gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_OUT);
  532.       gtk_container_add (GTK_CONTAINER (psp->pattern_popup), frame);
  533.       gtk_widget_show (frame);
  534.       psp->pattern_preview = gtk_preview_new (GTK_PREVIEW_COLOR);
  535.       gtk_container_add (GTK_CONTAINER (frame), psp->pattern_preview);
  536.       gtk_widget_show (psp->pattern_preview);
  537.     }
  538.   else
  539.     {
  540.       gtk_widget_hide (psp->pattern_popup);
  541.     }
  542.  
  543.   /* decide where to put the popup */
  544.   gdk_window_get_origin (psp->preview->window, &x_org, &y_org);
  545.   scr_w = gdk_screen_width ();
  546.   scr_h = gdk_screen_height ();
  547.   x = x_org + args->x - pattern->mask->width * 0.5;
  548.   y = y_org + args->y - pattern->mask->height * 0.5;
  549.   x = (x < 0) ? 0 : x;
  550.   y = (y < 0) ? 0 : y;
  551.   x = (x + pattern->mask->width > scr_w) ? scr_w - pattern->mask->width : x;
  552.   y = (y + pattern->mask->height > scr_h) ? scr_h - pattern->mask->height : y;
  553.   gtk_preview_size (GTK_PREVIEW (psp->pattern_preview),
  554.             pattern->mask->width, pattern->mask->height);
  555.   gtk_widget_popup (psp->pattern_popup, x, y);
  556.  
  557.   /*  Draw the pattern  */
  558.   buf = g_new (gchar, pattern->mask->width * 3);
  559.   src = (gchar *)temp_buf_data (pattern->mask);
  560.   for (y = 0; y < pattern->mask->height; y++)
  561.     {
  562.       if (pattern->mask->bytes == 1)
  563.     for (x = 0; x < pattern->mask->width; x++)
  564.       {
  565.         buf[x*3+0] = src[x];
  566.         buf[x*3+1] = src[x];
  567.         buf[x*3+2] = src[x];
  568.       }
  569.       else
  570.     for (x = 0; x < pattern->mask->width; x++)
  571.       {
  572.         buf[x*3+0] = src[x*3+0];
  573.         buf[x*3+1] = src[x*3+1];
  574.         buf[x*3+2] = src[x*3+2];
  575.       }
  576.       gtk_preview_draw_row (GTK_PREVIEW (psp->pattern_preview), (guchar *)buf, 0, y, pattern->mask->width);
  577.       src += pattern->mask->width * pattern->mask->bytes;
  578.     }
  579.   g_free (buf);
  580.  
  581.   /*  Draw the pattern preview  */
  582.   gtk_widget_draw (psp->pattern_preview, NULL);
  583.  
  584.   return FALSE;  /* don't repeat */
  585. }
  586.  
  587. static void
  588. pattern_popup_open (PatternSelect *psp,
  589.             gint           x,
  590.             gint           y,
  591.             GPattern      *pattern)
  592. {
  593.   static popup_timeout_args_t popup_timeout_args;
  594.  
  595.   /* if we've already got a timeout scheduled, then we complain */
  596.   g_return_if_fail (psp->popup_timeout_tag == 0);
  597.  
  598.   popup_timeout_args.psp = psp;
  599.   popup_timeout_args.x = x;
  600.   popup_timeout_args.y = y;
  601.   popup_timeout_args.pattern = pattern;
  602.   psp->popup_timeout_tag = gtk_timeout_add (POPUP_DELAY_MS,
  603.                         pattern_popup_timeout,
  604.                         &popup_timeout_args);
  605. }
  606.  
  607. static void
  608. pattern_popup_close (PatternSelect *psp)
  609. {
  610.   if (psp->popup_timeout_tag != 0)
  611.     gtk_timeout_remove (psp->popup_timeout_tag);
  612.   psp->popup_timeout_tag = 0;
  613.  
  614.   if (psp->pattern_popup != NULL)
  615.     gtk_widget_hide (psp->pattern_popup);
  616. }
  617.  
  618. static void
  619. display_setup (PatternSelect *psp)
  620. {
  621.   guchar * buf;
  622.   gint i;
  623.  
  624.   buf = g_new (guchar, 3 * psp->preview->allocation.width);
  625.  
  626.   /*  Set the buffer to white  */
  627.   memset (buf, 255, psp->preview->allocation.width * 3);
  628.  
  629.   /*  Set the image buffer to white  */
  630.   for (i = 0; i < psp->preview->allocation.height; i++)
  631.     gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf, 0, i,
  632.               psp->preview->allocation.width);
  633.  
  634.   g_free (buf);
  635. }
  636.  
  637. static void
  638. display_pattern (PatternSelect *psp,
  639.          GPattern      *pattern,
  640.          gint           col,
  641.          gint           row)
  642. {
  643.   TempBuf * pattern_buf;
  644.   guchar * src, *s;
  645.   guchar * buf, *b;
  646.   gint cell_width, cell_height;
  647.   gint width, height;
  648.   gint rowstride;
  649.   gint offset_x, offset_y;
  650.   gint yend;
  651.   gint ystart;
  652.   gint i, j;
  653.  
  654.   buf = g_new (guchar, psp->cell_width * 3);
  655.  
  656.   pattern_buf = pattern->mask;
  657.  
  658.   /*  calculate the offset into the image  */
  659.   cell_width = psp->cell_width - 2*MARGIN_WIDTH;
  660.   cell_height = psp->cell_height - 2*MARGIN_HEIGHT;
  661.   width = (pattern_buf->width > cell_width) ? cell_width :
  662.     pattern_buf->width;
  663.   height = (pattern_buf->height > cell_height) ? cell_height :
  664.     pattern_buf->height;
  665.  
  666.   offset_x = col * psp->cell_width + ((cell_width - width) >> 1) + MARGIN_WIDTH;
  667.   offset_y = row * psp->cell_height + ((cell_height - height) >> 1)
  668.     - psp->scroll_offset + MARGIN_HEIGHT;
  669.  
  670.   ystart = CLAMP (offset_y, 0, psp->preview->allocation.height);
  671.   yend = CLAMP (offset_y + height, 0, psp->preview->allocation.height);
  672.  
  673.   /*  Get the pointer into the pattern mask data  */
  674.   rowstride = pattern_buf->width * pattern_buf->bytes;
  675.   src = temp_buf_data (pattern_buf) + (ystart - offset_y) * rowstride;
  676.  
  677.   for (i = ystart; i < yend; i++)
  678.     {
  679.       s = src;
  680.       b = buf;
  681.  
  682.       if (pattern_buf->bytes == 1)
  683.     for (j = 0; j < width; j++)
  684.       {
  685.         *b++ = *s;
  686.         *b++ = *s;
  687.         *b++ = *s++;
  688.       }
  689.       else
  690.     for (j = 0; j < width; j++)
  691.       {
  692.         *b++ = *s++;
  693.         *b++ = *s++;
  694.         *b++ = *s++;
  695.       }
  696.  
  697.       gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf, offset_x, i, width);
  698.  
  699.       src += rowstride;
  700.     }
  701.  
  702.   g_free (buf);
  703. }
  704.  
  705. static void
  706. display_patterns (PatternSelect *psp)
  707. {
  708.   GSList *list = pattern_list;    /*  the global pattern list  */
  709.   gint row, col;
  710.   GPattern *pattern;
  711.  
  712.   if (list == NULL)
  713.     {
  714.       gtk_widget_set_sensitive (psp->options_box, FALSE);
  715.       return;
  716.     }
  717.   else
  718.     {
  719.       gtk_widget_set_sensitive (psp->options_box, TRUE);
  720.     }
  721.  
  722.   /*  setup the display area  */
  723.   display_setup (psp);
  724.  
  725.   row = col = 0;
  726.   for (; list; list = g_slist_next (list))
  727.     {
  728.       pattern = (GPattern *) list->data;
  729.  
  730.       /*  Display the pattern  */
  731.       display_pattern (psp, pattern, col, row);
  732.  
  733.       /*  increment the counts  */
  734.       if (++col == psp->NUM_PATTERN_COLUMNS)
  735.     {
  736.       row ++;
  737.       col = 0;
  738.     }
  739.     }
  740. }
  741.  
  742. static void
  743. pattern_select_show_selected (PatternSelect *psp,
  744.                   gint           row,
  745.                   gint           col)
  746. {
  747.   GdkRectangle area;
  748.   guchar * buf;
  749.   gint yend;
  750.   gint ystart;
  751.   gint offset_x, offset_y;
  752.   gint i;
  753.  
  754.   buf = g_new (guchar, 3 * psp->cell_width);
  755.  
  756.   if (psp->old_col != col || psp->old_row != row)
  757.     {
  758.       /*  remove the old selection  */
  759.       offset_x = psp->old_col * psp->cell_width;
  760.       offset_y = psp->old_row * psp->cell_height - psp->scroll_offset;
  761.  
  762.       ystart = CLAMP (offset_y , 0, psp->preview->allocation.height);
  763.       yend = CLAMP (offset_y + psp->cell_height, 0, psp->preview->allocation.height);
  764.  
  765.       /*  set the buf to white  */
  766.       memset (buf, 255, 3 * psp->cell_width);
  767.  
  768.       for (i = ystart; i < yend; i++)
  769.     {
  770.       if (i == offset_y || i == (offset_y + psp->cell_height - 1))
  771.         gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf,
  772.                   offset_x, i, psp->cell_width);
  773.       else
  774.         {
  775.           gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf,
  776.                     offset_x, i, 1);
  777.           gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf,
  778.                     offset_x + psp->cell_width - 1, i, 1);
  779.         }
  780.     }
  781.  
  782.       area.x = offset_x;
  783.       area.y = ystart;
  784.       area.width = psp->cell_width;
  785.       area.height = yend - ystart;
  786.       gtk_widget_draw (psp->preview, &area);
  787.     }
  788.  
  789.   /*  make the new selection  */
  790.   offset_x = col * psp->cell_width;
  791.   offset_y = row * psp->cell_height - psp->scroll_offset;
  792.  
  793.   ystart = CLAMP (offset_y , 0, psp->preview->allocation.height);
  794.   yend = CLAMP (offset_y + psp->cell_height, 0, psp->preview->allocation.height);
  795.  
  796.   /*  set the buf to black  */
  797.   memset (buf, 0, psp->cell_width * 3);
  798.  
  799.   for (i = ystart; i < yend; i++)
  800.     {
  801.       if (i == offset_y || i == (offset_y + psp->cell_height - 1))
  802.     gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf,
  803.                   offset_x, i, psp->cell_width);
  804.       else
  805.     {
  806.       gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf,
  807.                 offset_x, i, 1);
  808.       gtk_preview_draw_row (GTK_PREVIEW (psp->preview), buf,
  809.                 offset_x + psp->cell_width - 1, i, 1);
  810.     }
  811.     }
  812.  
  813.   area.x = offset_x;
  814.   area.y = ystart;
  815.   area.width = psp->cell_width;
  816.   area.height = yend - ystart;
  817.   gtk_widget_draw (psp->preview, &area);
  818.  
  819.   psp->old_row = row;
  820.   psp->old_col = col;
  821.  
  822.   g_free (buf);
  823. }
  824.  
  825. static void
  826. update_active_pattern_field (PatternSelect *psp)
  827. {
  828.   GPattern *pattern;
  829.   gchar buf[32];
  830.  
  831.   pattern = gimp_context_get_pattern (psp->context);
  832.  
  833.   if (!pattern)
  834.     return;
  835.  
  836.   /*  Set pattern name  */
  837.   gtk_label_set_text (GTK_LABEL (psp->pattern_name), pattern->name);
  838.  
  839.   /*  Set pattern size  */
  840.   g_snprintf (buf, sizeof (buf), "(%d X %d)",
  841.           pattern->mask->width, pattern->mask->height);
  842.   gtk_label_set_text (GTK_LABEL (psp->pattern_size), buf);
  843. }
  844.  
  845. static void
  846. preview_calc_scrollbar (PatternSelect *psp)
  847. {
  848.   gint num_rows;
  849.   gint page_size;
  850.   gint max;
  851.  
  852.   psp->scroll_offset = 0;
  853.   num_rows = ((num_patterns + psp->NUM_PATTERN_COLUMNS - 1) /
  854.           psp->NUM_PATTERN_COLUMNS);
  855.   max = num_rows * psp->cell_width;
  856.   if (!num_rows)
  857.     num_rows = 1;
  858.   page_size = psp->preview->allocation.height;
  859.  
  860.   psp->sbar_data->value          = psp->scroll_offset;
  861.   psp->sbar_data->upper          = max;
  862.   psp->sbar_data->page_size      = (page_size < max) ? page_size : max;
  863.   psp->sbar_data->page_increment = (page_size >> 1);
  864.   psp->sbar_data->step_increment = psp->cell_width;
  865.  
  866.   gtk_signal_emit_by_name (GTK_OBJECT (psp->sbar_data), "changed");
  867.   gtk_signal_emit_by_name (GTK_OBJECT (psp->sbar_data), "value_changed");
  868. }
  869.  
  870. static gint
  871. pattern_select_resize (GtkWidget     *widget,
  872.                GdkEvent      *event,
  873.                PatternSelect *psp)
  874. {
  875.   /*  calculate the best-fit approximation...  */  
  876.   gint wid;
  877.   gint now;
  878.   gint cell_size;
  879.  
  880.   wid = widget->allocation.width;
  881.  
  882.   for (now = cell_size = MIN_CELL_SIZE;
  883.        now < MAX_CELL_SIZE; ++now)
  884.     {
  885.       if ((wid % now) < (wid % cell_size)) cell_size = now;
  886.       if ((wid % cell_size) == 0)
  887.         break;
  888.     }
  889.  
  890.   psp->NUM_PATTERN_COLUMNS =
  891.     (gint) (wid / cell_size);
  892.   psp->NUM_PATTERN_ROWS =
  893.     (gint) ((num_patterns + psp->NUM_PATTERN_COLUMNS - 1) /
  894.         psp->NUM_PATTERN_COLUMNS);
  895.  
  896.   psp->cell_width  = cell_size;
  897.   psp->cell_height = cell_size;
  898.  
  899.   /*  recalculate scrollbar extents  */
  900.   preview_calc_scrollbar (psp);
  901.  
  902.   return FALSE;
  903. }
  904.  
  905. static gint
  906. pattern_select_events (GtkWidget     *widget,
  907.                GdkEvent      *event,
  908.                PatternSelect *psp)
  909. {
  910.   GdkEventButton *bevent;
  911.   GPattern *pattern;
  912.   int row, col, index;
  913.  
  914.   switch (event->type)
  915.     {
  916.     case GDK_BUTTON_PRESS:
  917.       bevent = (GdkEventButton *) event;
  918.  
  919.       col = bevent->x / psp->cell_width;
  920.       row = (bevent->y + psp->scroll_offset) / psp->cell_height;
  921.       index = row * psp->NUM_PATTERN_COLUMNS + col;
  922.  
  923.       pattern = pattern_list_get_pattern_by_index (pattern_list, index);
  924.  
  925.       if (pattern)
  926.     psp->dnd_pattern = pattern;
  927.       else
  928.     psp->dnd_pattern = gimp_context_get_pattern (psp->context);
  929.  
  930.       if (bevent->button == 1)
  931.     {
  932.       /*  Get the pattern and display the popup pattern preview  */
  933.       if (pattern)
  934.         {
  935.           gdk_pointer_grab (psp->preview->window, FALSE,
  936.                 (GDK_POINTER_MOTION_HINT_MASK |
  937.                  GDK_BUTTON1_MOTION_MASK |
  938.                  GDK_BUTTON_RELEASE_MASK),
  939.                 NULL, NULL, bevent->time);
  940.  
  941.           /*  Make this pattern the active pattern  */
  942.           gimp_context_set_pattern (psp->context, pattern);
  943.  
  944.           /*  Show the pattern popup window if the pattern is too large  */
  945.           if (pattern->mask->width  > psp->cell_width ||
  946.           pattern->mask->height > psp->cell_height)
  947.         {
  948.           pattern_popup_open (psp, bevent->x, bevent->y, pattern);
  949.         }
  950.         }
  951.     }
  952.  
  953.       /*  wheelmouse support  */
  954.       else if (bevent->button == 4)
  955.     {
  956.       GtkAdjustment *adj = psp->sbar_data;
  957.       gfloat new_value = adj->value - adj->page_increment / 2;
  958.       new_value =
  959.         CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
  960.       gtk_adjustment_set_value (adj, new_value);
  961.     }
  962.       else if (bevent->button == 5)
  963.     {
  964.       GtkAdjustment *adj = psp->sbar_data;
  965.       gfloat new_value = adj->value + adj->page_increment / 2;
  966.       new_value =
  967.         CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
  968.       gtk_adjustment_set_value (adj, new_value);
  969.     }
  970.  
  971.       break;
  972.  
  973.     case GDK_BUTTON_RELEASE:
  974.       bevent = (GdkEventButton *) event;
  975.  
  976.       if (bevent->button == 1)
  977.     {
  978.       /*  Ungrab the pointer  */
  979.       gdk_pointer_ungrab (bevent->time);
  980.  
  981.       /*  Close the brush popup window  */
  982.       pattern_popup_close (psp);
  983.     }
  984.       break;
  985.  
  986.     default:
  987.       break;
  988.     }
  989.  
  990.   return FALSE;
  991. }
  992.  
  993. static void
  994. pattern_select_scroll_update (GtkAdjustment *adjustment,
  995.                   gpointer       data)
  996. {
  997.   PatternSelect *psp;
  998.   GPattern *active;
  999.   gint row, col, index;
  1000.  
  1001.   psp = (PatternSelect *) data;
  1002.  
  1003.   if (psp)
  1004.     {
  1005.       psp->scroll_offset = adjustment->value;
  1006.  
  1007.       display_patterns (psp);
  1008.  
  1009.       active = gimp_context_get_pattern (psp->context);
  1010.  
  1011.       if (active)
  1012.     {
  1013.       index = active->index;
  1014.       if (index < 0)
  1015.         return;
  1016.       row = index / psp->NUM_PATTERN_COLUMNS;
  1017.       col = index - row * psp->NUM_PATTERN_COLUMNS;
  1018.  
  1019.       pattern_select_show_selected (psp, row, col);
  1020.     }
  1021.  
  1022.       gtk_widget_draw (psp->preview, NULL);
  1023.     }
  1024. }
  1025.  
  1026. static void
  1027. pattern_select_close_callback (GtkWidget *widget,
  1028.                    gpointer   data)
  1029. {
  1030.   PatternSelect *psp;
  1031.  
  1032.   psp = (PatternSelect *) data;
  1033.  
  1034.   if (GTK_WIDGET_VISIBLE (psp->shell))
  1035.     gtk_widget_hide (psp->shell);
  1036.  
  1037.   /* Free memory if poping down dialog which is not the main one */
  1038.   if (psp != pattern_select_dialog)
  1039.     {
  1040.       /* Send data back */
  1041.       pattern_change_callbacks (psp, TRUE);
  1042.       gtk_widget_destroy (psp->shell); 
  1043.       pattern_select_free (psp); 
  1044.     }
  1045. }
  1046.  
  1047. static void
  1048. pattern_select_refresh_callback (GtkWidget *widget,
  1049.                  gpointer   data)
  1050. {
  1051.   PatternSelect *psp;
  1052.   GSList *list;
  1053.  
  1054.   /*  re-init the pattern list  */
  1055.   patterns_init (FALSE);
  1056.  
  1057.   for (list = pattern_active_dialogs; list; list = g_slist_next (list))
  1058.     {
  1059.       psp = (PatternSelect *) list->data;
  1060.  
  1061.       /*  recalculate scrollbar extents  */
  1062.       preview_calc_scrollbar (psp);
  1063.     }
  1064. }
  1065.