libyui-gtk  2.44.9
ygtksteps.c
1 /********************************************************************
2  * YaST2-GTK - http://en.opensuse.org/YaST2-GTK *
3  ********************************************************************/
4 
5 /* YGtkSteps widget */
6 // check the header file for information about this widget
7 
8 /*
9  Textdomain "gtk"
10  */
11 
12 #include <yui/Libyui_config.h>
13 #include "ygtksteps.h"
14 #include <gtk/gtk.h>
15 #define YGI18N_C
16 #include "YGi18n.h"
17 
18 #define CURRENT_MARK_ANIMATION_TIME 250
19 #define CURRENT_MARK_ANIMATION_OFFSET 3
20 #define CURRENT_MARK_FRAMES_NB (CURRENT_MARK_ANIMATION_OFFSET*2)
21 
22 G_DEFINE_TYPE (YGtkSteps, ygtk_steps, GTK_TYPE_BOX)
23 
24 static void ygtk_steps_init (YGtkSteps *steps)
25 {
26  gtk_box_set_spacing (GTK_BOX (steps), 8);
27  gtk_orientable_set_orientation (GTK_ORIENTABLE (steps), GTK_ORIENTATION_VERTICAL);
28  gtk_container_set_border_width (GTK_CONTAINER (steps), 4);
29 
30  const gchar *check = "\u2714", *current = "\u25b6", *todo = "\u26ab";
31  if (gtk_widget_get_default_direction() == GTK_TEXT_DIR_RTL)
32  current = "\u25c0";
33  PangoContext *context = gtk_widget_get_pango_context (GTK_WIDGET (steps));
34  steps->check_mark_layout = pango_layout_new (context);
35  steps->current_mark_layout = pango_layout_new (context);
36  steps->todo_mark_layout = pango_layout_new (context);
37  pango_layout_set_text (steps->check_mark_layout, check, -1);
38  pango_layout_set_text (steps->current_mark_layout, current, -1);
39  pango_layout_set_text (steps->todo_mark_layout, todo, -1);
40  steps->current_mark_timeout_id = steps->current_mark_frame = 0;
41 }
42 
43 static void ygtk_steps_destroy (GtkWidget *widget)
44 {
45  YGtkSteps *steps = YGTK_STEPS (widget);
46  if (steps->current_mark_timeout_id) {
47  g_source_remove (steps->current_mark_timeout_id);
48  steps->current_mark_timeout_id = 0;
49  }
50  if (steps->check_mark_layout)
51  g_object_unref (steps->check_mark_layout);
52  steps->check_mark_layout = NULL;
53  if (steps->current_mark_layout)
54  g_object_unref (steps->current_mark_layout);
55  if (steps->todo_mark_layout)
56  g_object_unref (steps->todo_mark_layout);
57  steps->todo_mark_layout = NULL;
58 
59  GTK_WIDGET_CLASS (ygtk_steps_parent_class)->destroy(widget);
60 }
61 
62 static void ygtk_step_update_layout (YGtkSteps *steps, gint step)
63 {
64  if (step < 0) return;
65  gboolean bold = steps->current_step == step;
66  GList *children = gtk_container_get_children (GTK_CONTAINER (steps));
67  GtkWidget *label = (GtkWidget *) g_list_nth_data (children, step);
68  if (g_object_get_data (G_OBJECT (label), "is-header"))
69  return;
70  if (bold) {
71  PangoAttrList *attrbs = pango_attr_list_new();
72  pango_attr_list_insert (attrbs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
73  gtk_label_set_attributes (GTK_LABEL (label), attrbs);
74  pango_attr_list_unref (attrbs);
75  atk_object_set_description (gtk_widget_get_accessible (label), _("Current step"));
76  }
77  else {
78  gtk_label_set_attributes (GTK_LABEL (label), NULL);
79  atk_object_set_description (gtk_widget_get_accessible (label), "");
80  }
81  g_list_free (children);
82 }
83 
84 static gboolean ygtk_steps_draw (GtkWidget *widget, cairo_t *cr)
85 {
86  GTK_WIDGET_CLASS (ygtk_steps_parent_class)->draw(widget, cr);
87 
88  YGtkSteps *steps = YGTK_STEPS (widget);
89  gboolean reverse = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
90  GList *children = gtk_container_get_children (GTK_CONTAINER (widget)), *i;
91 
92  cairo_set_source_rgb (cr, 0, 0, 0);
93  int n = 0;
94  for (i = children; i; i = i->next, n++) {
95  GtkWidget *label = i->data;
96  GtkAllocation alloc;
97  gtk_widget_get_allocation(label, &alloc);
98 
99  if (g_object_get_data (G_OBJECT (label), "is-header"))
100  continue;
101  PangoLayout *layout;
102  if (n < steps->current_step)
103  layout = steps->check_mark_layout;
104  else if (n == steps->current_step)
105  layout = steps->current_mark_layout;
106  else //if (n > steps->current_step)
107  layout = steps->todo_mark_layout;
108  int x = alloc.x, y = alloc.y;
109  if (reverse) {
110  PangoRectangle rect;
111  pango_layout_get_pixel_extents (layout, NULL, &rect);
112  x += alloc.width - rect.width - 4;
113  }
114  else
115  x += 4;
116  if (n == steps->current_step) {
117  int offset;
118  if (steps->current_mark_frame < CURRENT_MARK_FRAMES_NB/2)
119  offset = steps->current_mark_frame * CURRENT_MARK_ANIMATION_OFFSET;
120  else
121  offset = (CURRENT_MARK_FRAMES_NB - steps->current_mark_frame) *
122  CURRENT_MARK_ANIMATION_OFFSET;
123  x += offset * (reverse ? 1 : -1);
124  }
125 
126  cairo_move_to (cr, x, y);
127  pango_cairo_show_layout (cr, layout);
128  }
129  g_list_free (children);
130  return FALSE;
131 }
132 
133 GtkWidget* ygtk_steps_new (void)
134 {
135  return g_object_new (YGTK_TYPE_STEPS, NULL);
136 }
137 
138 gint ygtk_steps_append (YGtkSteps *steps, const gchar *text)
139 {
140  GtkWidget *label = gtk_label_new (text);
141  GdkRGBA black = { 0.0, 0.0, 0.0, 1.0 };
142  gtk_widget_override_color (label, GTK_STATE_NORMAL, &black);
143  gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
144  int mark_width = 10;
145  pango_layout_get_pixel_size (steps->check_mark_layout, &mark_width, NULL);
146  gtk_misc_set_padding (GTK_MISC (label), mark_width+12, 0);
147  gtk_widget_show (label);
148  gtk_box_pack_start (GTK_BOX (steps), label, FALSE, TRUE, 0);
149  return ygtk_steps_total (steps)-1;
150 }
151 
152 void ygtk_steps_append_heading (YGtkSteps *steps, const gchar *heading)
153 {
154  GtkWidget *label = gtk_label_new (heading);
155  GdkRGBA black = { 0.0, 0.0, 0.0, 1.0 };
156  gtk_widget_override_color (label, GTK_STATE_NORMAL, &black);
157  g_object_set_data (G_OBJECT (label), "is-header", GINT_TO_POINTER (1));
158  gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
159 
160  PangoAttrList *attrbs = pango_attr_list_new();
161  pango_attr_list_insert (attrbs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
162  pango_attr_list_insert (attrbs, pango_attr_scale_new (PANGO_SCALE_LARGE));
163  gtk_label_set_attributes (GTK_LABEL (label), attrbs);
164  pango_attr_list_unref (attrbs);
165 
166  gtk_widget_show (label);
167  gtk_box_pack_start (GTK_BOX (steps), label, FALSE, TRUE, 6);
168 }
169 
170 static gboolean current_mark_animation_cb (void *steps_ptr)
171 {
172  YGtkSteps *steps = steps_ptr;
173 
174  // should use gtk_widget_queue_draw_area (widget, x, y, w, h)...
175  gtk_widget_queue_draw (GTK_WIDGET (steps));
176 
177  if (++steps->current_mark_frame == CURRENT_MARK_FRAMES_NB) {
178  steps->current_mark_frame = 0;
179  return FALSE;
180  }
181  return TRUE;
182 }
183 
184 void ygtk_steps_set_current (YGtkSteps *steps, gint step)
185 {
186  gint old_step = steps->current_step;
187  steps->current_step = step;
188 
189  // update step icons
190  if (old_step != step) {
191  ygtk_step_update_layout (steps, old_step);
192  ygtk_step_update_layout (steps, step);
193  }
194 
195  if (step != -1 && step != old_step) {
196  steps->current_mark_frame = 0;
197  steps->current_mark_timeout_id = g_timeout_add
198  (CURRENT_MARK_ANIMATION_TIME / CURRENT_MARK_FRAMES_NB,
199  current_mark_animation_cb, steps);
200  }
201 }
202 
203 gint ygtk_steps_total (YGtkSteps *steps)
204 {
205  GList *children = gtk_container_get_children (GTK_CONTAINER (steps));
206  int steps_nb = g_list_length (children);
207  g_list_free (children);
208  return steps_nb;
209 }
210 
211 const gchar *ygtk_steps_get_nth_label (YGtkSteps *steps, gint n)
212 {
213  if (n < 0) return NULL;
214  GtkWidget *step;
215  GList *children = gtk_container_get_children (GTK_CONTAINER (steps));
216  step = g_list_nth_data (children, n);
217  g_list_free (children);
218  if (step)
219  return gtk_label_get_text (GTK_LABEL (step));
220  return NULL;
221 }
222 
223 void ygtk_steps_clear (YGtkSteps *steps)
224 {
225  GList *children = gtk_container_get_children (GTK_CONTAINER (steps)), *i;
226  for (i = children; i; i = i->next)
227  gtk_container_remove (GTK_CONTAINER (steps), (GtkWidget *) i->data);
228  g_list_free (children);
229 }
230 
231 static void ygtk_steps_class_init (YGtkStepsClass *klass)
232 {
233  ygtk_steps_parent_class = g_type_class_peek_parent (klass);
234 
235  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
236  widget_class->draw = ygtk_steps_draw;
237  widget_class->destroy = ygtk_steps_destroy;
238 }
239