home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / plug-ins / maze / maze_face.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-24  |  24.1 KB  |  827 lines

  1. /* -*- mode: C; c-file-style: "gnu"; c-basic-offset: 2; -*- */
  2. /* $Id: maze_face.c,v 1.18 2000/08/23 10:23:08 neo Exp $
  3.  * User interface for plug-in-maze.
  4.  * 
  5.  * Implemented as a GIMP 0.99 Plugin by 
  6.  * Kevin Turner <acapnotic@users.sourceforge.net>
  7.  * http://gimp-plug-ins.sourceforge.net/maze/
  8.  */
  9.  
  10. /*
  11.  * This program is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  *
  16.  * This program is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  *
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  *
  25.  */
  26.  
  27. #ifndef SOLO_COMPILE
  28. #include "config.h"
  29. #endif
  30.  
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33.  
  34. #include <libgimp/gimp.h>
  35. #include <libgimp/gimpui.h>
  36.  
  37. #include "maze.h"
  38.  
  39. #include "libgimp/stdplugins-intl.h"
  40.  
  41.  
  42. #define BORDER_TOLERANCE 1.00 /* maximum ratio of (max % divs) to width */
  43. #define ENTRY_WIDTH 75
  44.  
  45. /* entscale stuff begin */
  46. /* FIXME: Entry-Scale stuff is probably in libgimpui by now.  
  47.           Should use that instead.*/
  48. /* Indeed! By using the gimp_scale_entry you could get along without the
  49.    EntscaleIntData structure, since it has accessors to all objects  (Sven) */
  50.  
  51. #define ENTSCALE_INT_SCALE_WIDTH 125
  52. #define ENTSCALE_INT_ENTRY_WIDTH 40
  53.  
  54. #define MORE  1
  55. #define LESS -1
  56.  
  57. typedef void (*EntscaleIntCallbackFunc) (gint value, gpointer data);
  58.  
  59. typedef struct
  60. {
  61.   GtkObject               *adjustment;
  62.   GtkWidget               *entry;
  63.   gint                     constraint;
  64.   EntscaleIntCallbackFunc  callback;
  65.   gpointer               call_data;
  66. } EntscaleIntData;
  67. /* entscale stuff end */
  68.  
  69.  
  70. /* one buffer fits all */
  71. #define BUFSIZE 128  
  72. gchar buffer[BUFSIZE];
  73.  
  74.  
  75. gint        maze_dialog         (void);
  76.  
  77. static void maze_msg            (gchar     *msg);
  78. static void maze_ok_callback    (GtkWidget *widget,
  79.                  gpointer   data);
  80. static void maze_help           (GtkWidget *widget,
  81.                  gpointer   foo);
  82. #ifdef SHOW_PRNG_PRIVATES
  83. static void maze_entry_callback (GtkWidget *widget,
  84.                  gpointer   data);
  85. #endif
  86.  
  87. /* Looking back, it would probably have been easier to completely
  88.  * re-write the whole entry/scale thing to work with the divbox stuff.
  89.  * It would undoubtably be cleaner code.  But since I already *had*
  90.  * the entry/scale routines, I was under the (somewhat mistaken)
  91.  * impression that it would be easier to work with them... */
  92.  
  93. /* Now, it goes like this:
  94.  
  95.    To update entscale (width) when div_entry changes:
  96.  
  97.     entscale_int_new has been slightly modified to return a pointer to
  98.       its entry widget.
  99.  
  100.     This is fed to divbox_new as a "friend", which is in turn fed to
  101.       the div_entry_callback routine.  And that's not really so bad,
  102.       except...
  103.  
  104.     Oh, well, maybe it isn't so bad.  We can play with our friend's
  105.       userdata to block his callbacks so we don't get feedback loops,
  106.       that works nicely enough.
  107.  
  108.    To update div_entry when entscale (width) changes:
  109.  
  110.     The entry/scale setup graciously provides for callbacks.  However,
  111.       this means we need to know about div_entry when we set up
  112.       entry/scale, which we don't...  Chicken and egg problem.  So we
  113.       set up a pointer to where div_entry will be, and pass this
  114.       through to divbox_new when it happens.
  115.  
  116.     We need to block signal handlers for div_entry this time.  We
  117.       happen to know that div_entry's callback data is our old
  118.       "friend", so we pull our friend out from where we stuck him in
  119.       the entry's userdata...  Hopefully that does it.  */
  120.  
  121. /* Questions:
  122.  
  123.      Gosh that was dumb.  Is there a way to
  124.        signal_handler_block_by_name?
  125.      That would make life so much nicer.
  126.  
  127.          You could pass the handler_id around and use 
  128.          gtk_signal_handler_block ().   (Sven)
  129. */
  130.  
  131. static void div_button_callback   (GtkWidget *button, GtkWidget *entry);
  132. static void div_entry_callback    (GtkWidget *entry, GtkWidget *friend);
  133. static void height_width_callback (gint width, GtkWidget **div_entry);
  134.  
  135. static GtkWidget* divbox_new (guint *max,
  136.                   GtkWidget *friend, 
  137.                   GtkWidget **div_entry);
  138.  
  139. #if 0
  140. static void div_buttonl_callback (GtkObject *object);
  141. static void div_buttonr_callback (GtkObject *object);
  142. #endif 
  143.  
  144. /* entscale stuff begin */
  145. static GtkWidget*   entscale_int_new ( GtkWidget *table, gint x, gint y,
  146.                        gchar *caption, gint *intvar, 
  147.                        gint min, gint max, gboolean constraint,
  148.                        EntscaleIntCallbackFunc callback,
  149.                        gpointer data );
  150.  
  151. static void   entscale_int_destroy_callback (GtkWidget     *widget,
  152.                          gpointer       data);
  153. static void   entscale_int_scale_update     (GtkAdjustment *adjustment,
  154.                          gpointer       data);
  155. static void   entscale_int_entry_update     (GtkWidget     *widget,
  156.                          gpointer       data);
  157.  
  158. #define ISODD(X) ((X) & 1)
  159. /* entscale stuff end */
  160.  
  161. extern MazeValues mvals;
  162. extern guint      sel_w, sel_h;
  163.  
  164. static gint       maze_run = FALSE;
  165. static GtkWidget *msg_label;
  166.  
  167. gint
  168. maze_dialog (void) 
  169. {
  170.   GtkWidget *dlg;
  171.   GtkWidget *frame;
  172.   GtkWidget *table;
  173.   GtkWidget *tilecheck;
  174.  
  175.   GtkWidget *width_entry, *height_entry;
  176.   GtkWidget *seed_hbox;
  177.   GtkWidget *div_x_hbox, *div_y_hbox;
  178.   GtkWidget *div_x_entry, *div_y_entry;
  179.  
  180.   gint    trow = 0;
  181.   gchar  *message;
  182.  
  183.   gimp_ui_init ("maze", FALSE);
  184.   gimp_help_init();
  185.  
  186.   dlg = gimp_dialog_new (MAZE_TITLE, "maze", 
  187.              gimp_standard_help_func, "filters/maze.html",
  188.              GTK_WIN_POS_MOUSE,
  189.              FALSE, TRUE, FALSE,
  190.  
  191.              _("OK"), maze_ok_callback,
  192.              NULL, NULL, NULL, TRUE, FALSE,
  193.              _("Help"), maze_help,
  194.              NULL, NULL, NULL, FALSE, FALSE,
  195.              _("Cancel"), gtk_widget_destroy,
  196.              NULL, 1, NULL, FALSE, TRUE,
  197.  
  198.              NULL);
  199.  
  200.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  201.               GTK_SIGNAL_FUNC (gtk_main_quit),
  202.               NULL);
  203.  
  204.   frame = gtk_frame_new (_("Parameter Settings"));
  205.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  206.   gtk_container_set_border_width (GTK_CONTAINER(frame), 6);
  207.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dlg)->vbox), frame, TRUE, TRUE, 0);
  208.   
  209. #ifdef SHOW_PRNG_PRIVATES
  210.   table = gtk_table_new (8, 3, FALSE);
  211. #else
  212.   table = gtk_table_new (6, 3, FALSE);
  213. #endif  
  214.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  215.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  216.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  217.   gtk_container_add (GTK_CONTAINER (frame), table);
  218.  
  219.   /* entscale == Entry and Scale pair function found in pixelize.c */
  220.   width_entry = entscale_int_new (table, 0, trow, _("Width (Pixels):"), 
  221.                   &mvals.width, 
  222.                   1, sel_w/4, TRUE, 
  223.                   (EntscaleIntCallbackFunc) height_width_callback,
  224.                   &div_x_entry);
  225.   trow++;
  226.  
  227.   /* Number of Divisions entry */
  228.   div_x_hbox = divbox_new (&sel_w, width_entry, &div_x_entry);
  229.   g_snprintf (buffer, BUFSIZE, "%d", (sel_w / mvals.width));
  230.   gtk_entry_set_text (GTK_ENTRY (div_x_entry), buffer);
  231.   gimp_table_attach_aligned (GTK_TABLE (table), 0, trow, 
  232.                  _("Pieces:"), 1.0, 0.5,
  233.                  div_x_hbox, 1, FALSE);
  234.   gtk_table_set_row_spacing (GTK_TABLE (table), trow, 8);
  235.   trow++;
  236.  
  237.   height_entry = entscale_int_new (table, 0, trow, _("Height (Pixels):"), 
  238.                    &mvals.height, 
  239.                    1, sel_h/4, TRUE, 
  240.                    (EntscaleIntCallbackFunc) height_width_callback,
  241.                    &div_y_entry);
  242.   trow++;
  243.  
  244.   div_y_hbox = divbox_new (&sel_h, height_entry, &div_y_entry);
  245.   g_snprintf (buffer, BUFSIZE, "%d", (sel_h / mvals.height));
  246.   gtk_entry_set_text (GTK_ENTRY (div_y_entry), buffer);
  247.   gimp_table_attach_aligned (GTK_TABLE (table), 0, trow, 
  248.                  _("Pieces:"), 1.0, 0.5,
  249.                  div_y_hbox, 1, FALSE);
  250.   gtk_table_set_row_spacing (GTK_TABLE (table), trow, 8);
  251.   trow++;
  252.  
  253. #ifdef SHOW_PRNG_PRIVATES
  254.   /* Multiple input box */
  255.   entry = gtk_entry_new ();
  256.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  257.   g_snprintf (buffer, BUFSIZE, "%d", mvals.multiple);
  258.   gtk_entry_set_text (GTK_ENTRY (entry), buffer );
  259.   gimp_table_attach_aligned (GTK_TABLE (table), 0, trow, 
  260.                  _("Multiple (57):"), 1.0, 0.5,
  261.                  entry, 1, FALSE);
  262.   trow++;
  263.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  264.               (GtkSignalFunc) maze_entry_callback,
  265.               &mvals.multiple);
  266.  
  267.   /* Offset input box */
  268.   entry = gtk_entry_new ();
  269.   gtk_widget_set_usize (entry, ENTRY_WIDTH, 0);
  270.   g_snprintf (buffer, BUFSIZE, "%d", mvals.offset);
  271.   gtk_entry_set_text (GTK_ENTRY (entry), buffer );
  272.   gimp_table_attach_aligned (GTK_TABLE (table), 0, trow, 
  273.                  _("Offset (1):"), 1.0, 0.5,
  274.                  entry, 1, FALSE);
  275.   trow++;
  276.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  277.               (GtkSignalFunc) maze_entry_callback,
  278.               &mvals.offset);
  279. #endif
  280.  
  281.   /* Tileable checkbox */
  282.   tilecheck = gtk_check_button_new_with_label (_("Tileable"));
  283.   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (tilecheck), mvals.tile);
  284.   gtk_signal_connect (GTK_OBJECT (tilecheck), "clicked",
  285.               GTK_SIGNAL_FUNC (gimp_toggle_button_update), 
  286.               &mvals.tile);
  287.   gimp_table_attach_aligned (GTK_TABLE (table), 0, trow, 
  288.                  NULL, 1.0, 0.5,
  289.                  tilecheck, 1, FALSE);
  290.   trow++;
  291.  
  292.   /* Seed input box */
  293.   seed_hbox = gimp_random_seed_new (&mvals.seed,
  294.                     &mvals.timeseed, 
  295.                     TRUE, FALSE);
  296.   gimp_table_attach_aligned (GTK_TABLE (table), 0, trow, 
  297.                  _("Seed:"), 1.0, 0.5,
  298.                  seed_hbox, 1, TRUE);
  299.   trow++;
  300.  
  301.   /* Algorithm Choice */
  302.   frame = gimp_radio_group_new (TRUE, _("Style"),
  303.  
  304.                 _("Depth First"), 
  305.                 gimp_radio_button_update,
  306.                 &mvals.algorithm,
  307.                 (gpointer) DEPTH_FIRST,
  308.                 NULL, 
  309.                 (mvals.algorithm == DEPTH_FIRST),
  310.  
  311.                 _("Prim's Algorithm"), 
  312.                 gimp_radio_button_update,
  313.                 &mvals.algorithm,
  314.                 (gpointer) PRIMS_ALGORITHM,
  315.                 NULL, 
  316.                 (mvals.algorithm == PRIMS_ALGORITHM),
  317.  
  318.                 NULL);
  319.  
  320.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  321.   gtk_container_set_border_width (GTK_CONTAINER(frame), 6);
  322.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dlg)->vbox), frame, FALSE, FALSE, 0);
  323.  
  324.   /* Message label */
  325.   frame = gtk_frame_new (NULL);
  326.   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
  327.   gtk_container_set_border_width (GTK_CONTAINER(frame), 6);
  328.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dlg)->vbox), frame, FALSE, FALSE, 0);
  329.   
  330.   message = g_strdup_printf (_("Selection is %dx%d"), sel_w, sel_h);
  331.   msg_label = gtk_label_new (message);
  332.   g_free (message);
  333.   gtk_misc_set_padding (GTK_MISC (msg_label), 4, 4);
  334.   gtk_misc_set_alignment (GTK_MISC (msg_label), 0.5, 0.5);
  335.   gtk_container_add (GTK_CONTAINER (frame), msg_label);
  336.  
  337.   gtk_widget_show_all (dlg);
  338.  
  339.   gtk_main ();
  340.   gdk_flush ();
  341.  
  342.   return maze_run;
  343. }
  344.  
  345. static GtkWidget*
  346. divbox_new (guint      *max,
  347.         GtkWidget  *friend,
  348.         GtkWidget **div_entry)
  349. {
  350.   GtkWidget *align;
  351.   GtkWidget *hbox;
  352.   GtkWidget *arrowl, *arrowr;
  353.   GtkWidget *buttonl, *buttonr;
  354. #if DIVBOX_LOOKS_LIKE_SPINBUTTON
  355.   GtkWidget *buttonbox;
  356. #endif
  357.  
  358.   align = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
  359.   hbox = gtk_hbox_new (FALSE, 0);
  360.   gtk_container_add (GTK_CONTAINER (align), hbox);
  361.  
  362. #if DIVBOX_LOOKS_LIKE_SPINBUTTON     
  363.   arrowl = gtk_arrow_new (GTK_ARROW_DOWN,  GTK_SHADOW_OUT);
  364.   arrowr = gtk_arrow_new (GTK_ARROW_UP, GTK_SHADOW_OUT);
  365. #else
  366.   arrowl = gtk_arrow_new (GTK_ARROW_LEFT,  GTK_SHADOW_IN);
  367.   arrowr = gtk_arrow_new (GTK_ARROW_RIGHT, GTK_SHADOW_IN);
  368. #endif
  369.  
  370.   buttonl = gtk_button_new ();
  371.   buttonr = gtk_button_new ();
  372.      
  373.   gtk_object_set_data (GTK_OBJECT (buttonl), "direction", GINT_TO_POINTER (LESS));
  374.   gtk_object_set_data (GTK_OBJECT (buttonr), "direction", GINT_TO_POINTER (MORE));
  375.  
  376.   *div_entry = gtk_entry_new ();
  377.  
  378.   gtk_object_set_data (GTK_OBJECT (*div_entry), "max", max);
  379.   gtk_object_set_data (GTK_OBJECT (*div_entry), "friend", friend);
  380.  
  381.   gtk_container_add (GTK_CONTAINER (buttonl), arrowl);
  382.   gtk_container_add (GTK_CONTAINER (buttonr), arrowr);
  383.  
  384.   gtk_widget_set_usize (*div_entry, ENTRY_WIDTH, 0);
  385.  
  386. #if DIVBOX_LOOKS_LIKE_SPINBUTTON
  387.   buttonbox = gtk_vbox_new (FALSE, 0);
  388.  
  389.   gtk_box_pack_start (GTK_BOX (buttonbox), buttonr, FALSE, FALSE, 0);
  390.   gtk_box_pack_start (GTK_BOX (buttonbox), buttonl, FALSE, FALSE, 0);
  391.   gtk_widget_show (buttonbox);
  392.  
  393.   gtk_box_pack_start (GTK_BOX (hbox), *div_entry, FALSE, FALSE, 2);
  394.   gtk_box_pack_start (GTK_BOX (hbox), buttonbox, FALSE, FALSE, 0);
  395. #else
  396.   gtk_misc_set_padding (GTK_MISC (arrowl), 2, 2);
  397.   gtk_misc_set_padding (GTK_MISC (arrowr), 2, 2);
  398.  
  399.   gtk_box_pack_start (GTK_BOX (hbox), buttonl, FALSE, FALSE, 0);
  400.   gtk_box_pack_start (GTK_BOX (hbox), *div_entry, FALSE, FALSE, 2);
  401.   gtk_box_pack_start (GTK_BOX (hbox), buttonr, FALSE, FALSE, 0);
  402. #endif     
  403.  
  404.   gtk_widget_show_all (hbox);
  405.  
  406.   gtk_signal_connect (GTK_OBJECT (buttonl), "clicked",
  407.               (GtkSignalFunc) div_button_callback, 
  408.               *div_entry);
  409.   gtk_signal_connect (GTK_OBJECT (buttonr), "clicked",
  410.               (GtkSignalFunc) div_button_callback, 
  411.               *div_entry);
  412.   gtk_signal_connect (GTK_OBJECT (*div_entry), "changed",
  413.               (GtkSignalFunc) div_entry_callback,
  414.               friend);
  415.  
  416.   return align;
  417. }
  418.  
  419. static void
  420. div_button_callback (GtkWidget *button,
  421.              GtkWidget *entry)
  422. {
  423.   guint  max, divs;
  424.   gchar *text;
  425.   gint   direction;
  426.  
  427.   direction = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (button), "direction"));
  428.   max = *((guint*) gtk_object_get_data (GTK_OBJECT (entry), "max"));
  429.  
  430.   /* Tileable mazes shall have only an even number of divisions.
  431.      Other mazes have odd. */
  432.  
  433.   /* Sanity check: */
  434.   if (mvals.tile && ISODD(max))
  435.     {
  436.       maze_msg (_("Selection size is not even.\n"
  437.           "Tileable maze won't work perfectly."));
  438.       return;
  439.     }
  440.  
  441.   text = gtk_entry_get_text (GTK_ENTRY (entry));
  442.  
  443.   divs = atoi (text);
  444.  
  445.   if (divs <= 3)
  446.     {
  447.       divs = mvals.tile ? 
  448.     max - (ISODD(max) ? 1 : 0) : 
  449.     max - (ISODD(max) ? 0 : 1);
  450.     }
  451.   else if (divs > max)
  452.     {
  453.       divs = mvals.tile ? 6 : 5;
  454.     }
  455.      
  456.   /* Makes sure we're appropriately even or odd, adjusting in the
  457.      proper direction. */
  458.  
  459.   divs += direction * (mvals.tile ? (ISODD(divs) ? 1 : 0) :
  460.                                     (ISODD(divs) ? 0 : 1));
  461.       
  462.   if (mvals.tile)
  463.     {
  464.       if (direction > 0)
  465.     {
  466.       do
  467.         {
  468.           divs += 2;
  469.           if (divs > max)
  470.         divs = 4;
  471.         }
  472.       while (max % divs);
  473.     }
  474.       else
  475.     { /* direction < 0 */
  476.       do
  477.         {
  478.           divs -= 2;
  479.           if (divs < 4)
  480.         divs = max - (max & 1);
  481.         }
  482.       while (max % divs);
  483.     } /* endif direction < 0 */
  484.     }
  485.   else
  486.     { /* If not tiling, having a non-zero remainder doesn't bother us much. */
  487.       if (direction > 0)
  488.     {
  489.       do
  490.         {
  491.           divs += 2;
  492.         }
  493.       while ((max % divs > max / divs * BORDER_TOLERANCE) && divs < max);
  494.     }
  495.       else
  496.     { /* direction < 0 */
  497.       do
  498.         {
  499.           divs -= 2;
  500.         }
  501.       while ((max % divs > max / divs * BORDER_TOLERANCE) && divs > 5);
  502.     } /* endif direction < 0 */
  503.     } /* endif not tiling */
  504.  
  505.   if (divs <= 3)
  506.     {
  507.       divs = mvals.tile ? 
  508.     max - (ISODD(max) ? 1 : 0) : 
  509.     max - (ISODD(max) ? 0 : 1);
  510.     }
  511.   else if (divs > max)
  512.     {
  513.       divs = mvals.tile ? 4 : 5;
  514.     }
  515.  
  516.   g_snprintf (buffer, BUFSIZE, "%d", divs);
  517.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  518.  
  519.   return;
  520. }
  521.  
  522. static void
  523. div_entry_callback (GtkWidget *entry,
  524.             GtkWidget *friend)
  525. {
  526.   guint divs, width, max;
  527.   EntscaleIntData *userdata;
  528.   EntscaleIntCallbackFunc friend_callback;
  529.  
  530.   divs = atoi (gtk_entry_get_text (GTK_ENTRY (entry)));
  531.   if (divs < 4) /* If this is under 4 (e.g. 0), something's weird.      */
  532.     return;     /* But it'll probably be ok, so just return and ignore. */
  533.  
  534.   max = *((guint*) gtk_object_get_data (GTK_OBJECT (entry), "max"));     
  535.  
  536.   /* I say "width" here, but it could be height.*/
  537.  
  538.   width = max / divs;
  539.   g_snprintf (buffer, BUFSIZE, "%d", width);
  540.  
  541.   /* No tagbacks from our friend... */
  542.   userdata = gtk_object_get_user_data (GTK_OBJECT (friend));
  543.   friend_callback = userdata->callback;
  544.   userdata->callback = NULL;
  545.  
  546.   gtk_entry_set_text (GTK_ENTRY (friend), buffer);
  547.  
  548.   userdata->callback = friend_callback;
  549. }
  550.  
  551. static void
  552. height_width_callback (gint        width,
  553.                GtkWidget **div_entry)
  554. {
  555.   guint divs, max;
  556.   gpointer data;
  557.  
  558.   max = *((guint*) gtk_object_get_data(GTK_OBJECT(*div_entry), "max"));
  559.   divs = max / width;
  560.  
  561.   g_snprintf (buffer, BUFSIZE, "%d", divs );
  562.  
  563.   data = gtk_object_get_data (GTK_OBJECT(*div_entry), "friend");
  564.   gtk_signal_handler_block_by_data (GTK_OBJECT (*div_entry), data );
  565.      
  566.   gtk_entry_set_text (GTK_ENTRY (*div_entry), buffer);
  567.  
  568.   gtk_signal_handler_unblock_by_data (GTK_OBJECT(*div_entry), data );
  569. }
  570.  
  571. static void
  572. maze_help (GtkWidget *widget,
  573.        gpointer   foo)
  574. {
  575.   gchar           *proc_blurb;
  576.   gchar           *proc_help;
  577.   gchar           *proc_author;
  578.   gchar           *proc_copyright;
  579.   gchar           *proc_date;
  580.   GimpPDBProcType  proc_type;
  581.   gint             nparams;
  582.   gint             nreturn_vals;
  583.   GimpParamDef    *params;
  584.   GimpParamDef    *return_vals;
  585.  
  586.   gint   baz;
  587.   gchar *message;
  588.  
  589.   if (gimp_procedural_db_proc_info ("extension_web_browser",
  590.                     &proc_blurb, 
  591.                     &proc_help, 
  592.                     &proc_author, 
  593.                     &proc_copyright, 
  594.                     &proc_date,
  595.                     &proc_type, 
  596.                     &nparams, &nreturn_vals,
  597.                     ¶ms, &return_vals))
  598.     {
  599.       /* open URL for help */ 
  600.       message = g_strdup_printf (_("Opening %s"), MAZE_URL);
  601.       maze_msg (message);
  602.       g_free (message);
  603.       gimp_run_procedure ("extension_web_browser", &baz,
  604.               GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
  605.               GIMP_PDB_STRING, MAZE_URL,
  606.               GIMP_PDB_INT32, HELP_OPENS_NEW_WINDOW,
  607.               GIMP_PDB_END);
  608.     } 
  609.   else 
  610.     {
  611.       message = g_strdup_printf (_("See %s"), MAZE_URL);
  612.       maze_msg (message);
  613.       g_free (message);
  614.     }                                            
  615. }
  616.  
  617. static void
  618. maze_msg (gchar *msg)
  619. {
  620.   gtk_label_set_text (GTK_LABEL (msg_label), msg);
  621. }
  622.  
  623. static void
  624. maze_ok_callback (GtkWidget *widget,
  625.           gpointer   data)
  626. {
  627.   maze_run = TRUE;
  628.  
  629.   gtk_widget_destroy (GTK_WIDGET (data));
  630. }
  631.  
  632. #ifdef SHOW_PRNG_PRIVATES
  633. static void 
  634. maze_entry_callback (GtkWidget *widget,
  635.              gpointer   data)
  636. {
  637.   gint *text_val;
  638.  
  639.   text_val = (gint *) data;
  640.  
  641.   *text_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
  642. }
  643. #endif
  644.  
  645. /* ==================================================================== */
  646. /* As found in pixelize.c, 
  647.  * hacked to return a pointer to the entry widget. */
  648.  
  649. /*
  650.   Entry and Scale pair 1.03
  651.  
  652.   TODO:
  653.   - Do the proper thing when the user changes value in entry,
  654.   so that callback should not be called when value is actually not changed.
  655.   - Update delay
  656.  */
  657.  
  658. /*
  659.  *  entscale: create new entscale with label. (int)
  660.  *  1 row and 2 cols of table are needed.
  661.  *  Input:
  662.  *    x, y:       starting row and col in table
  663.  *    caption:    label string
  664.  *    intvar:     pointer to variable
  665.  *    min, max:   the boundary of scale
  666.  *    constraint: (bool) true iff the value of *intvar should be constraint
  667.  *                by min and max
  668.  *    callback:      called when the value is actually changed
  669.  *    call_data:  data for callback func
  670.  */
  671. static GtkWidget*
  672. entscale_int_new (GtkWidget *table, 
  673.           gint       x, 
  674.           gint       y,
  675.           gchar     *caption, 
  676.           gint      *intvar,
  677.           gint       min, 
  678.           gint       max, 
  679.           gboolean   constraint,
  680.           EntscaleIntCallbackFunc callback,
  681.           gpointer   call_data)
  682. {
  683.   EntscaleIntData *userdata;
  684.   GtkWidget *hbox;
  685.   GtkWidget *label;
  686.   GtkWidget *entry;
  687.   GtkWidget *scale;
  688.   GtkObject *adjustment;
  689.   gint         constraint_val;
  690.  
  691.   userdata = g_new (EntscaleIntData, 1);
  692.  
  693.   label = gtk_label_new (caption);
  694.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
  695.  
  696.   /*
  697.     If the first arg of gtk_adjustment_new() isn't between min and
  698.     max, it is automatically corrected by gtk later with
  699.     "value_changed" signal. I don't like this, since I want to leave
  700.     *intvar untouched when `constraint' is false.
  701.     The lines below might look oppositely, but this is OK.
  702.    */
  703.   userdata->constraint = constraint;
  704.   if( constraint )
  705.     constraint_val = *intvar;
  706.   else
  707.     constraint_val = ( *intvar < min ? min : *intvar > max ? max : *intvar );
  708.  
  709.   userdata->adjustment = adjustment = 
  710.     gtk_adjustment_new (constraint_val, min, max, 1.0, 1.0, 0.0);
  711.   scale = gtk_hscale_new (GTK_ADJUSTMENT (adjustment));
  712.   gtk_widget_set_usize (scale, ENTSCALE_INT_SCALE_WIDTH, 0);
  713.   gtk_scale_set_draw_value (GTK_SCALE (scale), FALSE);
  714.  
  715.   userdata->entry = entry = gtk_entry_new ();
  716.   gtk_widget_set_usize (entry, ENTSCALE_INT_ENTRY_WIDTH, 0);
  717.   g_snprintf (buffer, BUFSIZE, "%d", *intvar);
  718.   gtk_entry_set_text (GTK_ENTRY (entry), buffer);
  719.  
  720.   userdata->callback = callback;
  721.   userdata->call_data = call_data;
  722.  
  723.   /* userdata is done */
  724.   gtk_object_set_user_data (GTK_OBJECT (adjustment), userdata);
  725.   gtk_object_set_user_data (GTK_OBJECT (entry), userdata);
  726.  
  727.   /* now ready for signals */
  728.   gtk_signal_connect (GTK_OBJECT (entry), "changed",
  729.               (GtkSignalFunc) entscale_int_entry_update,
  730.               intvar);
  731.   gtk_signal_connect (GTK_OBJECT (adjustment), "value_changed",
  732.               (GtkSignalFunc) entscale_int_scale_update,
  733.               intvar);
  734.   gtk_signal_connect (GTK_OBJECT (entry), "destroy",
  735.               (GtkSignalFunc) entscale_int_destroy_callback,
  736.               userdata );
  737.  
  738.   /* start packing */
  739.   hbox = gtk_hbox_new (FALSE, 5);
  740.   gtk_box_pack_start (GTK_BOX (hbox), scale, TRUE, TRUE, 0);
  741.   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, TRUE, 0);
  742.  
  743.   gtk_table_attach (GTK_TABLE (table), label, x, x+1, y, y+1,
  744.             GTK_FILL, GTK_FILL, 0, 0);
  745.   gtk_table_attach (GTK_TABLE (table), hbox, x+1, x+2, y, y+1,
  746.             GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
  747.  
  748.   gtk_widget_show (label);
  749.   gtk_widget_show (entry);
  750.   gtk_widget_show (scale);
  751.   gtk_widget_show (hbox);
  752.  
  753.   return entry;
  754. }  
  755.  
  756.  
  757. /* when destroyed, userdata is destroyed too */
  758. static void
  759. entscale_int_destroy_callback (GtkWidget *widget,
  760.                    gpointer   data)
  761. {
  762.   EntscaleIntData *userdata;
  763.  
  764.   userdata = data;
  765.   g_free (userdata);
  766. }
  767.  
  768. static void
  769. entscale_int_scale_update (GtkAdjustment *adjustment,
  770.                gpointer       data)
  771. {
  772.   EntscaleIntData *userdata;
  773.   GtkEntry *entry;
  774.   gint     *intvar = data;
  775.   gint      new_val;
  776.  
  777.   userdata = gtk_object_get_user_data (GTK_OBJECT (adjustment));
  778.  
  779.   new_val = (gint) adjustment->value;
  780.  
  781.   *intvar = new_val;
  782.  
  783.   entry = GTK_ENTRY (userdata->entry);
  784.   g_snprintf (buffer, BUFSIZE, "%d", (int) new_val);
  785.   
  786.   /* avoid infinite loop (scale, entry, scale, entry ...) */
  787.   gtk_signal_handler_block_by_data (GTK_OBJECT (entry), data);
  788.   gtk_entry_set_text (entry, buffer);
  789.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (entry), data);
  790.  
  791.   if (userdata->callback)
  792.     (*userdata->callback) (*intvar, userdata->call_data);
  793. }
  794.  
  795. static void
  796. entscale_int_entry_update (GtkWidget *widget,
  797.                gpointer   data)
  798. {
  799.   EntscaleIntData *userdata;
  800.   GtkAdjustment      *adjustment;
  801.   gint           new_val, constraint_val;
  802.   gint          *intvar = data;
  803.  
  804.   userdata = gtk_object_get_user_data (GTK_OBJECT (widget));
  805.   adjustment = GTK_ADJUSTMENT (userdata->adjustment);
  806.  
  807.   new_val = atoi (gtk_entry_get_text (GTK_ENTRY (widget)));
  808.   constraint_val = new_val;
  809.   if ( constraint_val < adjustment->lower )
  810.     constraint_val = adjustment->lower;
  811.   if ( constraint_val > adjustment->upper )
  812.     constraint_val = adjustment->upper;
  813.  
  814.   if ( userdata->constraint )
  815.     *intvar = constraint_val;
  816.   else
  817.     *intvar = new_val;
  818.  
  819.   adjustment->value = constraint_val;
  820.   gtk_signal_handler_block_by_data (GTK_OBJECT (adjustment), data);
  821.   gtk_signal_emit_by_name (GTK_OBJECT (adjustment), "value_changed");
  822.   gtk_signal_handler_unblock_by_data (GTK_OBJECT (adjustment), data);
  823.   
  824.   if (userdata->callback)
  825.     (*userdata->callback) (*intvar, userdata->call_data);
  826. }
  827.