/* dzl-tree-store.c * * Copyright (C) 2017 Christian Hergert * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define G_LOG_DOMAIN "dzl-tree-store" #include "config.h" #include "tree/dzl-tree-builder.h" #include "tree/dzl-tree-node.h" #include "tree/dzl-tree-private.h" #include "tree/dzl-tree-store.h" #include "util/dzl-macros.h" struct _DzlTreeStore { GtkTreeStore parent_instance; /* Weak references */ DzlTree *tree; }; static void dest_iface_init (GtkTreeDragDestIface *iface); static void source_iface_init (GtkTreeDragSourceIface *iface); G_DEFINE_TYPE_WITH_CODE (DzlTreeStore, dzl_tree_store, GTK_TYPE_TREE_STORE, G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_DEST, dest_iface_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE, source_iface_init)) static void dzl_tree_store_dispose (GObject *object) { DzlTreeStore *self = (DzlTreeStore *)object; dzl_clear_weak_pointer (&self->tree); G_OBJECT_CLASS (dzl_tree_store_parent_class)->dispose (object); } static void dzl_tree_store_class_init (DzlTreeStoreClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = dzl_tree_store_dispose; } static void dzl_tree_store_init (DzlTreeStore *self) { GType types[] = { DZL_TYPE_TREE_NODE }; gtk_tree_store_set_column_types (GTK_TREE_STORE (self), 1, types); } static gboolean dzl_tree_store_row_draggable (GtkTreeDragSource *source, GtkTreePath *path) { GtkTreeIter iter; g_assert (GTK_IS_TREE_DRAG_SOURCE (source)); g_assert (path != NULL); if (gtk_tree_model_get_iter (GTK_TREE_MODEL (source), &iter, path)) { g_autoptr(DzlTreeNode) node = NULL; GPtrArray *builders; DzlTree *tree; gtk_tree_model_get (GTK_TREE_MODEL (source), &iter, 0, &node, -1); g_assert (DZL_IS_TREE_NODE (node)); tree = dzl_tree_node_get_tree (node); g_assert (DZL_IS_TREE (tree)); builders = _dzl_tree_get_builders (tree); g_assert (builders != NULL); if (dzl_tree_node_is_root (node) || _dzl_tree_node_is_dummy (node)) return FALSE; for (guint i = 0; i < builders->len; i++) { DzlTreeBuilder *builder = g_ptr_array_index (builders, i); if (_dzl_tree_builder_node_draggable (builder, node)) return TRUE; } } return FALSE; } static gboolean dzl_tree_store_drag_data_get (GtkTreeDragSource *source, GtkTreePath *path, GtkSelectionData *data) { GtkTreeIter iter; g_assert (DZL_IS_TREE_STORE (source)); g_assert (path != NULL); g_assert (data != NULL); if (gtk_tree_model_get_iter (GTK_TREE_MODEL (source), &iter, path)) { g_autoptr(DzlTreeNode) node = NULL; GPtrArray *builders; DzlTree *tree; gtk_tree_model_get (GTK_TREE_MODEL (source), &iter, 0, &node, -1); g_assert (DZL_IS_TREE_NODE (node)); tree = dzl_tree_node_get_tree (node); builders = _dzl_tree_get_builders (tree); for (guint i = 0; i < builders->len; i++) { DzlTreeBuilder *builder = g_ptr_array_index (builders, i); if (_dzl_tree_builder_drag_data_get (builder, node, data)) return TRUE; } } return FALSE; } static gboolean dzl_tree_store_row_drop_possible (GtkTreeDragDest *dest, GtkTreePath *path, GtkSelectionData *data) { GtkTreeIter iter; g_assert (GTK_IS_TREE_DRAG_DEST (dest)); g_assert (path != NULL); g_assert (data != NULL); if (gtk_tree_model_get_iter (GTK_TREE_MODEL (dest), &iter, path)) { g_autoptr(DzlTreeNode) node = NULL; DzlTreeNode *effective; GPtrArray *builders; DzlTree *tree; gtk_tree_model_get (GTK_TREE_MODEL (dest), &iter, 0, &node, -1); g_assert (DZL_IS_TREE_NODE (node)); tree = dzl_tree_node_get_tree (node); g_assert (DZL_IS_TREE (tree)); builders = _dzl_tree_get_builders (tree); g_assert (builders != NULL); if (dzl_tree_node_is_root (node)) return FALSE; effective = _dzl_tree_node_is_dummy (node) ? dzl_tree_node_get_parent (node) : node; if (effective == NULL || dzl_tree_node_is_root (effective)) return FALSE; g_assert (effective != NULL); g_assert (!_dzl_tree_node_is_dummy (effective)); g_assert (!dzl_tree_node_is_root (effective)); for (guint i = 0; i < builders->len; i++) { DzlTreeBuilder *builder = g_ptr_array_index (builders, i); if (_dzl_tree_builder_node_droppable (builder, effective, data)) return TRUE; } } return FALSE; } static gboolean dzl_tree_store_drag_data_received (GtkTreeDragDest *dest, GtkTreePath *path, GtkSelectionData *data) { DzlTreeStore *self = (DzlTreeStore *)dest; g_autoptr(DzlTreeNode) drop_node = NULL; GPtrArray *builders; DzlTreeDropPosition pos = 0; GdkDragAction action; g_assert (GTK_IS_TREE_DRAG_DEST (self)); g_assert (self->tree != NULL); g_assert (path != NULL); g_assert (data != NULL); builders = _dzl_tree_get_builders (self->tree); drop_node = _dzl_tree_get_drop_node (self->tree, &pos); action = _dzl_tree_get_drag_action (self->tree); /* * If we have a drag/drop of a node onto/adjacent another node, then * use the simplified drag_node_received API for the builders instead * of making them extract the node information themselves. */ if (gtk_selection_data_get_target (data) == gdk_atom_intern_static_string ("GTK_TREE_MODEL_ROW")) { GtkTreePath *src_path = NULL; GtkTreeModel *model = NULL; if (gtk_tree_get_row_drag_data (data, &model, &src_path)) { GtkTreeIter iter; gboolean found; found = gtk_tree_model_get_iter (model, &iter, src_path); g_clear_pointer (&src_path, gtk_tree_path_free); if (found) { g_autoptr(DzlTreeNode) drag_node = NULL; gtk_tree_model_get (model, &iter, 0, &drag_node, -1); g_assert (DZL_IS_TREE_NODE (drag_node)); for (guint i = 0; i < builders->len; i++) { DzlTreeBuilder *builder = g_ptr_array_index (builders, i); g_assert (DZL_IS_TREE_BUILDER (builder)); if (_dzl_tree_builder_drag_node_received (builder, drag_node, drop_node, pos, action, data)) return TRUE; } } } } for (guint i = 0; i < builders->len; i++) { DzlTreeBuilder *builder = g_ptr_array_index (builders, i); g_assert (DZL_IS_TREE_BUILDER (builder)); if (_dzl_tree_builder_drag_data_received (builder, drop_node, pos, action, data)) return TRUE; } return FALSE; } static gboolean dzl_tree_store_drag_data_delete (GtkTreeDragSource *source, GtkTreePath *path) { DzlTreeStore *self = (DzlTreeStore *)source; GtkTreeIter iter; GPtrArray *builders; g_assert (GTK_IS_TREE_DRAG_SOURCE (self)); g_assert (self->tree != NULL); g_assert (path != NULL); builders = _dzl_tree_get_builders (self->tree); g_assert (builders != NULL); if (gtk_tree_model_get_iter (GTK_TREE_MODEL (source), &iter, path)) { g_autoptr(DzlTreeNode) node = NULL; gtk_tree_model_get (GTK_TREE_MODEL (source), &iter, 0, &node, -1); g_assert (DZL_IS_TREE_NODE (node)); for (guint i = 0; i < builders->len; i++) { DzlTreeBuilder *builder = g_ptr_array_index (builders, i); g_assert (DZL_IS_TREE_BUILDER (builder)); if (_dzl_tree_builder_drag_node_delete (builder, node)) return TRUE; } } return FALSE; } static void dest_iface_init (GtkTreeDragDestIface *iface) { iface->row_drop_possible = dzl_tree_store_row_drop_possible; iface->drag_data_received = dzl_tree_store_drag_data_received; } static void source_iface_init (GtkTreeDragSourceIface *iface) { iface->row_draggable = dzl_tree_store_row_draggable; iface->drag_data_get = dzl_tree_store_drag_data_get; iface->drag_data_delete = dzl_tree_store_drag_data_delete; } GtkTreeStore * _dzl_tree_store_new (DzlTree *tree) { DzlTreeStore *self; self = g_object_new (DZL_TYPE_TREE_STORE, NULL); dzl_set_weak_pointer (&self->tree, tree); return GTK_TREE_STORE (self); }