home *** CD-ROM | disk | FTP | other *** search
/ PC Pro 2002 April / pcpro0402.iso / essentials / graphics / Gimp / gimp-src-20001226.exe / src / gimp / libgimp / gimpchainbutton.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-08-28  |  11.8 KB  |  415 lines

  1. /* LIBGIMP - The GIMP Library 
  2.  * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball                
  3.  *
  4.  * gimpchainbutton.c
  5.  * Copyright (C) 1999-2000 Sven Neumann <sven@gimp.org> 
  6.  *
  7.  * This library is free software; you can redistribute it and/or
  8.  * modify it under the terms of the GNU Lesser General Public
  9.  * License as published by the Free Software Foundation; either
  10.  * version 2 of the License, or (at your option) any later version.
  11.  * 
  12.  * This library is distributed in the hope that it will be useful, 
  13.  * but WITHOUT ANY WARRANTY; without even the implied warranty of 
  14.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU  
  15.  * Library General Public License for more details.
  16.  *
  17.  * You should have received a copy of the GNU Lesser General Public
  18.  * License along with this library; if not, write to the
  19.  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  20.  * Boston, MA 02111-1307, USA.
  21.  */
  22.  
  23. #include <gtk/gtk.h>
  24.  
  25. #include "gimpchainbutton.h"
  26.  
  27. #include "pixmaps/chain.xpm"
  28.  
  29. static gchar **gimp_chain_xpm[] =
  30. {
  31.   chain_hor_xpm,
  32.   chain_ver_xpm
  33. };
  34.  
  35. static gchar **gimp_chain_broken_xpm[] =
  36. {
  37.   chain_broken_hor_xpm,
  38.   chain_broken_ver_xpm 
  39. };
  40.  
  41. static guint gimp_chain_width[] =
  42. {
  43.   24,
  44.   9
  45. };
  46.  
  47. static guint gimp_chain_height[] =
  48. {
  49.   9,
  50.   24
  51. };
  52.  
  53. static void gimp_chain_button_destroy          (GtkObject       *object);
  54. static void gimp_chain_button_realize          (GtkWidget       *widget);
  55.  
  56. static void gimp_chain_button_clicked_callback (GtkWidget       *widget,
  57.                         GimpChainButton *gcb);
  58. static gint gimp_chain_button_draw_lines       (GtkWidget       *widget,
  59.                         GdkEventExpose  *eevent,
  60.                         GimpChainButton *gcb);
  61.  
  62. enum
  63. {
  64.   TOGGLED,
  65.   LAST_SIGNAL
  66. };
  67.  
  68. static guint gimp_chain_button_signals[LAST_SIGNAL] = { 0 };
  69.  
  70. static GtkTableClass *parent_class = NULL;
  71.  
  72.  
  73. static void
  74. gimp_chain_button_destroy (GtkObject *object)
  75. {
  76.   GimpChainButton *gcb;
  77.  
  78.   g_return_if_fail (gcb = GIMP_CHAIN_BUTTON (object));
  79.  
  80.   if (gcb->broken)
  81.     gdk_pixmap_unref (gcb->broken);
  82.   if (gcb->broken_mask)
  83.     gdk_bitmap_unref (gcb->broken_mask);
  84.  
  85.   if (gcb->chain)
  86.     gdk_pixmap_unref (gcb->chain);
  87.   if (gcb->chain_mask)
  88.     gdk_bitmap_unref (gcb->chain_mask);
  89.  
  90.   if (GTK_OBJECT_CLASS (parent_class)->destroy)
  91.     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
  92. }
  93.  
  94. static void
  95. gimp_chain_button_class_init (GimpChainButtonClass *class)
  96. {
  97.   GtkObjectClass *object_class;
  98.   GtkWidgetClass *widget_class;
  99.  
  100.   object_class = (GtkObjectClass*) class;
  101.   widget_class = (GtkWidgetClass *) class;
  102.  
  103.   parent_class = gtk_type_class (gtk_table_get_type ());
  104.  
  105.   object_class->destroy = gimp_chain_button_destroy;
  106.  
  107.   gimp_chain_button_signals[TOGGLED] = 
  108.     gtk_signal_new ("toggled",
  109.             GTK_RUN_FIRST,
  110.             object_class->type,
  111.             GTK_SIGNAL_OFFSET (GimpChainButtonClass,
  112.                        toggled),
  113.             gtk_signal_default_marshaller, GTK_TYPE_NONE, 0);
  114.  
  115.   gtk_object_class_add_signals (object_class, gimp_chain_button_signals, 
  116.                 LAST_SIGNAL);
  117.  
  118.   class->toggled = NULL;
  119.  
  120.   widget_class->realize = gimp_chain_button_realize;
  121. }
  122.  
  123. static void
  124. gimp_chain_button_init (GimpChainButton *gcb)
  125. {
  126.   gcb->position    = GIMP_CHAIN_TOP;
  127.   gcb->line1       = gtk_drawing_area_new ();
  128.   gcb->line2       = gtk_drawing_area_new ();
  129.   gcb->pixmap      = NULL;
  130.   gcb->broken      = NULL;
  131.   gcb->broken_mask = NULL;
  132.   gcb->chain       = NULL;
  133.   gcb->chain_mask  = NULL;
  134.   gcb->active      = FALSE;
  135.  
  136.   gcb->button = gtk_button_new ();
  137.   gtk_button_set_relief (GTK_BUTTON (gcb->button), GTK_RELIEF_NONE);
  138.  
  139.   gcb->pixmap = gtk_type_new (gtk_pixmap_get_type ());
  140.   gtk_pixmap_set_build_insensitive (GTK_PIXMAP (gcb->pixmap), TRUE);
  141.  
  142.   gtk_signal_connect (GTK_OBJECT(gcb->button), "clicked",
  143.               GTK_SIGNAL_FUNC (gimp_chain_button_clicked_callback), gcb);
  144.   gtk_signal_connect (GTK_OBJECT (gcb->line1), "expose_event",
  145.               GTK_SIGNAL_FUNC (gimp_chain_button_draw_lines),
  146.               gcb);
  147.   gtk_signal_connect (GTK_OBJECT (gcb->line2), "expose_event",
  148.               GTK_SIGNAL_FUNC (gimp_chain_button_draw_lines),
  149.               gcb);
  150. }
  151.  
  152. GtkType
  153. gimp_chain_button_get_type (void)
  154. {
  155.   static guint gcb_type = 0;
  156.  
  157.   if (!gcb_type)
  158.     {
  159.       GtkTypeInfo gcb_info =
  160.       {
  161.     "GimpChainButton",
  162.     sizeof (GimpChainButton),
  163.     sizeof (GimpChainButtonClass),
  164.     (GtkClassInitFunc) gimp_chain_button_class_init,
  165.     (GtkObjectInitFunc) gimp_chain_button_init,
  166.     /* reserved_1 */ NULL,
  167.     /* reserved_2 */ NULL,
  168.         (GtkClassInitFunc) NULL
  169.       };
  170.  
  171.       gcb_type = gtk_type_unique (gtk_table_get_type (), &gcb_info);
  172.     }
  173.  
  174.   return gcb_type;
  175. }
  176.  
  177. /**
  178.  * gimp_chain_button_new:
  179.  * @position: The position you are going to use for the button
  180.  *            with respect to the widgets you want to chain.
  181.  * 
  182.  * Creates a new #GimpChainButton widget.
  183.  *
  184.  * This returns a button showing either a broken or a linked chain and
  185.  * small clamps attached to both sides that visually group the two widgets 
  186.  * you want to connect. This widget looks best when attached
  187.  * to a table taking up two columns (or rows respectively) next
  188.  * to the widgets that it is supposed to connect. It may work
  189.  * for more than two widgets, but the look is optimized for two.
  190.  *
  191.  * Returns: Pointer to the new #GimpChainButton, which is inactive
  192.  *          by default. Use gimp_chain_button_set_active() to 
  193.  *          change its state.
  194.  */
  195. GtkWidget *
  196. gimp_chain_button_new (GimpChainPosition position)
  197. {
  198.   GimpChainButton *gcb;
  199.  
  200.   gcb = gtk_type_new (gimp_chain_button_get_type ());
  201.  
  202.   gcb->position = position;
  203.  
  204.   gcb->pixmap->requisition.width = 
  205.     gimp_chain_width[position % 2] + GTK_MISC (gcb->pixmap)->xpad * 2;
  206.   gcb->pixmap->requisition.height = 
  207.     gimp_chain_height[position % 2] + GTK_MISC (gcb->pixmap)->ypad * 2;
  208.  
  209.   gtk_container_add (GTK_CONTAINER (gcb->button), gcb->pixmap);
  210.   gtk_widget_show (gcb->pixmap);
  211.   gtk_widget_show (gcb->button);
  212.  
  213.   if (position & GIMP_CHAIN_LEFT) /* are we a vertical chainbutton? */
  214.     {
  215.       gtk_table_resize (GTK_TABLE (gcb), 3, 1);
  216.       gtk_table_attach (GTK_TABLE (gcb), gcb->button, 0, 1, 1, 2,
  217.             GTK_SHRINK, GTK_SHRINK, 0, 0);
  218.       gtk_table_attach_defaults (GTK_TABLE (gcb), gcb->line1, 0, 1, 0, 1);
  219.       gtk_table_attach_defaults (GTK_TABLE (gcb), gcb->line2, 0, 1, 2, 3);
  220.     }
  221.   else
  222.     {
  223.       gtk_table_resize (GTK_TABLE (gcb), 1, 3);
  224.       gtk_table_attach (GTK_TABLE (gcb), gcb->button, 1, 2, 0, 1,
  225.             GTK_SHRINK, GTK_SHRINK, 0, 0);
  226.       gtk_table_attach_defaults (GTK_TABLE (gcb), gcb->line1, 0, 1, 0, 1);
  227.       gtk_table_attach_defaults (GTK_TABLE (gcb), gcb->line2, 2, 3, 0, 1);
  228.     }
  229.  
  230.   gtk_widget_show (gcb->line1);
  231.   gtk_widget_show (gcb->line2);
  232.  
  233.   return GTK_WIDGET (gcb);
  234. }
  235.  
  236. /** 
  237.  * gimp_chain_button_set_active:
  238.  * @gcb: Pointer to a #GimpChainButton.
  239.  * @is_active: The new state.
  240.  * 
  241.  * Sets the state of the #GimpChainButton to be either locked (TRUE) or 
  242.  * unlocked (FALSE) and changes the showed pixmap to reflect the new state.
  243.  */
  244. void       
  245. gimp_chain_button_set_active (GimpChainButton  *gcb,
  246.                   gboolean          is_active)
  247. {
  248.   g_return_if_fail (GIMP_IS_CHAIN_BUTTON (gcb));
  249.  
  250.   if (gcb->active != is_active)
  251.     {
  252.       gcb->active = is_active;
  253.  
  254.      if (!GTK_WIDGET_REALIZED (GTK_WIDGET (gcb)))
  255.     return;
  256.  
  257.      if (gcb->active)
  258.        gtk_pixmap_set (GTK_PIXMAP(gcb->pixmap), gcb->chain, gcb->chain_mask);
  259.      else
  260.        gtk_pixmap_set (GTK_PIXMAP(gcb->pixmap), gcb->broken, gcb->broken_mask);
  261.     }
  262. }
  263.  
  264. /**
  265.  * gimp_chain_button_get_active
  266.  * @gcb: Pointer to a #GimpChainButton.
  267.  * 
  268.  * Checks the state of the #GimpChainButton. 
  269.  *
  270.  * Returns: TRUE if the #GimpChainButton is active (locked).
  271.  */
  272. gboolean   
  273. gimp_chain_button_get_active (GimpChainButton *gcb)
  274. {
  275.   g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (gcb), FALSE);
  276.  
  277.   return gcb->active;
  278. }
  279.  
  280.  
  281. static void
  282. gimp_chain_button_realize (GtkWidget *widget)
  283. {
  284.   GimpChainButton *gcb;
  285.   GtkStyle *style;
  286.  
  287.   gcb = GIMP_CHAIN_BUTTON (widget);
  288.  
  289.   if (GTK_WIDGET_CLASS (parent_class)->realize)
  290.     (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
  291.  
  292.   style = gtk_widget_get_style (widget);
  293.   gcb->chain = gdk_pixmap_create_from_xpm_d (widget->window,
  294.                          &gcb->chain_mask,
  295.                          &style->bg[GTK_STATE_NORMAL],
  296.                          gimp_chain_xpm[gcb->position % 2]);
  297.   gcb->broken = gdk_pixmap_create_from_xpm_d (widget->window,
  298.                           &gcb->broken_mask,
  299.                           &style->bg[GTK_STATE_NORMAL],
  300.                           gimp_chain_broken_xpm[gcb->position % 2]);
  301.  
  302.   if (gcb->active) 
  303.     gtk_pixmap_set (GTK_PIXMAP (gcb->pixmap), gcb->chain, gcb->chain_mask);
  304.   else
  305.     gtk_pixmap_set (GTK_PIXMAP (gcb->pixmap), gcb->broken, gcb->broken_mask);
  306.  
  307.   gtk_widget_realize (gcb->line1);
  308.   gtk_style_set_background (widget->style, gcb->line1->window, GTK_STATE_NORMAL);
  309.   gdk_window_set_back_pixmap (gcb->line1->window, NULL, TRUE);
  310.  
  311.   gtk_widget_realize (gcb->line2);
  312.   gtk_style_set_background (widget->style, gcb->line2->window, GTK_STATE_NORMAL);
  313.   gdk_window_set_back_pixmap (gcb->line2->window, NULL, TRUE);
  314. }
  315.  
  316. static void
  317. gimp_chain_button_clicked_callback (GtkWidget       *widget,
  318.                     GimpChainButton *gcb)
  319. {
  320.   g_return_if_fail (GIMP_IS_CHAIN_BUTTON (gcb));
  321.  
  322.   gcb->active = !(gcb->active);
  323.  
  324.   if (!GTK_WIDGET_REALIZED (GTK_WIDGET (gcb)))
  325.     return;
  326.  
  327.   if (gcb->active)
  328.     gtk_pixmap_set (GTK_PIXMAP(gcb->pixmap), gcb->chain, gcb->chain_mask);
  329.   else
  330.     gtk_pixmap_set (GTK_PIXMAP(gcb->pixmap), gcb->broken, gcb->broken_mask);
  331.  
  332.   gtk_signal_emit (GTK_OBJECT (gcb), gimp_chain_button_signals[TOGGLED]);
  333. }
  334.  
  335. static gint
  336. gimp_chain_button_draw_lines (GtkWidget       *widget,
  337.                   GdkEventExpose  *eevent,
  338.                   GimpChainButton *gcb)
  339. {
  340.   GdkPoint      points[3];
  341.   GdkPoint      buf;
  342.   GtkShadowType    shadow;
  343.   gint          which_line;
  344.  
  345. #define SHORT_LINE 4
  346.   /* don't set this too high, there's no check against drawing outside 
  347.      the widgets bounds yet (and probably never will be) */
  348.  
  349.   g_return_val_if_fail (GIMP_IS_CHAIN_BUTTON (gcb), FALSE);
  350.  
  351.   points[0].x = widget->allocation.width / 2;
  352.   points[0].y = widget->allocation.height / 2;
  353.  
  354.   which_line = (widget == gcb->line1) ? 1 : -1;
  355.  
  356.   switch (gcb->position)
  357.     {
  358.     case GIMP_CHAIN_LEFT:
  359.       points[0].x += SHORT_LINE;
  360.       points[1].x = points[0].x - SHORT_LINE;
  361.       points[1].y = points[0].y;
  362.       points[2].x = points[1].x;
  363.       points[2].y = (which_line == 1) ? widget->allocation.height - 1 : 0;
  364.       shadow = GTK_SHADOW_ETCHED_IN;
  365.       break;
  366.     case GIMP_CHAIN_RIGHT:
  367.       points[0].x -= SHORT_LINE;
  368.       points[1].x = points[0].x + SHORT_LINE;
  369.       points[1].y = points[0].y;
  370.       points[2].x = points[1].x;
  371.       points[2].y = (which_line == 1) ? widget->allocation.height - 1 : 0;
  372.       shadow = GTK_SHADOW_ETCHED_OUT;
  373.       break;
  374.     case GIMP_CHAIN_TOP:
  375.       points[0].y += SHORT_LINE;
  376.       points[1].x = points[0].x;
  377.       points[1].y = points[0].y - SHORT_LINE;
  378.       points[2].x = (which_line == 1) ? widget->allocation.width - 1 : 0;
  379.       points[2].y = points[1].y;
  380.       shadow = GTK_SHADOW_ETCHED_OUT;
  381.       break;
  382.     case GIMP_CHAIN_BOTTOM:
  383.       points[0].y -= SHORT_LINE;
  384.       points[1].x = points[0].x;
  385.       points[1].y = points[0].y + SHORT_LINE;
  386.       points[2].x = (which_line == 1) ? widget->allocation.width - 1 : 0;
  387.       points[2].y = points[1].y;
  388.       shadow = GTK_SHADOW_ETCHED_IN;
  389.       break;
  390.     default:
  391.       return FALSE;
  392.     }
  393.  
  394.   if ( ((shadow == GTK_SHADOW_ETCHED_OUT) && (which_line == -1)) ||
  395.        ((shadow == GTK_SHADOW_ETCHED_IN) && (which_line == 1)) )
  396.     {
  397.       buf = points[0];
  398.       points[0] = points[2];
  399.       points[2] = buf;
  400.     }
  401.  
  402.   gtk_paint_polygon (widget->style,
  403.              widget->window,
  404.              GTK_STATE_NORMAL,
  405.              shadow,
  406.              &eevent->area,
  407.              widget,
  408.              "chainbutton",
  409.              points,
  410.              3,
  411.              FALSE);
  412.  
  413.   return TRUE;
  414. }
  415.