libyui-gtk  2.44.8
ygtkratiobox.c
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 
5 /* YGtkRatioBox container */
6 // check the header file for information about this container
7 
8 #include <yui/Libyui_config.h>
9 #include <math.h>
10 #include "ygtkratiobox.h"
11 
12 G_DEFINE_ABSTRACT_TYPE (YGtkRatioBox, ygtk_ratio_box, GTK_TYPE_CONTAINER)
13 
14 static void ygtk_ratio_box_init (YGtkRatioBox *box)
15 {
16  gtk_widget_set_has_window (GTK_WIDGET(box), FALSE);
17  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (box), FALSE);
18 }
19 
20 static GType ygtk_ratio_box_child_type (GtkContainer* container)
21 { return GTK_TYPE_WIDGET; }
22 
23 void ygtk_ratio_box_pack (YGtkRatioBox *box, GtkWidget *child, gfloat ratio)
24 {
25  YGtkRatioBoxChild* child_info;
26  child_info = g_new (YGtkRatioBoxChild, 1);
27  child_info->widget = child;
28  child_info->ratio = ratio;
29 
30  box->children = g_list_append (box->children, child_info);
31 
32  gtk_widget_freeze_child_notify (child);
33  gtk_widget_set_parent (child, GTK_WIDGET (box));
34  gtk_widget_thaw_child_notify (child);
35 }
36 
37 static YGtkRatioBoxChild *ygtk_ratio_get_child_info (YGtkRatioBox *box, GtkWidget *child)
38 {
39  YGtkRatioBoxChild *i = NULL;
40  GList *list;
41  for (list = box->children; list; list = list->next) {
42  i = (YGtkRatioBoxChild*) list->data;
43  if (i->widget == child)
44  break;
45  }
46  if (!list)
47  return NULL;
48  return i;
49 }
50 
51 static void ygtk_ratio_box_add (GtkContainer *container, GtkWidget *child)
52 {
53  ygtk_ratio_box_pack (YGTK_RATIO_BOX (container), child, 1.0);
54 }
55 
56 static void ygtk_ratio_box_remove (GtkContainer *container, GtkWidget *widget)
57 {
58  YGtkRatioBox* box = YGTK_RATIO_BOX (container);
59 
60  GList* child = box->children;
61  for (child = box->children; child; child = child->next) {
62  YGtkRatioBoxChild *box_child = (YGtkRatioBoxChild*) child->data;
63  if (box_child->widget == widget) {
64  gboolean was_visible = gtk_widget_get_visible (widget);
65  gtk_widget_unparent (widget);
66 
67  box->children = g_list_remove_link (box->children, child);
68  g_list_free (child);
69  g_free (box_child);
70 
71  if (was_visible)
72  gtk_widget_queue_resize (GTK_WIDGET (container));
73  break;
74  }
75  }
76 }
77 
78 static void ygtk_ratio_box_forall (GtkContainer *container, gboolean include_internals,
79  GtkCallback callback, gpointer callback_data)
80 {
81  g_return_if_fail (callback != NULL);
82 
83  YGtkRatioBox* box = YGTK_RATIO_BOX (container);
84 
85  GList* children = box->children;
86  while (children) {
87  YGtkRatioBoxChild* child = (YGtkRatioBoxChild*) children->data;
88  children = children->next;
89  (* callback) (child->widget, callback_data);
90  }
91 }
92 
93 /* We put size_request and _allocate in the same functions for both
94  orientations because it's just easier to maintain having the
95  logic in the same place. */
96 static void ygtk_ratio_box_get_preferred_size (GtkWidget *widget,
97  GtkRequisition *requisition,
98  GtkOrientation orientation)
99 {
100  requisition->width = requisition->height = 0;
101 
102  YGtkRatioBox* box = YGTK_RATIO_BOX (widget);
103  gint children_nb = 0;
104  GList *i;
105  for (i = box->children; i; i = i->next) {
106  YGtkRatioBoxChild* child = i->data;
107  if (!gtk_widget_get_visible (child->widget))
108  continue;
109 
110  GtkRequisition min_child_req;
111  GtkRequisition nat_child_req;
112  gtk_widget_get_preferred_size (child->widget, &min_child_req, &nat_child_req);
113  if (orientation == GTK_ORIENTATION_HORIZONTAL)
114  requisition->height = MAX (requisition->height, min_child_req.height);
115  else
116  requisition->width = MAX (requisition->width, min_child_req.width);
117  children_nb++;
118  }
119  gint spacing = children_nb ? box->spacing*(children_nb-1) : 0;
120  if (orientation == GTK_ORIENTATION_HORIZONTAL)
121  requisition->width += spacing;
122  else
123  requisition->height += spacing;
124 
125  int border = gtk_container_get_border_width(GTK_CONTAINER (box));
126  requisition->width += border*2;
127  requisition->height += border*2;
128 }
129 
130 static void ygtk_ratio_box_size_allocate (GtkWidget *widget,
131  GtkAllocation *allocation,
132  GtkOrientation orientation)
133 {
134  YGtkRatioBox* box = YGTK_RATIO_BOX (widget);
135 
136  gfloat ratios_sum = 0;
137  gint children_nb = 0;
138 
139  GList* i;
140  for (i = box->children; i; i = i->next) {
141  YGtkRatioBoxChild* child = i->data;
142  if (!gtk_widget_get_visible (child->widget))
143  continue;
144 
145  ratios_sum += child->ratio;
146  children_nb++;
147  }
148 
149  gint spacing = children_nb ? box->spacing*(children_nb-1) : 0;
150 
151  int border = gtk_container_get_border_width(GTK_CONTAINER (box));
152  int x = allocation->x + border, y = allocation->y + border,
153  width = allocation->width - border*2, height = allocation->height - border*2;
154 
155  gint length;
156  if (orientation == GTK_ORIENTATION_HORIZONTAL)
157  length = width - spacing;
158  else
159  length = height - spacing;
160  gint child_pos = 0;
161 
162  for (i = box->children; i; i = i->next) {
163  YGtkRatioBoxChild* child = i->data;
164  if (!gtk_widget_get_visible (child->widget))
165  continue;
166 
167  gint child_length = (child->ratio * length) / ratios_sum;
168  if (!i->next) // last takes rest (any residual length)
169  child_length = length - child_pos;
170 
171  GtkAllocation child_alloc;
172  if (orientation == GTK_ORIENTATION_HORIZONTAL) {
173  child_alloc.x = x + child_pos;
174  child_alloc.y = y;
175  child_alloc.width = child_length;
176  child_alloc.height = height;
177  }
178  else { // GTK_ORIENTATION_VERTICAL
179  child_alloc.x = x;
180  child_alloc.y = y + child_pos;
181  child_alloc.width = width;
182  child_alloc.height = child_length;
183  }
184 
185  GtkRequisition min_child_req;
186  GtkRequisition nat_child_req;
187  gtk_widget_get_preferred_size (child->widget, &min_child_req, &nat_child_req);
188 
189  child_alloc.width = MAX (child_alloc.width, 1);
190  child_alloc.height = MAX (child_alloc.height, 1);
191 
192  gtk_widget_size_allocate (child->widget, &child_alloc);
193  child_pos += child_length + box->spacing;
194  }
195 }
196 
197 void ygtk_ratio_box_set_child_packing (YGtkRatioBox *box, GtkWidget *child, gfloat ratio)
198 {
199  YGtkRatioBoxChild *child_info;
200  child_info = ygtk_ratio_get_child_info (box, child);
201  if (child_info) {
202  gtk_widget_freeze_child_notify (child);
203  child_info->ratio = ratio;
204  if (gtk_widget_get_visible (child) && gtk_widget_get_visible (GTK_WIDGET(box)))
205  gtk_widget_queue_resize (child);
206 
207  gtk_widget_thaw_child_notify (child);
208  }
209 }
210 
211 void ygtk_ratio_box_set_spacing (YGtkRatioBox *box, guint spacing)
212 {
213  box->spacing = spacing;
214 }
215 
216 static void ygtk_ratio_box_class_init (YGtkRatioBoxClass *klass)
217 {
218  ygtk_ratio_box_parent_class = (GtkContainerClass*) g_type_class_peek_parent (klass);
219 
220  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
221  container_class->add = ygtk_ratio_box_add;
222  container_class->remove = ygtk_ratio_box_remove;
223  container_class->forall = ygtk_ratio_box_forall;
224  container_class->child_type = ygtk_ratio_box_child_type;
225 }
226 
227 //** RatioHBox
228 
229 G_DEFINE_TYPE (YGtkRatioHBox, ygtk_ratio_hbox, YGTK_TYPE_RATIO_BOX)
230 
231 static void ygtk_ratio_hbox_init (YGtkRatioHBox *box)
232 { }
233 
234 static void ygtk_ratio_hbox_get_preferred_size (GtkWidget *widget,
235  GtkRequisition *requisition)
236 { ygtk_ratio_box_get_preferred_size (widget, requisition, GTK_ORIENTATION_HORIZONTAL); }
237 
238 static void
239 ygtk_ratio_hbox_get_preferred_width (GtkWidget *widget,
240  gint *minimal_width,
241  gint *natural_width)
242 {
243  GtkRequisition requisition;
244  ygtk_ratio_hbox_get_preferred_size (widget, &requisition);
245  *minimal_width = *natural_width = requisition.width;
246 }
247 
248 static void
249 ygtk_ratio_hbox_get_preferred_height (GtkWidget *widget,
250  gint *minimal_height,
251  gint *natural_height)
252 {
253  GtkRequisition requisition;
254  ygtk_ratio_hbox_get_preferred_size (widget, &requisition);
255  *minimal_height = *natural_height = requisition.height;
256 }
257 
258 static void ygtk_ratio_hbox_size_allocate (GtkWidget *widget,
259  GtkAllocation *allocation)
260 { ygtk_ratio_box_size_allocate (widget, allocation, GTK_ORIENTATION_HORIZONTAL); }
261 
262 GtkWidget* ygtk_ratio_hbox_new (gint spacing)
263 {
264  YGtkRatioBox* box = (YGtkRatioBox*) g_object_new (YGTK_TYPE_RATIO_HBOX, NULL);
265  box->spacing = spacing;
266  return GTK_WIDGET (box);
267 }
268 
269 static void ygtk_ratio_hbox_class_init (YGtkRatioHBoxClass *klass)
270 {
271  ygtk_ratio_hbox_parent_class = (YGtkRatioBoxClass*) g_type_class_peek_parent (klass);
272 
273  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
274  widget_class->get_preferred_width = ygtk_ratio_hbox_get_preferred_width;
275  widget_class->get_preferred_height = ygtk_ratio_hbox_get_preferred_height;
276 
277  widget_class->size_allocate = ygtk_ratio_hbox_size_allocate;
278 }
279 
280 //** RatioVBox
281 
282 G_DEFINE_TYPE (YGtkRatioVBox, ygtk_ratio_vbox, YGTK_TYPE_RATIO_BOX)
283 
284 static void ygtk_ratio_vbox_init (YGtkRatioVBox *box)
285 { }
286 
287 static void ygtk_ratio_vbox_get_preferred_size (GtkWidget *widget,
288  GtkRequisition *requisition)
289 { ygtk_ratio_box_get_preferred_size (widget, requisition, GTK_ORIENTATION_VERTICAL); }
290 
291 static void
292 ygtk_ratio_vbox_get_preferred_width (GtkWidget *widget,
293  gint *minimal_width,
294  gint *natural_width)
295 {
296  GtkRequisition requisition;
297  ygtk_ratio_vbox_get_preferred_size (widget, &requisition);
298  *minimal_width = *natural_width = requisition.width;
299 }
300 
301 static void
302 ygtk_ratio_vbox_get_preferred_height (GtkWidget *widget,
303  gint *minimal_height,
304  gint *natural_height)
305 {
306  GtkRequisition requisition;
307  ygtk_ratio_vbox_get_preferred_size (widget, &requisition);
308  *minimal_height = *natural_height = requisition.height;
309 }
310 
311 static void ygtk_ratio_vbox_size_allocate (GtkWidget *widget,
312  GtkAllocation *allocation)
313 { ygtk_ratio_box_size_allocate (widget, allocation, GTK_ORIENTATION_VERTICAL); }
314 
315 GtkWidget* ygtk_ratio_vbox_new (gint spacing)
316 {
317  YGtkRatioBox* box = (YGtkRatioBox*) g_object_new (YGTK_TYPE_RATIO_VBOX, NULL);
318  box->spacing = spacing;
319  return GTK_WIDGET (box);
320 }
321 
322 static void ygtk_ratio_vbox_class_init (YGtkRatioVBoxClass *klass)
323 {
324  ygtk_ratio_vbox_parent_class = g_type_class_peek_parent (klass);
325 
326  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
327  widget_class->get_preferred_width = ygtk_ratio_vbox_get_preferred_width;
328  widget_class->get_preferred_height = ygtk_ratio_vbox_get_preferred_height;
329  widget_class->size_allocate = ygtk_ratio_vbox_size_allocate;
330 }
331 
332 //** YGtkAdjSize
333 
334 G_DEFINE_TYPE (YGtkAdjSize, ygtk_adj_size, GTK_TYPE_BIN)
335 
336 static void ygtk_adj_size_init (YGtkAdjSize *adj_size)
337 {
338  gtk_widget_set_has_window(GTK_WIDGET(adj_size), FALSE);
339  gtk_widget_set_redraw_on_allocate (GTK_WIDGET (adj_size), FALSE);
340 }
341 
342 static void ygtk_adj_size_get_preferred_size (
343  GtkWidget *widget, GtkRequisition *requisition)
344 {
345  GtkWidget *child = gtk_bin_get_child(GTK_BIN (widget));
346 
347  requisition->width = requisition->height = 0;
348  if (child && gtk_widget_get_visible((child))) {
349  gtk_widget_get_preferred_size(child, NULL, requisition);
350 
351  guint border = gtk_container_get_border_width(GTK_CONTAINER (widget));
352  requisition->width += border * 2;
353  requisition->height += border * 2;
354 
355  YGtkAdjSize *adj_size = YGTK_ADJ_SIZE (widget);
356  if (adj_size->min_size_cb) {
357  guint min_width, min_height;
358  adj_size->min_size_cb (&min_width, &min_height, adj_size->min_size_data);
359  requisition->width = MAX (requisition->width, min_width);
360  requisition->height = MAX (requisition->height, min_height);
361  }
362  requisition->width = MAX (requisition->width, adj_size->min_width);
363  requisition->height = MAX (requisition->height, adj_size->min_height);
364 
365  if (adj_size->max_width)
366  requisition->width = MIN (requisition->width, adj_size->max_width);
367  if (adj_size->max_height)
368  requisition->height = MIN (requisition->height, adj_size->max_height);
369 
370  if (adj_size->only_expand) {
371  adj_size->min_width = requisition->width;
372  adj_size->min_height = requisition->height;
373  }
374  }
375 }
376 
377 static void
378 ygtk_adj_size_get_preferred_width (GtkWidget *widget,
379  gint *minimal_width,
380  gint *natural_width)
381 {
382  GtkRequisition requisition;
383  ygtk_adj_size_get_preferred_size (widget, &requisition);
384  *minimal_width = *natural_width = requisition.width;
385 }
386 
387 static void
388 ygtk_adj_size_get_preferred_height (GtkWidget *widget,
389  gint *minimal_height,
390  gint *natural_height)
391 {
392  GtkRequisition requisition;
393  ygtk_adj_size_get_preferred_size (widget, &requisition);
394  *minimal_height = *natural_height = requisition.height;
395 }
396 
397 static void ygtk_adj_size_size_allocate (GtkWidget *widget,
398  GtkAllocation *allocation)
399 {
400  GtkWidget *child = gtk_bin_get_child(GTK_BIN (widget));
401  if (child && gtk_widget_get_visible (child)) {
402  GtkAllocation child_alloc = *allocation;
403  guint border = gtk_container_get_border_width(GTK_CONTAINER (widget));
404  child_alloc.x += border;
405  child_alloc.y += border;
406  child_alloc.width -= border * 2;
407  child_alloc.height -= border * 2;
408 
409  GtkRequisition min_child_req;
410  GtkRequisition nat_child_req;
411  gtk_widget_get_preferred_size (child, &min_child_req, &nat_child_req);
412 
413  child_alloc.width = MAX (child_alloc.width, 1);
414  child_alloc.height = MAX (child_alloc.height, 1);
415  gtk_widget_size_allocate (child, &child_alloc);
416  }
417  GTK_WIDGET_CLASS (ygtk_adj_size_parent_class)->size_allocate (widget, allocation);
418 }
419 
420 GtkWidget* ygtk_adj_size_new (void)
421 {
422  return GTK_WIDGET (g_object_new (YGTK_TYPE_ADJ_SIZE, NULL));
423 }
424 
425 void ygtk_adj_size_set_min (YGtkAdjSize *adj_size, guint min_width, guint min_height)
426 {
427  adj_size->min_width = min_width;
428  adj_size->min_height = min_height;
429 }
430 
431 void ygtk_adj_size_set_max (YGtkAdjSize *adj_size, guint max_width, guint max_height)
432 {
433  adj_size->max_width = max_width;
434  adj_size->max_height = max_height;
435 }
436 
437 void ygtk_adj_size_set_min_cb (YGtkAdjSize *adj_size, LimitSizeCb min_size_cb, gpointer data)
438 {
439  adj_size->min_size_cb = min_size_cb;
440  adj_size->min_size_data = data;
441 }
442 
443 void ygtk_adj_size_set_only_expand (YGtkAdjSize *adj_size, gboolean only_expand)
444 {
445  adj_size->only_expand = only_expand;
446 }
447 
448 static void ygtk_adj_size_class_init (YGtkAdjSizeClass *klass)
449 {
450  ygtk_adj_size_parent_class = g_type_class_peek_parent (klass);
451 
452  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
453  widget_class->get_preferred_width = ygtk_adj_size_get_preferred_width;
454  widget_class->get_preferred_height = ygtk_adj_size_get_preferred_height;
455  widget_class->size_allocate = ygtk_adj_size_size_allocate;
456 }
457