/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ #include #include #include #include #include #include "disks.h" #include "application.h" #include "util.h" #include "settings-keys.h" #include "legacy/treeview.h" enum DiskColumns { /* string columns* */ DISK_DEVICE, DISK_DIR, DISK_TYPE, DISK_TOTAL, DISK_FREE, DISK_AVAIL, /* USED has to be the last column */ DISK_USED, // then invisible columns /* PixBuf column */ DISK_ICON, /* numeric columns */ DISK_USED_PERCENTAGE, DISK_N_COLUMNS }; static void cb_sort_changed (GtkTreeSortable *model, gpointer data) { GsmApplication *app = (GsmApplication *) data; gsm_tree_view_save_state (GSM_TREE_VIEW (app->disk_list)); } static void fsusage_stats(const glibtop_fsusage *buf, guint64 *bused, guint64 *bfree, guint64 *bavail, guint64 *btotal, gint *percentage) { guint64 total = buf->blocks * buf->block_size; if (!total) { /* not a real device */ *btotal = *bfree = *bavail = *bused = 0ULL; *percentage = 0; } else { int percent; *btotal = total; *bfree = buf->bfree * buf->block_size; *bavail = buf->bavail * buf->block_size; *bused = *btotal - *bfree; /* percent = 100.0f * *bused / *btotal; */ percent = 100 * *bused / (*bused + *bavail); *percentage = CLAMP(percent, 0, 100); } } static const char* get_icon_for_path(const char* path) { GVolumeMonitor *monitor; GList *mounts; uint i; GMount *mount; GIcon *icon; const char* name = ""; monitor = g_volume_monitor_get (); mounts = g_volume_monitor_get_mounts (monitor); for (i = 0; i < g_list_length (mounts); i++) { mount = G_MOUNT (g_list_nth_data(mounts, i)); if (strcmp(g_mount_get_name(mount), path)) continue; icon = g_mount_get_icon (mount); if (!icon) continue; name = g_icon_to_string (icon); g_object_unref (icon); } g_list_free_full (mounts, g_object_unref); return name; } static GdkPixbuf* get_icon_for_device(const char *mountpoint) { const char* icon_name = get_icon_for_path(mountpoint); if (!strcmp(icon_name, "")) // FIXME: defaults to a safe value icon_name = "drive-harddisk"; // get_icon_for_path("/"); return gtk_icon_theme_load_icon (gtk_icon_theme_get_default (), icon_name, 24, GTK_ICON_LOOKUP_USE_BUILTIN, NULL); } static gboolean find_disk_in_model(GtkTreeModel *model, const char *mountpoint, GtkTreeIter *result) { GtkTreeIter iter; gboolean found = FALSE; if (gtk_tree_model_get_iter_first(model, &iter)) { do { char *dir; gtk_tree_model_get(model, &iter, DISK_DIR, &dir, -1); if (dir && !strcmp(dir, mountpoint)) { *result = iter; found = TRUE; } g_free(dir); } while (!found && gtk_tree_model_iter_next(model, &iter)); } return found; } static void remove_old_disks(GtkTreeModel *model, const glibtop_mountentry *entries, guint n) { GtkTreeIter iter; if (!gtk_tree_model_get_iter_first(model, &iter)) return; while (true) { char *dir; guint i; gboolean found = FALSE; gtk_tree_model_get(model, &iter, DISK_DIR, &dir, -1); for (i = 0; i != n; ++i) { if (!strcmp(dir, entries[i].mountdir)) { found = TRUE; break; } } g_free(dir); if (!found) { if (!gtk_list_store_remove(GTK_LIST_STORE(model), &iter)) break; else continue; } if (!gtk_tree_model_iter_next(model, &iter)) break; } } static void add_disk(GtkListStore *list, const glibtop_mountentry *entry, bool show_all_fs) { GdkPixbuf* pixbuf; GtkTreeIter iter; glibtop_fsusage usage; guint64 bused, bfree, bavail, btotal; gint percentage; glibtop_get_fsusage(&usage, entry->mountdir); if (not show_all_fs and usage.blocks == 0) { if (find_disk_in_model(GTK_TREE_MODEL(list), entry->mountdir, &iter)) gtk_list_store_remove(list, &iter); return; } fsusage_stats(&usage, &bused, &bfree, &bavail, &btotal, &percentage); pixbuf = get_icon_for_device(entry->mountdir); /* if we can find a row with the same mountpoint, we get it but we still need to update all the fields. This makes selection persistent. */ if (!find_disk_in_model(GTK_TREE_MODEL(list), entry->mountdir, &iter)) gtk_list_store_append(list, &iter); gtk_list_store_set(list, &iter, DISK_ICON, pixbuf, DISK_DEVICE, entry->devname, DISK_DIR, entry->mountdir, DISK_TYPE, entry->type, DISK_USED_PERCENTAGE, percentage, DISK_TOTAL, btotal, DISK_FREE, bfree, DISK_AVAIL, bavail, DISK_USED, bused, -1); } static void mount_changed (GVolumeMonitor *monitor, GMount *mount, GsmApplication *app) { disks_update(app); } static gboolean cb_timeout (gpointer data) { GsmApplication *app = (GsmApplication *) data; disks_update (app); return G_SOURCE_CONTINUE; } void disks_update(GsmApplication *app) { GtkListStore *list; glibtop_mountentry * entries; glibtop_mountlist mountlist; guint i; gboolean show_all_fs; list = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(app->disk_list))); show_all_fs = app->settings->get_boolean (GSM_SETTING_SHOW_ALL_FS); entries = glibtop_get_mountlist (&mountlist, show_all_fs); remove_old_disks(GTK_TREE_MODEL(list), entries, mountlist.number); for (i = 0; i < mountlist.number; i++) add_disk(list, &entries[i], show_all_fs); g_free(entries); } static void init_volume_monitor (GsmApplication *app) { GVolumeMonitor *monitor = g_volume_monitor_get (); g_signal_connect (monitor, "mount-added", G_CALLBACK (mount_changed), app); g_signal_connect (monitor, "mount-changed", G_CALLBACK (mount_changed), app); g_signal_connect (monitor, "mount-removed", G_CALLBACK (mount_changed), app); } void disks_freeze (GsmApplication *app) { if (app->disk_timeout) { g_source_remove (app->disk_timeout); app->disk_timeout = 0; } } void disks_thaw (GsmApplication *app) { if (app->disk_timeout) return; app->disk_timeout = g_timeout_add (app->config.disks_update_interval, cb_timeout, app); } void disks_reset_timeout (GsmApplication *app) { disks_freeze (app); disks_thaw (app); } static void cb_disk_columns_changed(GtkTreeView *treeview, gpointer data) { gsm_tree_view_save_state (GSM_TREE_VIEW (treeview)); } static void open_dir(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { GtkTreeIter iter; GtkTreeModel *model; char *dir, *url; model = gtk_tree_view_get_model(tree_view); if (!gtk_tree_model_get_iter(model, &iter, path)) { char *p; p = gtk_tree_path_to_string(path); g_warning("Cannot get iter for path '%s'\n", p); g_free(p); return; } gtk_tree_model_get(model, &iter, DISK_DIR, &dir, -1); url = g_strdup_printf("file://%s", dir); GError* error = 0; if (!g_app_info_launch_default_for_uri(url, NULL, &error)) { g_warning("Cannot open '%s' : %s\n", url, error->message); g_error_free(error); } g_free(url); g_free(dir); } static void cb_disk_list_destroying (GtkWidget *self, gpointer data) { g_signal_handlers_disconnect_by_func(self, (gpointer) cb_disk_columns_changed, data); g_signal_handlers_disconnect_by_func (gtk_tree_view_get_model (GTK_TREE_VIEW(self)), (gpointer) cb_sort_changed, data); } void create_disk_view(GsmApplication *app, GtkBuilder *builder) { GtkScrolledWindow *scrolled; GsmTreeView *disk_tree; GtkListStore *model; GtkTreeViewColumn *col; GtkCellRenderer *cell; PangoAttrList *attrs = NULL; guint i; init_volume_monitor (app); const gchar * const titles[] = { N_("Device"), N_("Directory"), N_("Type"), N_("Total"), N_("Free"), N_("Available"), N_("Used") }; scrolled = GTK_SCROLLED_WINDOW (gtk_builder_get_object (builder, "disks_scrolled")); model = gtk_list_store_new(DISK_N_COLUMNS, /* n columns */ G_TYPE_STRING, /* DISK_DEVICE */ G_TYPE_STRING, /* DISK_DIR */ G_TYPE_STRING, /* DISK_TYPE */ G_TYPE_UINT64, /* DISK_TOTAL */ G_TYPE_UINT64, /* DISK_FREE */ G_TYPE_UINT64, /* DISK_AVAIL */ G_TYPE_UINT64, /* DISK_USED */ GDK_TYPE_PIXBUF, /* DISK_ICON */ G_TYPE_INT /* DISK_USED_PERCENTAGE */ ); disk_tree = gsm_tree_view_new (g_settings_get_child (app->settings->gobj(), GSM_SETTINGS_CHILD_DISKS), TRUE); gtk_tree_view_set_model (GTK_TREE_VIEW (disk_tree), GTK_TREE_MODEL (model)); g_signal_connect(G_OBJECT(disk_tree), "row-activated", G_CALLBACK(open_dir), NULL); app->disk_list = disk_tree; gtk_container_add(GTK_CONTAINER(scrolled), GTK_WIDGET (disk_tree)); g_object_unref(G_OBJECT(model)); /* icon + device */ col = gtk_tree_view_column_new(); cell = gtk_cell_renderer_pixbuf_new(); gtk_tree_view_column_pack_start(col, cell, FALSE); gtk_tree_view_column_set_min_width(col, 30); gtk_tree_view_column_set_attributes(col, cell, "pixbuf", DISK_ICON, NULL); cell = gtk_cell_renderer_text_new(); gtk_tree_view_column_pack_start(col, cell, FALSE); gtk_tree_view_column_set_attributes(col, cell, "text", DISK_DEVICE, NULL); gtk_tree_view_column_set_title(col, _(titles[DISK_DEVICE])); gtk_tree_view_column_set_sort_column_id(col, DISK_DEVICE); gtk_tree_view_column_set_reorderable(col, TRUE); gtk_tree_view_column_set_resizable(col, TRUE); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gsm_tree_view_append_and_bind_column (GSM_TREE_VIEW (disk_tree), col); /* sizes - used */ for (i = DISK_DIR; i <= DISK_AVAIL; i++) { cell = gtk_cell_renderer_text_new(); col = gtk_tree_view_column_new(); gtk_tree_view_column_pack_start(col, cell, TRUE); gtk_tree_view_column_set_title(col, _(titles[i])); gtk_tree_view_column_set_resizable(col, TRUE); gtk_tree_view_column_set_sort_column_id(col, i); gtk_tree_view_column_set_reorderable(col, TRUE); gtk_tree_view_column_set_min_width(col, i == DISK_TYPE ? 40 : 72); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gsm_tree_view_append_and_bind_column (GSM_TREE_VIEW (disk_tree), col); switch (i) { case DISK_TOTAL: case DISK_FREE: case DISK_AVAIL: gtk_tree_view_column_set_cell_data_func(col, cell, &procman::size_si_cell_data_func, GUINT_TO_POINTER(i), NULL); attrs = make_tnum_attr_list (); g_object_set (cell, "attributes", attrs, "xalign", 1.0f, NULL); g_clear_pointer (&attrs, pango_attr_list_unref); break; default: gtk_tree_view_column_set_attributes(col, cell, "text", i, NULL); break; } } /* used + percentage */ col = gtk_tree_view_column_new(); cell = gtk_cell_renderer_text_new(); attrs = make_tnum_attr_list (); g_object_set (cell, "attributes", attrs, "xalign", 1.0f, NULL); g_clear_pointer (&attrs, pango_attr_list_unref); gtk_tree_view_column_set_min_width(col, 72); gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED); gtk_tree_view_column_pack_start(col, cell, FALSE); gtk_tree_view_column_set_cell_data_func(col, cell, &procman::size_si_cell_data_func, GUINT_TO_POINTER(DISK_USED), NULL); gtk_tree_view_column_set_title(col, _(titles[DISK_USED])); cell = gtk_cell_renderer_progress_new(); gtk_cell_renderer_set_padding(cell, 4.0f, 4.0f); gtk_tree_view_column_pack_start(col, cell, TRUE); gtk_tree_view_column_set_attributes(col, cell, "value", DISK_USED_PERCENTAGE, NULL); gtk_tree_view_column_set_resizable(col, TRUE); gtk_tree_view_column_set_sort_column_id(col, DISK_USED); gtk_tree_view_column_set_reorderable(col, TRUE); gsm_tree_view_append_and_bind_column (GSM_TREE_VIEW (disk_tree), col); /* numeric sort */ gsm_tree_view_load_state (GSM_TREE_VIEW (disk_tree)); g_signal_connect (G_OBJECT(disk_tree), "destroy", G_CALLBACK(cb_disk_list_destroying), app); g_signal_connect (G_OBJECT(disk_tree), "columns-changed", G_CALLBACK(cb_disk_columns_changed), app); g_signal_connect (G_OBJECT (model), "sort-column-changed", G_CALLBACK (cb_sort_changed), app); app->settings->signal_changed(GSM_SETTING_SHOW_ALL_FS).connect ([app](const Glib::ustring&) { disks_update (app); disks_reset_timeout (app); }); gtk_widget_show (GTK_WIDGET (disk_tree)); }