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