No issues found
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2
3 /* Nautilus - Floating status bar.
4 *
5 * Copyright (C) 2011 Red Hat Inc.
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 *
22 * Authors: Cosimo Cecchi <cosimoc@redhat.com>
23 *
24 */
25
26 #include <config.h>
27
28 #include <string.h>
29
30 #include "nautilus-floating-bar.h"
31
32 struct _NautilusFloatingBarDetails {
33 gchar *primary_label;
34 gchar *details_label;
35
36 GtkWidget *primary_label_widget;
37 GtkWidget *details_label_widget;
38 GtkWidget *spinner;
39 gboolean show_spinner;
40 gboolean is_interactive;
41 };
42
43 enum {
44 PROP_PRIMARY_LABEL = 1,
45 PROP_DETAILS_LABEL,
46 PROP_SHOW_SPINNER,
47 NUM_PROPERTIES
48 };
49
50 enum {
51 ACTION,
52 NUM_SIGNALS
53 };
54
55 static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
56 static guint signals[NUM_SIGNALS] = { 0, };
57
58 G_DEFINE_TYPE (NautilusFloatingBar, nautilus_floating_bar,
59 GTK_TYPE_BOX);
60
61 static void
62 action_button_clicked_cb (GtkButton *button,
63 NautilusFloatingBar *self)
64 {
65 gint action_id;
66
67 action_id = GPOINTER_TO_INT
68 (g_object_get_data (G_OBJECT (button), "action-id"));
69
70 g_signal_emit (self, signals[ACTION], 0, action_id);
71 }
72
73 static void
74 nautilus_floating_bar_finalize (GObject *obj)
75 {
76 NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (obj);
77
78 g_free (self->priv->primary_label);
79 g_free (self->priv->details_label);
80
81 G_OBJECT_CLASS (nautilus_floating_bar_parent_class)->finalize (obj);
82 }
83
84 static void
85 nautilus_floating_bar_get_property (GObject *object,
86 guint property_id,
87 GValue *value,
88 GParamSpec *pspec)
89 {
90 NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (object);
91
92 switch (property_id) {
93 case PROP_PRIMARY_LABEL:
94 g_value_set_string (value, self->priv->primary_label);
95 break;
96 case PROP_DETAILS_LABEL:
97 g_value_set_string (value, self->priv->details_label);
98 break;
99 case PROP_SHOW_SPINNER:
100 g_value_set_boolean (value, self->priv->show_spinner);
101 break;
102 default:
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
104 break;
105 }
106 }
107
108 static void
109 nautilus_floating_bar_set_property (GObject *object,
110 guint property_id,
111 const GValue *value,
112 GParamSpec *pspec)
113 {
114 NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (object);
115
116 switch (property_id) {
117 case PROP_PRIMARY_LABEL:
118 nautilus_floating_bar_set_primary_label (self, g_value_get_string (value));
119 break;
120 case PROP_DETAILS_LABEL:
121 nautilus_floating_bar_set_details_label (self, g_value_get_string (value));
122 break;
123 case PROP_SHOW_SPINNER:
124 nautilus_floating_bar_set_show_spinner (self, g_value_get_boolean (value));
125 break;
126 default:
127 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
128 break;
129 }
130 }
131
132 static void
133 update_labels (NautilusFloatingBar *self)
134 {
135 gboolean primary_visible, details_visible;
136
137 primary_visible = (self->priv->primary_label != NULL) &&
138 (strlen (self->priv->primary_label) > 0);
139 details_visible = (self->priv->details_label != NULL) &&
140 (strlen (self->priv->details_label) > 0);
141
142 gtk_label_set_text (GTK_LABEL (self->priv->primary_label_widget),
143 self->priv->primary_label);
144 gtk_widget_set_visible (self->priv->primary_label_widget, primary_visible);
145
146 gtk_label_set_text (GTK_LABEL (self->priv->details_label_widget),
147 self->priv->details_label);
148 gtk_widget_set_visible (self->priv->details_label_widget, details_visible);
149 }
150
151 static gboolean
152 overlay_enter_notify_cb (GtkWidget *parent,
153 GdkEventCrossing *event,
154 gpointer user_data)
155 {
156 GtkWidget *widget = user_data;
157
158 if (event->window != gtk_widget_get_window (widget)) {
159 return FALSE;
160 }
161
162 if (NAUTILUS_FLOATING_BAR (widget)->priv->is_interactive) {
163 return FALSE;
164 }
165
166 if (gtk_widget_get_halign (widget) == GTK_ALIGN_START) {
167 gtk_widget_set_halign (widget, GTK_ALIGN_END);
168 } else {
169 gtk_widget_set_halign (widget, GTK_ALIGN_START);
170 }
171
172 gtk_widget_queue_resize (widget);
173
174 return FALSE;
175 }
176
177 static void
178 nautilus_floating_bar_parent_set (GtkWidget *widget,
179 GtkWidget *old_parent)
180 {
181 GtkWidget *parent;
182
183 parent = gtk_widget_get_parent (widget);
184
185 if (old_parent != NULL) {
186 g_signal_handlers_disconnect_by_func (old_parent,
187 overlay_enter_notify_cb, widget);
188 }
189
190 if (parent != NULL) {
191 g_signal_connect (parent, "enter-notify-event",
192 G_CALLBACK (overlay_enter_notify_cb), widget);
193 }
194 }
195
196 static void
197 nautilus_floating_bar_show (GtkWidget *widget)
198 {
199 NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
200
201 GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->show (widget);
202
203 if (self->priv->show_spinner) {
204 gtk_spinner_start (GTK_SPINNER (self->priv->spinner));
205 }
206 }
207
208 static void
209 nautilus_floating_bar_hide (GtkWidget *widget)
210 {
211 NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (widget);
212
213 GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->hide (widget);
214
215 gtk_spinner_stop (GTK_SPINNER (self->priv->spinner));
216 }
217
218 static gboolean
219 nautilus_floating_bar_draw (GtkWidget *widget,
220 cairo_t *cr)
221 {
222 GtkStyleContext *context;
223
224 context = gtk_widget_get_style_context (widget);
225
226 gtk_style_context_save (context);
227 gtk_style_context_set_state (context, gtk_widget_get_state_flags (widget));
228
229 gtk_render_background (context, cr, 0, 0,
230 gtk_widget_get_allocated_width (widget),
231 gtk_widget_get_allocated_height (widget));
232
233 gtk_render_frame (context, cr, 0, 0,
234 gtk_widget_get_allocated_width (widget),
235 gtk_widget_get_allocated_height (widget));
236
237 gtk_style_context_restore (context);
238
239 return GTK_WIDGET_CLASS (nautilus_floating_bar_parent_class)->draw (widget, cr);;
240 }
241
242 static void
243 nautilus_floating_bar_constructed (GObject *obj)
244 {
245 NautilusFloatingBar *self = NAUTILUS_FLOATING_BAR (obj);
246 GtkWidget *w, *box, *labels_box;
247
248 G_OBJECT_CLASS (nautilus_floating_bar_parent_class)->constructed (obj);
249
250 box = GTK_WIDGET (obj);
251
252 w = gtk_spinner_new ();
253 gtk_box_pack_start (GTK_BOX (box), w, FALSE, FALSE, 0);
254 gtk_widget_set_visible (w, self->priv->show_spinner);
255 self->priv->spinner = w;
256
257 gtk_widget_set_size_request (w, 16, 16);
258 gtk_widget_set_margin_left (w, 8);
259
260 labels_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
261 gtk_box_pack_start (GTK_BOX (box), labels_box, TRUE, TRUE, 0);
262 g_object_set (labels_box,
263 "margin-top", 2,
264 "margin-bottom", 2,
265 "margin-left", 12,
266 "margin-right", 12,
267 NULL);
268 gtk_widget_show (labels_box);
269
270 w = gtk_label_new (NULL);
271 gtk_label_set_ellipsize (GTK_LABEL (w), PANGO_ELLIPSIZE_MIDDLE);
272 gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
273 gtk_container_add (GTK_CONTAINER (labels_box), w);
274 self->priv->primary_label_widget = w;
275 gtk_widget_show (w);
276
277 w = gtk_label_new (NULL);
278 gtk_label_set_single_line_mode (GTK_LABEL (w), TRUE);
279 gtk_container_add (GTK_CONTAINER (labels_box), w);
280 self->priv->details_label_widget = w;
281 gtk_widget_show (w);
282 }
283
284 static void
285 nautilus_floating_bar_init (NautilusFloatingBar *self)
286 {
287 GtkStyleContext *context;
288
289 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, NAUTILUS_TYPE_FLOATING_BAR,
290 NautilusFloatingBarDetails);
291
292 context = gtk_widget_get_style_context (GTK_WIDGET (self));
293 gtk_style_context_add_class (context, "floating-bar");
294 }
295
296 static void
297 nautilus_floating_bar_class_init (NautilusFloatingBarClass *klass)
298 {
299 GObjectClass *oclass = G_OBJECT_CLASS (klass);
300 GtkWidgetClass *wclass = GTK_WIDGET_CLASS (klass);
301
302 oclass->constructed = nautilus_floating_bar_constructed;
303 oclass->set_property = nautilus_floating_bar_set_property;
304 oclass->get_property = nautilus_floating_bar_get_property;
305 oclass->finalize = nautilus_floating_bar_finalize;
306
307 wclass->draw = nautilus_floating_bar_draw;
308 wclass->show = nautilus_floating_bar_show;
309 wclass->hide = nautilus_floating_bar_hide;
310 wclass->parent_set = nautilus_floating_bar_parent_set;
311
312 properties[PROP_PRIMARY_LABEL] =
313 g_param_spec_string ("primary-label",
314 "Bar's primary label",
315 "Primary label displayed by the bar",
316 NULL,
317 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
318 properties[PROP_DETAILS_LABEL] =
319 g_param_spec_string ("details-label",
320 "Bar's details label",
321 "Details label displayed by the bar",
322 NULL,
323 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
324 properties[PROP_SHOW_SPINNER] =
325 g_param_spec_boolean ("show-spinner",
326 "Show spinner",
327 "Whether a spinner should be shown in the floating bar",
328 FALSE,
329 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
330
331 signals[ACTION] =
332 g_signal_new ("action",
333 G_TYPE_FROM_CLASS (klass),
334 G_SIGNAL_RUN_LAST,
335 0, NULL, NULL,
336 g_cclosure_marshal_VOID__INT,
337 G_TYPE_NONE, 1,
338 G_TYPE_INT);
339
340 g_type_class_add_private (klass, sizeof (NautilusFloatingBarDetails));
341 g_object_class_install_properties (oclass, NUM_PROPERTIES, properties);
342 }
343
344 void
345 nautilus_floating_bar_set_primary_label (NautilusFloatingBar *self,
346 const gchar *label)
347 {
348 if (g_strcmp0 (self->priv->primary_label, label) != 0) {
349 g_free (self->priv->primary_label);
350 self->priv->primary_label = g_strdup (label);
351
352 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_PRIMARY_LABEL]);
353
354 update_labels (self);
355 }
356 }
357
358 void
359 nautilus_floating_bar_set_details_label (NautilusFloatingBar *self,
360 const gchar *label)
361 {
362 if (g_strcmp0 (self->priv->details_label, label) != 0) {
363 g_free (self->priv->details_label);
364 self->priv->details_label = g_strdup (label);
365
366 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_DETAILS_LABEL]);
367
368 update_labels (self);
369 }
370 }
371
372 void
373 nautilus_floating_bar_set_labels (NautilusFloatingBar *self,
374 const gchar *primary_label,
375 const gchar *details_label)
376 {
377 nautilus_floating_bar_set_primary_label (self, primary_label);
378 nautilus_floating_bar_set_details_label (self, details_label);
379 }
380
381 void
382 nautilus_floating_bar_set_show_spinner (NautilusFloatingBar *self,
383 gboolean show_spinner)
384 {
385 if (self->priv->show_spinner != show_spinner) {
386 self->priv->show_spinner = show_spinner;
387 gtk_widget_set_visible (self->priv->spinner,
388 show_spinner);
389
390 g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SHOW_SPINNER]);
391 }
392 }
393
394 GtkWidget *
395 nautilus_floating_bar_new (const gchar *primary_label,
396 const gchar *details_label,
397 gboolean show_spinner)
398 {
399 return g_object_new (NAUTILUS_TYPE_FLOATING_BAR,
400 "primary-label", primary_label,
401 "details-label", details_label,
402 "show-spinner", show_spinner,
403 "orientation", GTK_ORIENTATION_HORIZONTAL,
404 "spacing", 8,
405 NULL);
406 }
407
408 void
409 nautilus_floating_bar_add_action (NautilusFloatingBar *self,
410 const gchar *stock_id,
411 gint action_id)
412 {
413 GtkWidget *w, *button;
414
415 w = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
416 gtk_widget_show (w);
417
418 button = gtk_button_new ();
419 gtk_button_set_image (GTK_BUTTON (button), w);
420 gtk_box_pack_end (GTK_BOX (self), button, FALSE, FALSE, 0);
421 gtk_widget_show (button);
422
423 g_object_set_data (G_OBJECT (button), "action-id",
424 GINT_TO_POINTER (action_id));
425
426 g_signal_connect (button, "clicked",
427 G_CALLBACK (action_button_clicked_cb), self);
428
429 self->priv->is_interactive = TRUE;
430 }
431
432 void
433 nautilus_floating_bar_cleanup_actions (NautilusFloatingBar *self)
434 {
435 GtkWidget *widget;
436 GList *children, *l;
437 gpointer data;
438
439 children = gtk_container_get_children (GTK_CONTAINER (self));
440 l = children;
441
442 while (l != NULL) {
443 widget = l->data;
444 data = g_object_get_data (G_OBJECT (widget), "action-id");
445 l = l->next;
446
447 if (data != NULL) {
448 /* destroy this */
449 gtk_widget_destroy (widget);
450 }
451 }
452
453 g_list_free (children);
454
455 self->priv->is_interactive = FALSE;
456 }