No issues found
1 /*
2 * e-shell-taskbar.c
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 /**
23 * SECTION: e-shell-taskbar
24 * @short_description: the bottom of the main window
25 * @include: shell/e-shell-taskbar.h
26 **/
27
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
31
32 #include "e-shell-taskbar.h"
33
34 #include <libebackend/libebackend.h>
35
36 #include <e-shell-view.h>
37 #include <misc/e-activity-proxy.h>
38
39 #define E_SHELL_TASKBAR_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_SHELL_TASKBAR, EShellTaskbarPrivate))
42
43 struct _EShellTaskbarPrivate {
44
45 gpointer shell_view; /* weak pointer */
46
47 /* Keep a reference to the shell backend since
48 * we connect to its "activity-added" signal. */
49 EShellBackend *shell_backend;
50
51 GtkWidget *label;
52 GtkWidget *hbox;
53
54 GHashTable *proxy_table;
55
56 gint fixed_height;
57 };
58
59 enum {
60 PROP_0,
61 PROP_MESSAGE,
62 PROP_SHELL_VIEW
63 };
64
65 G_DEFINE_TYPE_WITH_CODE (
66 EShellTaskbar,
67 e_shell_taskbar,
68 GTK_TYPE_HBOX,
69 G_IMPLEMENT_INTERFACE (
70 E_TYPE_EXTENSIBLE, NULL))
71
72 static void
73 shell_taskbar_weak_notify_cb (EShellTaskbar *shell_taskbar,
74 GObject *where_the_activity_was)
75 {
76 GtkWidget *proxy;
77 GtkContainer *container;
78 GHashTable *proxy_table;
79 GList *children;
80
81 proxy_table = shell_taskbar->priv->proxy_table;
82 proxy = g_hash_table_lookup (proxy_table, where_the_activity_was);
83 g_hash_table_remove (proxy_table, where_the_activity_was);
84 g_return_if_fail (proxy != NULL);
85
86 container = GTK_CONTAINER (shell_taskbar->priv->hbox);
87 gtk_container_remove (container, proxy);
88
89 children = gtk_container_get_children (container);
90
91 if (children == NULL)
92 gtk_widget_hide (GTK_WIDGET (container));
93
94 g_list_free (children);
95 }
96
97 static void
98 shell_taskbar_activity_add (EShellTaskbar *shell_taskbar,
99 EActivity *activity)
100 {
101 GtkBox *box;
102 GtkWidget *proxy;
103 EActivityState state;
104 GHashTable *proxy_table;
105
106 /* Sanity check the activity state. */
107 state = e_activity_get_state (activity);
108 g_return_if_fail (state == E_ACTIVITY_RUNNING);
109
110 /* Make sure it hasn't already been added. */
111 proxy_table = shell_taskbar->priv->proxy_table;
112 proxy = g_hash_table_lookup (proxy_table, activity);
113 g_return_if_fail (proxy == NULL);
114
115 /* Proxy widgets manage their own visibility.
116 * Don't call gtk_widget_show() on it here. */
117 proxy = e_activity_proxy_new (activity);
118 box = GTK_BOX (shell_taskbar->priv->hbox);
119 gtk_box_pack_start (box, proxy, TRUE, TRUE, 0);
120 gtk_box_reorder_child (box, proxy, 0);
121 gtk_widget_show (GTK_WIDGET (box));
122
123 /* The proxy widget also holds a weak reference to the activity,
124 * so the activity should get finalized in the normal course of
125 * operation. When that happens we remove the corresponding
126 * proxy widget from the taskbar. */
127
128 g_object_weak_ref (
129 G_OBJECT (activity), (GWeakNotify)
130 shell_taskbar_weak_notify_cb, shell_taskbar);
131
132 g_hash_table_insert (proxy_table, activity, proxy);
133 }
134
135 static gboolean
136 shell_taskbar_weak_unref (EActivity *activity,
137 EActivityProxy *proxy,
138 EShellTaskbar *shell_taskbar)
139 {
140 g_object_weak_unref (
141 G_OBJECT (activity), (GWeakNotify)
142 shell_taskbar_weak_notify_cb, shell_taskbar);
143
144 return TRUE;
145 }
146
147 static void
148 shell_taskbar_set_shell_view (EShellTaskbar *shell_taskbar,
149 EShellView *shell_view)
150 {
151 g_return_if_fail (shell_taskbar->priv->shell_view == NULL);
152
153 shell_taskbar->priv->shell_view = shell_view;
154
155 g_object_add_weak_pointer (
156 G_OBJECT (shell_view),
157 &shell_taskbar->priv->shell_view);
158 }
159
160 static void
161 shell_taskbar_set_property (GObject *object,
162 guint property_id,
163 const GValue *value,
164 GParamSpec *pspec)
165 {
166 switch (property_id) {
167 case PROP_MESSAGE:
168 e_shell_taskbar_set_message (
169 E_SHELL_TASKBAR (object),
170 g_value_get_string (value));
171 return;
172
173 case PROP_SHELL_VIEW:
174 shell_taskbar_set_shell_view (
175 E_SHELL_TASKBAR (object),
176 g_value_get_object (value));
177 return;
178 }
179
180 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
181 }
182
183 static void
184 shell_taskbar_get_property (GObject *object,
185 guint property_id,
186 GValue *value,
187 GParamSpec *pspec)
188 {
189 switch (property_id) {
190 case PROP_MESSAGE:
191 g_value_set_string (
192 value, e_shell_taskbar_get_message (
193 E_SHELL_TASKBAR (object)));
194 return;
195
196 case PROP_SHELL_VIEW:
197 g_value_set_object (
198 value, e_shell_taskbar_get_shell_view (
199 E_SHELL_TASKBAR (object)));
200 return;
201 }
202
203 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
204 }
205
206 static void
207 shell_taskbar_dispose (GObject *object)
208 {
209 EShellTaskbarPrivate *priv;
210
211 priv = E_SHELL_TASKBAR_GET_PRIVATE (object);
212
213 g_hash_table_foreach_remove (
214 priv->proxy_table, (GHRFunc)
215 shell_taskbar_weak_unref, object);
216
217 if (priv->shell_view != NULL) {
218 g_object_remove_weak_pointer (
219 G_OBJECT (priv->shell_view), &priv->shell_view);
220 priv->shell_view = NULL;
221 }
222
223 if (priv->shell_backend != NULL) {
224 g_signal_handlers_disconnect_matched (
225 priv->shell_backend, G_SIGNAL_MATCH_DATA,
226 0, 0, NULL, NULL, object);
227 g_object_unref (priv->shell_backend);
228 priv->shell_backend = NULL;
229 }
230
231 if (priv->label != NULL) {
232 g_object_unref (priv->label);
233 priv->label = NULL;
234 }
235
236 if (priv->hbox != NULL) {
237 g_object_unref (priv->hbox);
238 priv->hbox = NULL;
239 }
240
241 /* Chain up to parent's dispose() method. */
242 G_OBJECT_CLASS (e_shell_taskbar_parent_class)->dispose (object);
243 }
244
245 static void
246 shell_taskbar_finalize (GObject *object)
247 {
248 EShellTaskbarPrivate *priv;
249
250 priv = E_SHELL_TASKBAR_GET_PRIVATE (object);
251
252 g_hash_table_destroy (priv->proxy_table);
253
254 /* Chain up to parent's finalize() method. */
255 G_OBJECT_CLASS (e_shell_taskbar_parent_class)->finalize (object);
256 }
257
258 static void
259 shell_taskbar_constructed (GObject *object)
260 {
261 EShellView *shell_view;
262 EShellBackend *shell_backend;
263 EShellTaskbar *shell_taskbar;
264
265 shell_taskbar = E_SHELL_TASKBAR (object);
266 shell_view = e_shell_taskbar_get_shell_view (shell_taskbar);
267 shell_backend = e_shell_view_get_shell_backend (shell_view);
268
269 /* Keep a reference to the shell backend so we can
270 * disconnect the signal handler during dispose(). */
271 shell_taskbar->priv->shell_backend = g_object_ref (shell_backend);
272
273 g_signal_connect_swapped (
274 shell_backend, "activity-added",
275 G_CALLBACK (shell_taskbar_activity_add), shell_taskbar);
276
277 e_extensible_load_extensions (E_EXTENSIBLE (object));
278
279 /* Chain up to parent's constructed() method. */
280 G_OBJECT_CLASS (e_shell_taskbar_parent_class)->constructed (object);
281 }
282
283 static void
284 shell_taskbar_size_allocate (GtkWidget *widget,
285 GtkAllocation *allocation)
286 {
287 EShellTaskbar *shell_taskbar;
288 gint fixed_height;
289
290 shell_taskbar = E_SHELL_TASKBAR (widget);
291
292 /* Maximum height allocation sticks. */
293 fixed_height = shell_taskbar->priv->fixed_height;
294 fixed_height = MAX (fixed_height, allocation->height);
295 shell_taskbar->priv->fixed_height = fixed_height;
296
297 /* Chain up to parent's size_allocate() method. */
298 GTK_WIDGET_CLASS (e_shell_taskbar_parent_class)->
299 size_allocate (widget, allocation);
300 }
301
302 static void
303 shell_taskbar_get_preferred_height (GtkWidget *widget,
304 gint *minimum_height,
305 gint *natural_height)
306 {
307 EShellTaskbar *shell_taskbar;
308
309 shell_taskbar = E_SHELL_TASKBAR (widget);
310
311 if (minimum_height != NULL)
312 *minimum_height = shell_taskbar->priv->fixed_height;
313
314 if (natural_height != NULL)
315 *natural_height = shell_taskbar->priv->fixed_height;
316 }
317
318 static void
319 shell_taskbar_get_preferred_width (GtkWidget *widget,
320 gint *minimum_width,
321 gint *natural_width)
322 {
323 /* to never get larger than allocated size (which changes window width) */
324
325 if (minimum_width != NULL)
326 *minimum_width = 1;
327
328 if (natural_width != NULL)
329 *natural_width = 1;
330 }
331
332 static void
333 e_shell_taskbar_class_init (EShellTaskbarClass *class)
334 {
335 GObjectClass *object_class;
336 GtkWidgetClass *widget_class;
337
338 g_type_class_add_private (class, sizeof (EShellTaskbarPrivate));
339
340 object_class = G_OBJECT_CLASS (class);
341 object_class->set_property = shell_taskbar_set_property;
342 object_class->get_property = shell_taskbar_get_property;
343 object_class->dispose = shell_taskbar_dispose;
344 object_class->finalize = shell_taskbar_finalize;
345 object_class->constructed = shell_taskbar_constructed;
346
347 widget_class = GTK_WIDGET_CLASS (class);
348 widget_class->size_allocate = shell_taskbar_size_allocate;
349 widget_class->get_preferred_height = shell_taskbar_get_preferred_height;
350 widget_class->get_preferred_width = shell_taskbar_get_preferred_width;
351
352 /**
353 * EShellTaskbar:message
354 *
355 * The message to display in the taskbar.
356 **/
357 g_object_class_install_property (
358 object_class,
359 PROP_MESSAGE,
360 g_param_spec_string (
361 "message",
362 NULL,
363 NULL,
364 NULL,
365 G_PARAM_READWRITE |
366 G_PARAM_CONSTRUCT));
367
368 /**
369 * EShellTaskbar:shell-view
370 *
371 * The #EShellView to which the taskbar widget belongs.
372 **/
373 g_object_class_install_property (
374 object_class,
375 PROP_SHELL_VIEW,
376 g_param_spec_object (
377 "shell-view",
378 NULL,
379 NULL,
380 E_TYPE_SHELL_VIEW,
381 G_PARAM_READWRITE |
382 G_PARAM_CONSTRUCT_ONLY));
383 }
384
385 static void
386 e_shell_taskbar_init (EShellTaskbar *shell_taskbar)
387 {
388 GtkWidget *widget;
389
390 shell_taskbar->priv = E_SHELL_TASKBAR_GET_PRIVATE (shell_taskbar);
391 shell_taskbar->priv->proxy_table = g_hash_table_new (NULL, NULL);
392
393 gtk_box_set_spacing (GTK_BOX (shell_taskbar), 12);
394
395 widget = gtk_label_new (NULL);
396 gtk_label_set_ellipsize (GTK_LABEL (widget), PANGO_ELLIPSIZE_END);
397 gtk_box_pack_start (GTK_BOX (shell_taskbar), widget, TRUE, TRUE, 0);
398 gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5);
399 shell_taskbar->priv->label = g_object_ref (widget);
400 gtk_widget_hide (widget);
401
402 widget = gtk_hbox_new (FALSE, 3);
403 gtk_box_pack_start (GTK_BOX (shell_taskbar), widget, TRUE, TRUE, 0);
404 shell_taskbar->priv->hbox = g_object_ref (widget);
405 gtk_widget_hide (widget);
406 }
407
408 /**
409 * e_shell_taskbar_new:
410 * @shell_view: an #EShellView
411 *
412 * Creates a new #EShellTaskbar instance belonging to @shell_view.
413 *
414 * Returns: a new #EShellTaskbar instance
415 **/
416 GtkWidget *
417 e_shell_taskbar_new (EShellView *shell_view)
418 {
419 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
420
421 return g_object_new (
422 E_TYPE_SHELL_TASKBAR, "shell-view", shell_view, NULL);
423 }
424
425 /**
426 * e_shell_taskbar_get_shell_view:
427 * @shell_taskbar: an #EShellTaskbar
428 *
429 * Returns the #EShellView that was passed to e_shell_taskbar_new().
430 *
431 * Returns: the #EShellView to which @shell_taskbar belongs
432 **/
433 EShellView *
434 e_shell_taskbar_get_shell_view (EShellTaskbar *shell_taskbar)
435 {
436 g_return_val_if_fail (E_IS_SHELL_TASKBAR (shell_taskbar), NULL);
437
438 return shell_taskbar->priv->shell_view;
439 }
440
441 /**
442 * e_shell_taskbar_get_message:
443 * @shell_taskbar: an #EShellTaskbar
444 *
445 * Returns the message currently shown in the taskbar, or an empty string
446 * if no message is shown. Taskbar messages are used primarily for menu
447 * tooltips.
448 *
449 * Returns: the current taskbar message
450 **/
451 const gchar *
452 e_shell_taskbar_get_message (EShellTaskbar *shell_taskbar)
453 {
454 GtkWidget *label;
455
456 g_return_val_if_fail (E_IS_SHELL_TASKBAR (shell_taskbar), NULL);
457
458 label = shell_taskbar->priv->label;
459
460 return gtk_label_get_text (GTK_LABEL (label));
461 }
462
463 /**
464 * e_shell_taskbar_set_message:
465 * @shell_taskbar: an #EShellTaskbar
466 * @message: the message to show
467 *
468 * Shows a message in the taskbar. If @message is %NULL or an empty string,
469 * the taskbar message is cleared. Taskbar messages are used primarily for
470 * menu tooltips.
471 **/
472 void
473 e_shell_taskbar_set_message (EShellTaskbar *shell_taskbar,
474 const gchar *message)
475 {
476 GtkWidget *label;
477
478 g_return_if_fail (E_IS_SHELL_TASKBAR (shell_taskbar));
479
480 label = shell_taskbar->priv->label;
481 gtk_label_set_text (GTK_LABEL (label), message);
482
483 if (message != NULL && *message != '\0')
484 gtk_widget_show (label);
485 else
486 gtk_widget_hide (label);
487
488 g_object_notify (G_OBJECT (shell_taskbar), "message");
489 }
490
491 /**
492 * e_shell_taskbar_unset_message:
493 * @shell_taskbar: an #EShellTaskbar
494 *
495 * This is equivalent to passing a %NULL message to
496 * e_shell_taskbar_set_message().
497 **/
498 void
499 e_shell_taskbar_unset_message (EShellTaskbar *shell_taskbar)
500 {
501 g_return_if_fail (E_IS_SHELL_TASKBAR (shell_taskbar));
502
503 e_shell_taskbar_set_message (shell_taskbar, NULL);
504 }
505
506 /**
507 * e_shell_taskbar_get_activity_count:
508 * @shell_taskbar: an #EShellTaskbar
509 *
510 * Returns the number of active #EActivity instances being tracked.
511 *
512 * Returns: the number of #EActivity instances
513 **/
514 guint
515 e_shell_taskbar_get_activity_count (EShellTaskbar *shell_taskbar)
516 {
517 g_return_val_if_fail (E_IS_SHELL_TASKBAR (shell_taskbar), 0);
518
519 return g_hash_table_size (shell_taskbar->priv->proxy_table);
520 }