libyui-gtk-pkg  2.42.9
 All Classes
ygtkcellrendererbutton.c
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 
5 /* YGtkCellRendererButton widget */
6 // check the header file for information about this widget
7 
8 #include "ygtkcellrendererbutton.h"
9 #include <gtk/gtk.h>
10 
11 #define INNER_BORDER 4
12 #define OUTER_BORDER 1
13 #define BORDER (INNER_BORDER+OUTER_BORDER)
14 #define DEPRESS_PAD 1
15 #define PIXBUF_TEXT_SPACING 4
16 
17 enum {
18  PROP_0,
19  PROP_ACTIVE,
20  PROP_PIXBUF,
21  PROP_ICON_NAME,
22  PROP_STOCK_ID,
23  PROP_ICON_SIZE,
24 };
25 
26 static guint toggle_cell_signal = 0;
27 
28 G_DEFINE_TYPE (YGtkCellRendererButton, ygtk_cell_renderer_button, GTK_TYPE_CELL_RENDERER_TEXT)
29 
30 static void ygtk_cell_renderer_button_init (YGtkCellRendererButton *bcell)
31 {
32  bcell->active = FALSE;
33  GtkCellRenderer *cell = GTK_CELL_RENDERER (bcell);
34  g_object_set(cell, "xpad", BORDER, "ypad", BORDER, "mode", GTK_CELL_RENDERER_MODE_ACTIVATABLE, NULL);
35  bcell->icon_size = 16;
36 }
37 
38 static void unset_image_properties (YGtkCellRendererButton *cell)
39 {
40  if (cell->icon_name) {
41  g_free (cell->icon_name);
42  cell->icon_name = NULL;
43  }
44  if (cell->pixbuf) {
45  g_object_unref (cell->pixbuf);
46  cell->pixbuf = NULL;
47  }
48 }
49 
50 static void ygtk_cell_renderer_button_finalize (GObject *object)
51 {
52  YGtkCellRendererButton *ccell = YGTK_CELL_RENDERER_BUTTON (object);
53  unset_image_properties (ccell);
54  G_OBJECT_CLASS (ygtk_cell_renderer_button_parent_class)->finalize (object);
55 }
56 
57 static void ygtk_cell_renderer_button_get_property (GObject *object,
58  guint param_id, GValue *value, GParamSpec *pspec)
59 {
60  if (pspec->owner_type == YGTK_TYPE_CELL_RENDERER_BUTTON) {
61  YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (object);
62  switch (param_id) {
63  case PROP_ACTIVE:
64  g_value_set_boolean (value, bcell->active);
65  break;
66  case PROP_PIXBUF:
67  g_value_set_object (value, G_OBJECT (bcell->pixbuf));
68  break;
69  case PROP_STOCK_ID:
70  g_value_set_string (value, bcell->stock_id);
71  break;
72  case PROP_ICON_NAME:
73  g_value_set_string (value, bcell->icon_name);
74  break;
75  case PROP_ICON_SIZE:
76  g_value_set_uint (value, bcell->icon_size);
77  break;
78  }
79  }
80  else
81  G_OBJECT_CLASS (ygtk_cell_renderer_button_parent_class)->get_property (
82  object, param_id, value, pspec);
83 }
84 
85 static void ygtk_cell_renderer_button_set_property (GObject *object,
86  guint param_id, const GValue *value, GParamSpec *pspec)
87 {
88  if (pspec->owner_type == YGTK_TYPE_CELL_RENDERER_BUTTON) {
89  YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (object);
90  switch (param_id) {
91  case PROP_ACTIVE:
92  bcell->active = g_value_get_boolean (value);
93  break;
94  case PROP_PIXBUF:
95  unset_image_properties (bcell);
96  bcell->pixbuf = (GdkPixbuf *) g_value_dup_object (value);
97  break;
98  case PROP_STOCK_ID:
99  unset_image_properties (bcell);
100  bcell->stock_id = g_value_dup_string (value);
101  break;
102  case PROP_ICON_NAME:
103  unset_image_properties (bcell);
104  bcell->icon_name = g_value_dup_string (value);
105  break;
106  case PROP_ICON_SIZE:
107  bcell->icon_size = g_value_get_uint (value);
108  break;
109  }
110  }
111  else
112  G_OBJECT_CLASS (ygtk_cell_renderer_button_parent_class)->set_property (
113  object, param_id, value, pspec);
114 }
115 
116 static PangoLayout *create_layout (YGtkCellRendererButton *bcell, GtkWidget *widget)
117 {
118  GtkCellRendererText *tcell = GTK_CELL_RENDERER_TEXT (bcell);
119  gchar *text = NULL;
120  g_object_get (G_OBJECT (tcell), "text", &text, NULL);
121  PangoLayout *layout = NULL;
122  if (text) {
123  layout = gtk_widget_create_pango_layout (widget, text);
124  g_free(text);
125  }
126  return layout;
127 }
128 
129 static void ensure_pixbuf (YGtkCellRendererButton *cell, GtkWidget *widget)
130 {
131  if (!cell->pixbuf && (cell->icon_name || cell->stock_id)) {
132  if (cell->icon_name) {
133  GtkIconTheme *theme = gtk_icon_theme_get_default();
134  GError *error = 0;
135  cell->pixbuf = gtk_icon_theme_load_icon (theme, cell->icon_name,
136  cell->icon_size, GTK_ICON_LOOKUP_FORCE_SIZE, &error);
137  if (!cell->pixbuf)
138  g_warning ("Couldn't load ygtk-cell-renderer-button icon: %s\nGtk: %s\n",
139  cell->icon_name, error->message);
140  }
141  else // stock-id
142  cell->pixbuf = gtk_widget_render_icon_pixbuf (
143  widget, cell->stock_id, GTK_ICON_SIZE_BUTTON);
144  }
145 }
146 
147 static void ygtk_cell_renderer_button_get_size_full (GtkCellRenderer *cell,
148  GtkWidget *widget, const GdkRectangle *cell_area, gint *_xoffset, gint *_yoffset,
149  gint *_width, gint *_height, gint *_pixbuf_xoffset, gint *_pixbuf_yoffset,
150  gint *_pixbuf_width, gint *_pixbuf_height, gint *_text_xoffset, gint *_text_yoffset)
151 {
152  YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (cell);
153  ensure_pixbuf (bcell, widget);
154 
155  int pixbuf_width = 0, pixbuf_height = 0;
156  if (bcell->pixbuf) {
157  pixbuf_width = gdk_pixbuf_get_width (bcell->pixbuf);
158  pixbuf_height = gdk_pixbuf_get_height (bcell->pixbuf);
159  }
160 
161  PangoLayout *layout = create_layout (bcell, widget);
162  int text_width = 0, text_height = 0;
163  if (layout) {
164  PangoRectangle rect;
165  pango_layout_get_pixel_extents (layout, NULL, &rect);
166  text_width = rect.x + rect.width;
167  text_height = rect.y + rect.height;
168  }
169 
170  int width, height;
171  width = pixbuf_width + text_width;
172  if (pixbuf_width && text_width)
173  width += PIXBUF_TEXT_SPACING;
174  height = MAX (pixbuf_height, text_height);
175 
176  gfloat xalign, yalign;
177  guint xpad, ypad;
178  g_object_get(cell,
179  "xalign", &xalign,
180  "yalign", &yalign,
181  "xpad", &xpad,
182  "ypad", &ypad, NULL);
183 
184  if (cell_area) {
185  gboolean reverse = gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL;
186  if (reverse)
187  xalign = 1.0 - xalign;
188 
189  int cell_width = cell_area->width - xpad*2,
190  cell_height = cell_area->height - ypad*2;
191  int xoffset = (xalign * (cell_width - width)) + xpad;
192  int yoffset = (yalign * (cell_height - height)) + ypad;
193  if (_xoffset) *_xoffset = xoffset;
194  if (_yoffset) *_yoffset = yoffset;
195 
196  int text_x = xoffset, text_y;
197  if (pixbuf_width && !reverse)
198  text_x += (pixbuf_width + PIXBUF_TEXT_SPACING);
199  text_y = (yalign * (cell_height - text_height)) + ypad;
200 
201  int pixbuf_x = xoffset, pixbuf_y;
202  if (text_width && reverse)
203  pixbuf_x += (text_width + PIXBUF_TEXT_SPACING);
204  pixbuf_y = (yalign * (cell_height - pixbuf_height)) + ypad;
205 
206  if (_pixbuf_xoffset) *_pixbuf_xoffset = pixbuf_x;
207  if (_pixbuf_yoffset) *_pixbuf_yoffset = pixbuf_y;
208  if (_pixbuf_width) *_pixbuf_width = pixbuf_width;
209  if (_pixbuf_height) *_pixbuf_height = pixbuf_height;
210  if (_text_xoffset) *_text_xoffset = text_x;
211  if (_text_yoffset) *_text_yoffset = text_y;
212  }
213 
214  if (_width) *_width = width + (xpad * 2);
215  if (_height) *_height = height + (ypad * 2);
216 }
217 
218 static void ygtk_cell_renderer_button_get_size (GtkCellRenderer *cell,
219  GtkWidget *widget, const GdkRectangle *cell_area, gint *xoffset, gint *yoffset,
220  gint *width, gint *height)
221 {
222  ygtk_cell_renderer_button_get_size_full (cell, widget, cell_area,
223  xoffset, yoffset, width, height, NULL, NULL, NULL, NULL, NULL, NULL);
224 }
225 
226 static void ygtk_cell_renderer_button_render(
227  GtkCellRenderer *cell, cairo_t *cr, GtkWidget *widget,
228  const GdkRectangle *background_area, const GdkRectangle *cell_area,
229  GtkCellRendererState flags)
230 {
231  YGtkCellRendererButton *bcell = YGTK_CELL_RENDERER_BUTTON (cell);
232 
233  GtkStateType state = GTK_STATE_NORMAL;
234  if (!gtk_cell_renderer_get_sensitive(cell) || gtk_widget_get_state (widget) == GTK_STATE_INSENSITIVE)
235  state = GTK_STATE_INSENSITIVE;
236  else if ((flags & GTK_CELL_RENDERER_PRELIT))
237  state = GTK_STATE_PRELIGHT;
238  if (bcell->active)
239  state = GTK_STATE_ACTIVE;
240 
241  GtkStyleContext *style = gtk_widget_get_style_context(widget);
242  gtk_style_context_save(style);
243  gtk_style_context_set_state(style, state);
244  gtk_style_context_add_class (style, GTK_STYLE_CLASS_BUTTON);
245 
246  int text_xoffset, text_yoffset, pixbuf_xoffset, pixbuf_yoffset, pixbuf_width,
247  pixbuf_height, width, height;
248  ygtk_cell_renderer_button_get_size_full (cell, widget, cell_area,
249  NULL, NULL, &width, &height, &pixbuf_xoffset, &pixbuf_yoffset, &pixbuf_width,
250  &pixbuf_height, &text_xoffset, &text_yoffset);
251 
252  int x = cell_area->x + OUTER_BORDER + 1;
253  int y = cell_area->y + (cell_area->height - height)/2 + OUTER_BORDER;
254  width -= OUTER_BORDER*2; height -= OUTER_BORDER*2;
255 
256  gtk_render_background (style, cr, x, y, width, height);
257  gtk_render_frame (style, cr, x, y, width, height);
258 
259  // paint content
260 
261  ensure_pixbuf (bcell, widget);
262  if (bcell->pixbuf) {
263  int _x = x + pixbuf_xoffset, _y = y + pixbuf_yoffset;
264  if (bcell->active) {
265  _x += DEPRESS_PAD; _y += DEPRESS_PAD;
266  }
267  gdk_cairo_set_source_pixbuf (cr, bcell->pixbuf, _x, _y);
268  cairo_rectangle (cr, _x, _y, pixbuf_width, pixbuf_height);
269  cairo_fill (cr);
270  }
271 
272  PangoLayout *layout = create_layout (bcell, widget);
273  if (layout) {
274  int _x = cell_area->x + text_xoffset, _y = cell_area->y + text_yoffset;
275  if (bcell->active) {
276  _x += DEPRESS_PAD; _y += DEPRESS_PAD;
277  }
278 
279  gtk_render_layout(style, cr, _x, _y, layout);
280  g_object_unref (G_OBJECT (layout));
281  }
282 
283  gtk_style_context_restore(style);
284 }
285 
286 static gboolean ygtk_cell_renderer_button_activate (GtkCellRenderer *cell,
287  GdkEvent *event, GtkWidget *widget, const gchar *path, const GdkRectangle *background_area,
288  const GdkRectangle *cell_area, GtkCellRendererState flags)
289 {
290  GdkEventButton *_event = &event->button;
291  if (_event->x >= cell_area->x && _event->x <= cell_area->x + cell_area->width) {
292  g_signal_emit (cell, toggle_cell_signal, 0, path);
293  return TRUE;
294  }
295  return FALSE;
296 }
297 
298 GtkCellRenderer *ygtk_cell_renderer_button_new (void)
299 { return g_object_new (YGTK_TYPE_CELL_RENDERER_BUTTON, NULL); }
300 
301 gboolean ygtk_cell_renderer_button_get_active (YGtkCellRendererButton *cell)
302 { return cell->active; }
303 
304 static void ygtk_cell_renderer_button_class_init (YGtkCellRendererButtonClass *class)
305 {
306  GObjectClass *object_class = G_OBJECT_CLASS (class);
307  object_class->get_property = ygtk_cell_renderer_button_get_property;
308  object_class->set_property = ygtk_cell_renderer_button_set_property;
309  object_class->finalize = ygtk_cell_renderer_button_finalize;
310 
311  GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (class);
312  cell_class->get_size = ygtk_cell_renderer_button_get_size;
313  cell_class->render = ygtk_cell_renderer_button_render;
314  cell_class->activate = ygtk_cell_renderer_button_activate;
315 
316  GParamFlags readwrite_flag =
317  G_PARAM_READWRITE|G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB;
318  g_object_class_install_property (object_class, PROP_ACTIVE,
319  g_param_spec_boolean ("active", "Toggle state", "The toggle state of the button",
320  FALSE, readwrite_flag));
321  g_object_class_install_property (object_class, PROP_PIXBUF,
322  g_param_spec_object ("pixbuf", "Image", "Side image", GDK_TYPE_PIXBUF, readwrite_flag));
323  g_object_class_install_property (object_class, PROP_ICON_NAME,
324  g_param_spec_string ("icon-name", "Icon name", "Theme icon to render", NULL, readwrite_flag));
325  g_object_class_install_property (object_class, PROP_STOCK_ID,
326  g_param_spec_string ("stock-id", "Stock id", "Stock icon to render", NULL, readwrite_flag));
327  g_object_class_install_property (object_class, PROP_ICON_SIZE,
328  g_param_spec_uint ("icon-size", "Size", "Size of the icon to render",
329  0, G_MAXUINT, 22, readwrite_flag));
330 
331  toggle_cell_signal = g_signal_new ("toggled", G_OBJECT_CLASS_TYPE (object_class),
332  G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (YGtkCellRendererButtonClass, toggled),
333  NULL, NULL, g_cclosure_marshal_VOID__STRING, G_TYPE_NONE, 1, G_TYPE_STRING);
334 }
335