1 /* -*- mode: C; c-basic-offset: 4 -*-
2 * Drive Mount Applet
3 * Copyright (c) 2004 Canonical Ltd
4 * Copyright 2008 Pierre Ossman
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 * Author:
21 * James Henstridge <jamesh@canonical.com>
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <gio/gio.h>
29 #include "drive-list.h"
30 #include "drive-button.h"
31 #include <glib/gi18n.h>
32
33 G_DEFINE_TYPE (DriveList, drive_list, GTK_TYPE_GRID);
34
35 static GVolumeMonitor *volume_monitor = NULL;
36
37 static void drive_list_finalize (GObject *object);
38 static void drive_list_dispose (GObject *object);
39 static void drive_list_add (GtkContainer *container, GtkWidget *child);
40 static void drive_list_remove (GtkContainer *container, GtkWidget *child);
41
42 static void mount_added (GVolumeMonitor *monitor, GMount *mount, DriveList *self);
43 static void mount_changed (GVolumeMonitor *monitor, GMount *mount, DriveList *self);
44 static void mount_removed (GVolumeMonitor *monitor, GMount *mount, DriveList *self);
45 static void volume_added (GVolumeMonitor *monitor, GVolume *volume, DriveList *self);
46 static void volume_changed (GVolumeMonitor *monitor, GVolume *volume, DriveList *self);
47 static void volume_removed (GVolumeMonitor *monitor, GVolume *volume, DriveList *self);
48 static void add_volume (DriveList *self, GVolume *volume);
49 static void remove_volume (DriveList *self, GVolume *volume);
50 static void add_mount (DriveList *self, GMount *mount);
51 static void remove_mount (DriveList *self, GMount *mount);
52 static void queue_relayout (DriveList *self);
53
54 static void
drive_list_class_init(DriveListClass * class)55 drive_list_class_init (DriveListClass *class)
56 {
57 G_OBJECT_CLASS (class)->finalize = drive_list_finalize;
58 G_OBJECT_CLASS (class)->dispose = drive_list_dispose;
59 GTK_CONTAINER_CLASS (class)->add = drive_list_add;
60 GTK_CONTAINER_CLASS (class)->remove = drive_list_remove;
61 }
62
63 static void
drive_list_init(DriveList * self)64 drive_list_init (DriveList *self)
65 {
66 GList *volumes, *mounts, *tmp;
67
68 gtk_grid_set_column_homogeneous (GTK_GRID (self), TRUE);
69 gtk_grid_set_row_homogeneous (GTK_GRID (self), TRUE);
70
71 self->volumes = g_hash_table_new (NULL, NULL);
72 self->mounts = g_hash_table_new (NULL, NULL);
73 self->orientation = GTK_ORIENTATION_HORIZONTAL;
74 self->layout_tag = 0;
75 self->settings = g_settings_new ("org.mate.drivemount");
76 self->icon_size = 24;
77 self->relief = GTK_RELIEF_NORMAL;
78
79 g_signal_connect (self->settings,
80 "changed::drivemount-checkmark-color",
81 G_CALLBACK (settings_color_changed),
82 self);
83
84 /* listen for drive connects/disconnects, and add
85 * currently connected drives. */
86 self->count = 0;
87 if (!volume_monitor)
88 volume_monitor = g_volume_monitor_get ();
89
90 g_signal_connect_object (volume_monitor, "mount_added",
91 G_CALLBACK (mount_added), self, 0);
92 g_signal_connect_object (volume_monitor, "mount_changed",
93 G_CALLBACK (mount_changed), self, 0);
94 g_signal_connect_object (volume_monitor, "mount_removed",
95 G_CALLBACK (mount_removed), self, 0);
96 g_signal_connect_object (volume_monitor, "volume_added",
97 G_CALLBACK (volume_added), self, 0);
98 g_signal_connect_object (volume_monitor, "volume_changed",
99 G_CALLBACK (volume_changed), self, 0);
100 g_signal_connect_object (volume_monitor, "volume_removed",
101 G_CALLBACK (volume_removed), self, 0);
102 volumes = g_volume_monitor_get_volumes (volume_monitor);
103 for (tmp = volumes; tmp != NULL; tmp = tmp->next) {
104 GVolume *volume = tmp->data;
105 add_volume (self, volume);
106 g_object_unref (volume);
107 self->count++;
108 }
109 g_list_free (volumes);
110
111 mounts = g_volume_monitor_get_mounts (volume_monitor);
112 for (tmp = mounts; tmp != NULL; tmp = tmp->next) {
113 GMount *mount = tmp->data;
114 add_mount (self, mount);
115 g_object_unref (mount);
116 self->count++;
117 }
118 self->dummy = drive_button_new (NULL);
119 gtk_button_set_relief (GTK_BUTTON (self->dummy), self->relief);
120 drive_button_set_size (DRIVE_BUTTON (self->dummy), self->icon_size);
121
122 if (self->count == 0) {
123 gtk_container_add (GTK_CONTAINER (self), self->dummy);
124 queue_relayout (self);
125 drive_button_queue_update (DRIVE_BUTTON (self->dummy));
126 }
127 g_list_free (mounts);
128 }
129
130 GtkWidget *
drive_list_new(void)131 drive_list_new (void)
132 {
133 return g_object_new (DRIVE_TYPE_LIST, NULL);
134 }
135
136 static void
drive_list_finalize(GObject * object)137 drive_list_finalize (GObject *object)
138 {
139 DriveList *self = DRIVE_LIST (object);
140
141 g_hash_table_destroy (self->volumes);
142 g_hash_table_destroy (self->mounts);
143 g_object_unref (self->settings);
144
145 if (G_OBJECT_CLASS (drive_list_parent_class)->finalize)
146 (* G_OBJECT_CLASS (drive_list_parent_class)->finalize) (object);
147 }
148
149 static void
drive_list_dispose(GObject * object)150 drive_list_dispose (GObject *object)
151 {
152 DriveList *self = DRIVE_LIST (object);
153
154 g_signal_handlers_disconnect_by_func (volume_monitor,
155 G_CALLBACK (mount_added), self);
156 g_signal_handlers_disconnect_by_func (volume_monitor,
157 G_CALLBACK (mount_changed), self);
158 g_signal_handlers_disconnect_by_func (volume_monitor,
159 G_CALLBACK (mount_removed), self);
160 g_signal_handlers_disconnect_by_func (volume_monitor,
161 G_CALLBACK (volume_added), self);
162 g_signal_handlers_disconnect_by_func (volume_monitor,
163 G_CALLBACK (volume_changed), self);
164 g_signal_handlers_disconnect_by_func (volume_monitor,
165 G_CALLBACK (volume_removed), self);
166
167 if (self->layout_tag)
168 g_source_remove (self->layout_tag);
169 self->layout_tag = 0;
170
171 if (G_OBJECT_CLASS (drive_list_parent_class)->dispose)
172 (* G_OBJECT_CLASS (drive_list_parent_class)->dispose) (object);
173 }
174
175 static void
drive_list_add(GtkContainer * container,GtkWidget * child)176 drive_list_add (GtkContainer *container,
177 GtkWidget *child)
178 {
179 DriveList *self;
180 DriveButton *button;
181
182 g_return_if_fail (DRIVE_IS_LIST (container));
183 g_return_if_fail (DRIVE_IS_BUTTON (child));
184
185 if (GTK_CONTAINER_CLASS (drive_list_parent_class)->add)
186 (* GTK_CONTAINER_CLASS (drive_list_parent_class)->add) (container, child);
187
188 self = DRIVE_LIST (container);
189 button = DRIVE_BUTTON (child);
190 if (button->volume)
191 g_hash_table_insert (self->volumes, button->volume, button);
192 else
193 g_hash_table_insert (self->mounts, button->mount, button);
194 }
195
196 static void
drive_list_remove(GtkContainer * container,GtkWidget * child)197 drive_list_remove (GtkContainer *container,
198 GtkWidget *child)
199 {
200 DriveList *self;
201 DriveButton *button;
202
203 g_return_if_fail (DRIVE_IS_LIST (container));
204 g_return_if_fail (DRIVE_IS_BUTTON (child));
205
206 self = DRIVE_LIST (container);
207 button = DRIVE_BUTTON (child);
208 if (button->volume)
209 g_hash_table_remove (self->volumes, button->volume);
210 else
211 g_hash_table_remove (self->mounts, button->mount);
212
213 if (GTK_CONTAINER_CLASS (drive_list_parent_class)->remove)
214 (* GTK_CONTAINER_CLASS (drive_list_parent_class)->remove) (container, child);
215 }
216
217 static void
list_buttons(gpointer key,gpointer value,gpointer user_data)218 list_buttons (gpointer key,
219 gpointer value,
220 gpointer user_data)
221 {
222 GtkWidget *button = value;
223 GList **sorted_buttons = user_data;
224
225 *sorted_buttons = g_list_insert_sorted (*sorted_buttons, button,
226 (GCompareFunc)drive_button_compare);
227 }
228
229 static gboolean
relayout_buttons(gpointer data)230 relayout_buttons (gpointer data)
231 {
232 DriveList *self = DRIVE_LIST (data);
233 GList *sorted_buttons = NULL, *tmp;
234 int i = 0;
235
236 self->layout_tag = 0;
237 if ( self->count > 0 ) {
238 g_hash_table_foreach (self->volumes, list_buttons, &sorted_buttons);
239 g_hash_table_foreach (self->mounts, list_buttons, &sorted_buttons);
240
241 /* position buttons in the table according to their sorted order */
242 for (tmp = sorted_buttons, i = 0; tmp != NULL; tmp = tmp->next, i++) {
243 GtkWidget *button = tmp->data;
244
245 if (self->orientation == GTK_ORIENTATION_HORIZONTAL) {
246 gtk_container_child_set (GTK_CONTAINER (self), button,
247 "left-attach", i + 1, "top-attach", 0,
248 "width", 1, "height", 1,
249 NULL);
250 }
251 else {
252 gtk_container_child_set (GTK_CONTAINER (self), button,
253 "left-attach", 0, "top-attach", i + 1,
254 "width", 1, "height", 1,
255 NULL);
256 }
257 }
258 }
259 else {
260 gtk_widget_show (self->dummy);
261 if (self->orientation == GTK_ORIENTATION_HORIZONTAL) {
262 gtk_container_child_set (GTK_CONTAINER (self), self->dummy,
263 "left-attach", i + 1, "top-attach", 0,
264 "width", 1, "height", 1,
265 NULL);
266 }
267 else {
268 gtk_container_child_set (GTK_CONTAINER (self), self->dummy,
269 "left-attach", 0, "top-attach", i + 1,
270 "width", 1, "height", 1,
271 NULL);
272 }
273 }
274 return FALSE;
275 }
276
277 static void
queue_relayout(DriveList * self)278 queue_relayout (DriveList *self)
279 {
280 if (!self->layout_tag) {
281 self->layout_tag = g_idle_add (relayout_buttons, self);
282 }
283 }
284
285 static void
mount_added(GVolumeMonitor * monitor,GMount * mount,DriveList * self)286 mount_added (GVolumeMonitor *monitor,
287 GMount *mount,
288 DriveList *self)
289 {
290 add_mount (self, mount);
291 self->count++;
292 if (self->count == 1)
293 gtk_container_remove (GTK_CONTAINER (self), g_object_ref (self->dummy));
294
295 mount_changed (monitor, mount, self);
296 }
297
298 static void
mount_changed(GVolumeMonitor * monitor,GMount * mount,DriveList * self)299 mount_changed (GVolumeMonitor *monitor,
300 GMount *mount,
301 DriveList *self)
302 {
303 GVolume *volume;
304 DriveButton *button = NULL;;
305
306 volume = g_mount_get_volume (mount);
307 if (volume) {
308 button = g_hash_table_lookup (self->volumes, volume);
309 g_object_unref (volume);
310 } else {
311 button = g_hash_table_lookup (self->mounts, mount);
312 }
313 if (button)
314 drive_button_queue_update (button);
315 }
316
317 static void
mount_removed(GVolumeMonitor * monitor,GMount * mount,DriveList * self)318 mount_removed (GVolumeMonitor *monitor,
319 GMount *mount,
320 DriveList *self)
321 {
322 remove_mount (self, mount);
323 mount_changed (monitor, mount, self);
324 self->count--;
325 if (self->count == 0) {
326 gtk_container_add (GTK_CONTAINER (self), self->dummy);
327 queue_relayout (self);
328 }
329 }
330
331 static void
volume_added(GVolumeMonitor * monitor,GVolume * volume,DriveList * self)332 volume_added (GVolumeMonitor *monitor,
333 GVolume *volume,
334 DriveList *self)
335 {
336 add_volume (self, volume);
337 self->count++;
338 if (self->count == 1)
339 gtk_container_remove (GTK_CONTAINER (self), g_object_ref (self->dummy));
340 }
341
342 static void
volume_changed(GVolumeMonitor * monitor,GVolume * volume,DriveList * self)343 volume_changed (GVolumeMonitor *monitor,
344 GVolume *volume,
345 DriveList *self)
346 {
347 DriveButton *button = NULL;;
348
349 button = g_hash_table_lookup (self->volumes, volume);
350 if (button)
351 drive_button_queue_update (button);
352 }
353
354 static void
volume_removed(GVolumeMonitor * monitor,GVolume * volume,DriveList * self)355 volume_removed (GVolumeMonitor *monitor,
356 GVolume *volume,
357 DriveList *self)
358 {
359 remove_volume (self, volume);
360 self->count--;
361 if (self->count == 0) {
362 gtk_container_add (GTK_CONTAINER (self), self->dummy);
363 queue_relayout (self);
364 }
365 }
366
367 static void
add_volume(DriveList * self,GVolume * volume)368 add_volume (DriveList *self,
369 GVolume *volume)
370 {
371 GtkWidget *button;
372
373 /* if the volume has already been added, return */
374 if (g_hash_table_lookup (self->volumes, volume) != NULL)
375 return;
376
377 button = drive_button_new (volume);
378 gtk_button_set_relief (GTK_BUTTON (button), self->relief);
379 drive_button_set_size (DRIVE_BUTTON (button), self->icon_size);
380 gtk_container_add (GTK_CONTAINER (self), button);
381 gtk_widget_show (button);
382 queue_relayout (self);
383 }
384
385 static void
remove_volume(DriveList * self,GVolume * volume)386 remove_volume (DriveList *self,
387 GVolume *volume)
388 {
389 GtkWidget *button;
390
391 /* if the volume has already been added, return */
392 button = g_hash_table_lookup (self->volumes, volume);
393 if (button) {
394 gtk_container_remove (GTK_CONTAINER (self), button);
395 queue_relayout (self);
396 }
397 }
398
399 static void
add_mount(DriveList * self,GMount * mount)400 add_mount (DriveList *self,
401 GMount *mount)
402 {
403 GtkWidget *button;
404 GVolume *volume;
405
406 /* ignore mounts reported as shadowed */
407 if (g_mount_is_shadowed (mount)) {
408 return;
409 }
410
411 /* ignore mounts attached to a volume */
412 volume = g_mount_get_volume (mount);
413 if (volume) {
414 g_object_unref (volume);
415 return;
416 }
417
418 /* if the mount has already been added, return */
419 if (g_hash_table_lookup (self->mounts, mount) != NULL)
420 return;
421
422 button = drive_button_new_from_mount (mount);
423 gtk_button_set_relief (GTK_BUTTON (button), self->relief);
424 drive_button_set_size (DRIVE_BUTTON (button), self->icon_size);
425 gtk_container_add (GTK_CONTAINER (self), button);
426 gtk_widget_show (button);
427 queue_relayout (self);
428 }
429
430 static void
remove_mount(DriveList * self,GMount * mount)431 remove_mount (DriveList *self,
432 GMount *mount)
433 {
434 GtkWidget *button;
435
436 /* if the mount has already been added, return */
437 button = g_hash_table_lookup (self->mounts, mount);
438 if (button) {
439 gtk_container_remove (GTK_CONTAINER (self), button);
440 queue_relayout (self);
441 }
442 }
443
444 void
drive_list_set_orientation(DriveList * self,GtkOrientation orientation)445 drive_list_set_orientation (DriveList *self,
446 GtkOrientation orientation)
447 {
448 g_return_if_fail (DRIVE_IS_LIST (self));
449
450 if (orientation != self->orientation) {
451 self->orientation = orientation;
452 queue_relayout (self);
453 }
454 }
455
456 static void
set_icon_size(gpointer key,gpointer value,gpointer user_data)457 set_icon_size (gpointer key,
458 gpointer value,
459 gpointer user_data)
460 {
461 DriveButton *button = value;
462 DriveList *self = user_data;
463
464 drive_button_set_size (button, self->icon_size);
465 }
466
467
468 void
drive_list_set_panel_size(DriveList * self,int panel_size)469 drive_list_set_panel_size (DriveList *self,
470 int panel_size)
471 {
472 g_return_if_fail (DRIVE_IS_LIST (self));
473
474 if (self->icon_size != panel_size) {
475 self->icon_size = panel_size;
476 g_hash_table_foreach (self->volumes, set_icon_size, self);
477 g_hash_table_foreach (self->mounts, set_icon_size, self);
478 }
479 }
480
481 void
drive_list_redraw(DriveList * self)482 drive_list_redraw (DriveList *self)
483 {
484 g_hash_table_foreach (self->volumes, drive_button_redraw, self);
485 g_hash_table_foreach (self->mounts, drive_button_redraw, self);
486 }
487
488 void
settings_color_changed(GSettings * settings,gchar * key,DriveList * drive_list)489 settings_color_changed (GSettings *settings,
490 gchar *key,
491 DriveList *drive_list)
492 {
493 g_return_if_fail (DRIVE_IS_LIST (drive_list));
494 drive_list_redraw (drive_list);
495 }
496
497 static void
set_button_relief(gpointer key,gpointer value,gpointer user_data)498 set_button_relief (gpointer key,
499 gpointer value,
500 gpointer user_data)
501 {
502 GtkButton *button = value;
503 DriveList *self = user_data;
504
505 gtk_button_set_relief (button, self->relief);
506 }
507
508 void
drive_list_set_transparent(DriveList * self,gboolean transparent)509 drive_list_set_transparent (DriveList *self,
510 gboolean transparent)
511 {
512 GtkReliefStyle relief;
513
514 relief = transparent ? GTK_RELIEF_NONE : GTK_RELIEF_NORMAL;
515
516 if (relief == self->relief)
517 return;
518
519 self->relief = relief;
520 g_hash_table_foreach (self->volumes, set_button_relief, self);
521 g_hash_table_foreach (self->mounts, set_button_relief, self);
522 }
523