1 /* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 #include <config.h> 3 4 #include "treeview.h" 5 6 typedef struct 7 { 8 GSettings *settings; 9 gboolean store_column_order; 10 GHashTable *excluded_columns; 11 } GsmTreeViewPrivate; 12 13 G_DEFINE_TYPE_WITH_PRIVATE (GsmTreeView, gsm_tree_view, GTK_TYPE_TREE_VIEW) 14 15 static void 16 gsm_tree_view_finalize (GObject *object) 17 { 18 GsmTreeViewPrivate *priv = gsm_tree_view_get_instance_private (GSM_TREE_VIEW (object)); 19 20 g_hash_table_destroy (priv->excluded_columns); 21 priv->excluded_columns = NULL; 22 23 G_OBJECT_CLASS (gsm_tree_view_parent_class)->finalize (object); 24 } 25 26 static void 27 gsm_tree_view_class_init (GsmTreeViewClass *klass) 28 { 29 GObjectClass *gobject_class = G_OBJECT_CLASS (klass); 30 31 gobject_class->finalize = gsm_tree_view_finalize; 32 } 33 34 static void 35 gsm_tree_view_init (GsmTreeView *self) 36 { 37 GsmTreeViewPrivate *priv = gsm_tree_view_get_instance_private (self); 38 39 priv->excluded_columns = g_hash_table_new (g_direct_hash, g_direct_equal); 40 } 41 42 void 43 gsm_tree_view_save_state (GsmTreeView *tree_view) 44 { 45 GsmTreeViewPrivate *priv; 46 47 g_return_if_fail (GSM_IS_TREE_VIEW (tree_view)); 48 49 priv = gsm_tree_view_get_instance_private (tree_view); 50 GtkTreeModel *model; 51 gint sort_col; 52 GtkSortType sort_type; 53 54 model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); 55 g_settings_delay (priv->settings); 56 if (gtk_tree_sortable_get_sort_column_id (GTK_TREE_SORTABLE (model), 57 &sort_col, 58 &sort_type)) { 59 g_settings_set_int (priv->settings, "sort-col", sort_col); 60 g_settings_set_int (priv->settings, "sort-order", sort_type); 61 } 62 63 if (priv->store_column_order) { 64 GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree_view)); 65 GList *iter; 66 GVariantBuilder builder; 67 68 g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); 69 70 for (iter = columns; iter != NULL; iter = iter->next) { 71 gint id = gtk_tree_view_column_get_sort_column_id (GTK_TREE_VIEW_COLUMN (iter->data)); 72 g_variant_builder_add (&builder, "i", id); 73 } 74 75 g_settings_set_value (priv->settings, "columns-order", 76 g_variant_builder_end (&builder)); 77 78 g_list_free (columns); 79 } 80 81 g_settings_apply (priv->settings); 82 } 83 84 GtkTreeViewColumn * 85 gsm_tree_view_get_column_from_id (GsmTreeView *tree_view, gint sort_id) 86 { 87 GList *columns; 88 GList *iter; 89 GtkTreeViewColumn *col = NULL; 90 91 g_return_val_if_fail (GSM_IS_TREE_VIEW (tree_view), NULL); 92 93 columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree_view)); 94 95 for (iter = columns; iter != NULL; iter = iter->next) { 96 col = GTK_TREE_VIEW_COLUMN (iter->data); 97 if (gtk_tree_view_column_get_sort_column_id (col) == sort_id) 98 break; 99 } 100 101 g_list_free (columns); 102 103 return col; 104 } 105 106 static gboolean 107 cb_column_header_clicked (GtkTreeViewColumn *column, GdkEventButton *event, gpointer data) 108 { 109 GtkMenu *menu = GTK_MENU (data); 110 111 if (event->button == GDK_BUTTON_SECONDARY) { 112 gtk_menu_popup_at_pointer (menu, (GdkEvent*)event); 113 return TRUE; 114 } 115 116 return FALSE; 117 } 118 119 void 120 gsm_tree_view_load_state (GsmTreeView *tree_view) 121 { 122 GsmTreeViewPrivate *priv; 123 GtkTreeModel *model; 124 gint sort_col; 125 GtkSortType sort_type; 126 127 g_return_if_fail (GSM_IS_TREE_VIEW (tree_view)); 128 129 priv = gsm_tree_view_get_instance_private (tree_view); 130 model = gtk_tree_view_get_model (GTK_TREE_VIEW (tree_view)); 131 132 sort_col = g_settings_get_int (priv->settings, "sort-col"); 133 sort_type = g_settings_get_int (priv->settings, "sort-order"); 134 135 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 136 sort_col, 137 sort_type); 138 139 if (priv->store_column_order) { 140 GtkMenu *header_menu = GTK_MENU (gtk_menu_new ()); 141 GList *columns = gtk_tree_view_get_columns (GTK_TREE_VIEW (tree_view)); 142 GList *iter; 143 GVariantIter *var_iter; 144 GtkTreeViewColumn *col, *last; 145 gint sort_id; 146 147 for (iter = columns; iter != NULL; iter = iter->next) { 148 const char *title; 149 char *key; 150 GtkButton *button; 151 GtkCheckMenuItem *column_item; 152 153 col = GTK_TREE_VIEW_COLUMN (iter->data); 154 sort_id = gtk_tree_view_column_get_sort_column_id (col); 155 156 if (priv->excluded_columns && 157 g_hash_table_contains (priv->excluded_columns, GINT_TO_POINTER (sort_id))) { 158 gtk_tree_view_column_set_visible (col, FALSE); 159 continue; 160 } 161 162 title = gtk_tree_view_column_get_title (col); 163 164 button = GTK_BUTTON (gtk_tree_view_column_get_button (col)); 165 g_signal_connect (button, "button-press-event", 166 G_CALLBACK (cb_column_header_clicked), 167 header_menu); 168 169 column_item = GTK_CHECK_MENU_ITEM (gtk_check_menu_item_new_with_label (title)); 170 g_object_bind_property (col, "visible", 171 column_item, "active", 172 G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE); 173 174 gtk_menu_shell_append (GTK_MENU_SHELL (header_menu), GTK_WIDGET (column_item)); 175 176 key = g_strdup_printf ("col-%d-width", sort_id); 177 gtk_tree_view_column_set_fixed_width (col, g_settings_get_int (priv->settings, key)); 178 gtk_tree_view_column_set_min_width (col, 30); 179 g_free (key); 180 181 key = g_strdup_printf ("col-%d-visible", sort_id); 182 gtk_tree_view_column_set_visible (col, g_settings_get_boolean (priv->settings, key)); 183 g_free (key); 184 } 185 186 g_list_free (columns); 187 188 gtk_widget_show_all (GTK_WIDGET (header_menu)); 189 190 g_settings_get (priv->settings, "columns-order", "ai", &var_iter); 191 last = NULL; 192 while (g_variant_iter_loop (var_iter, "i", &sort_id)) { 193 col = gsm_tree_view_get_column_from_id (tree_view, sort_id); 194 195 if (col != NULL && col != last) { 196 gtk_tree_view_move_column_after (GTK_TREE_VIEW (tree_view), 197 col, last); 198 last = col; 199 } 200 } 201 g_variant_iter_free (var_iter); 202 } 203 } 204 205 void 206 gsm_tree_view_add_excluded_column (GsmTreeView *tree_view, gint column_id) 207 { 208 GsmTreeViewPrivate *priv; 209 210 g_return_if_fail (GSM_IS_TREE_VIEW (tree_view)); 211 212 priv = gsm_tree_view_get_instance_private (tree_view); 213 g_hash_table_add (priv->excluded_columns, GINT_TO_POINTER (column_id)); 214 } 215 216 static guint timeout_id = 0; 217 static GtkTreeViewColumn *current_column; 218 219 static gboolean 220 save_column_state (gpointer data) 221 { 222 GSettings *settings = G_SETTINGS (data); 223 gint column_id = gtk_tree_view_column_get_sort_column_id (current_column); 224 gint width = gtk_tree_view_column_get_width (current_column); 225 gboolean visible = gtk_tree_view_column_get_visible (current_column); 226 227 gchar *key; 228 g_settings_delay (settings); 229 230 key = g_strdup_printf ("col-%d-width", column_id); 231 g_settings_set_int (settings, key, width); 232 g_free (key); 233 234 key = g_strdup_printf ("col-%d-visible", column_id); 235 g_settings_set_boolean (settings, key, visible); 236 g_free (key); 237 timeout_id = 0; 238 g_settings_apply (settings); 239 240 return FALSE; 241 } 242 243 static void 244 cb_update_column_state (GObject *object, GParamSpec *pspec, gpointer data) 245 { 246 GtkTreeViewColumn *column = GTK_TREE_VIEW_COLUMN (object); 247 248 current_column = column; 249 250 if (timeout_id > 0) 251 g_source_remove (timeout_id); 252 253 timeout_id = g_timeout_add_seconds (1, save_column_state, data); 254 } 255 256 void 257 gsm_tree_view_append_and_bind_column (GsmTreeView *tree_view, GtkTreeViewColumn *column) 258 { 259 GsmTreeViewPrivate *priv; 260 261 g_return_if_fail (GSM_IS_TREE_VIEW (tree_view)); 262 g_return_if_fail (GTK_IS_TREE_VIEW_COLUMN (column)); 263 264 priv = gsm_tree_view_get_instance_private (tree_view); 265 266 gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), 267 column); 268 269 g_signal_connect (column, "notify::fixed-width", 270 G_CALLBACK (cb_update_column_state), 271 priv->settings); 272 273 g_signal_connect (column, "notify::visible", 274 G_CALLBACK (cb_update_column_state), 275 priv->settings); 276 } 277 278 279 GsmTreeView * 280 gsm_tree_view_new (GSettings *settings, gboolean store_column_order) 281 { 282 GsmTreeView *self = g_object_new (GSM_TYPE_TREE_VIEW, NULL); 283 GsmTreeViewPrivate *priv = gsm_tree_view_get_instance_private (self); 284 285 priv->settings = settings; 286 priv->store_column_order = store_column_order; 287 288 return self; 289 } 290