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 
144  // gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
145  gtk_widget_set_halign (label, GTK_ALIGN_START);
146  gtk_widget_set_valign (label, GTK_ALIGN_START);
147 
148  int mark_width = 10;
149  pango_layout_get_pixel_size (steps->check_mark_layout, &mark_width, NULL);
150 
151  // gtk_misc_set_padding (GTK_MISC (label), mark_width+12, 0);
152  gtk_widget_set_margin_start (label, mark_width+12);
153  gtk_widget_set_margin_end (label, mark_width+12);
154  gtk_widget_set_margin_top (label, 0);
155  gtk_widget_set_margin_bottom (label, 0);
156 
157  gtk_widget_show (label);
158  gtk_box_pack_start (GTK_BOX (steps), label, FALSE, TRUE, 0);
159  return ygtk_steps_total (steps)-1;
160 }
161 
162 void ygtk_steps_append_heading (YGtkSteps *steps, const gchar *heading)
163 {
164  GtkWidget *label = gtk_label_new (heading);
165  GdkRGBA black = { 0.0, 0.0, 0.0, 1.0 };
166  gtk_widget_override_color (label, GTK_STATE_NORMAL, &black);
167  g_object_set_data (G_OBJECT (label), "is-header", GINT_TO_POINTER (1));
168 
169  //gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
170  gtk_widget_set_halign (label, GTK_ALIGN_START);
171  gtk_widget_set_valign (label, GTK_ALIGN_START);
172 
173  PangoAttrList *attrbs = pango_attr_list_new();
174  pango_attr_list_insert (attrbs, pango_attr_weight_new (PANGO_WEIGHT_BOLD));
175  pango_attr_list_insert (attrbs, pango_attr_scale_new (PANGO_SCALE_LARGE));
176  gtk_label_set_attributes (GTK_LABEL (label), attrbs);
177  pango_attr_list_unref (attrbs);
178 
179  gtk_widget_show (label);
180  gtk_box_pack_start (GTK_BOX (steps), label, FALSE, TRUE, 6);
181 }
182 
183 static gboolean current_mark_animation_cb (void *steps_ptr)
184 {
185  YGtkSteps *steps = steps_ptr;
186 
187  // should use gtk_widget_queue_draw_area (widget, x, y, w, h)...
188  gtk_widget_queue_draw (GTK_WIDGET (steps));
189 
190  if (++steps->current_mark_frame == CURRENT_MARK_FRAMES_NB) {
191  steps->current_mark_frame = 0;
192  return FALSE;
193  }
194  return TRUE;
195 }
196 
197 void ygtk_steps_set_current (YGtkSteps *steps, gint step)
198 {
199  gint old_step = steps->current_step;
200  steps->current_step = step;
201 
202  // update step icons
203  if (old_step != step) {
204  ygtk_step_update_layout (steps, old_step);
205  ygtk_step_update_layout (steps, step);
206  }
207 
208  if (step != -1 && step != old_step) {
209  steps->current_mark_frame = 0;
210  steps->current_mark_timeout_id = g_timeout_add
211  (CURRENT_MARK_ANIMATION_TIME / CURRENT_MARK_FRAMES_NB,
212  current_mark_animation_cb, steps);
213  }
214 }
215 
216 gint ygtk_steps_total (YGtkSteps *steps)
217 {
218  GList *children = gtk_container_get_children (GTK_CONTAINER (steps));
219  int steps_nb = g_list_length (children);
220  g_list_free (children);
221  return steps_nb;
222 }
223 
224 const gchar *ygtk_steps_get_nth_label (YGtkSteps *steps, gint n)
225 {
226  if (n < 0) return NULL;
227  GtkWidget *step;
228  GList *children = gtk_container_get_children (GTK_CONTAINER (steps));
229  step = g_list_nth_data (children, n);
230  g_list_free (children);
231  if (step)
232  return gtk_label_get_text (GTK_LABEL (step));
233  return NULL;
234 }
235 
236 void ygtk_steps_clear (YGtkSteps *steps)
237 {
238  GList *children = gtk_container_get_children (GTK_CONTAINER (steps)), *i;
239  for (i = children; i; i = i->next)
240  gtk_container_remove (GTK_CONTAINER (steps), (GtkWidget *) i->data);
241  g_list_free (children);
242 }
243 
244 static void ygtk_steps_class_init (YGtkStepsClass *klass)
245 {
246  ygtk_steps_parent_class = g_type_class_peek_parent (klass);
247 
248  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS (klass);
249  widget_class->draw = ygtk_steps_draw;
250  widget_class->destroy = ygtk_steps_destroy;
251 }
252