/* This is -*- C -*- */ /* $Id: guppi-data-tree-view.c,v 1.22 2001/09/27 05:07:29 trow Exp $ */ /* * guppi-data-tree-view.c * * Copyright (C) 2000 EMC Capital Management, Inc. * * Developed by Jon Trowbridge and * Havoc Pennington . * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if 0 #include #endif #include "guppi-data-tree-view.h" static GtkObjectClass *parent_class = NULL; static void guppi_data_tree_view_destroy (GtkObject * obj) { GuppiDataTreeView *tv = GUPPI_DATA_TREE_VIEW (obj); gtk_signal_disconnect_by_data (GTK_OBJECT (tv->tree), tv); guppi_unref0 (tv->tree); if (parent_class->destroy) parent_class->destroy (obj); } static void guppi_data_tree_view_finalize (GtkObject * obj) { guppi_finalized (obj); if (parent_class->finalize) parent_class->finalize (obj); } /**************************************************************************/ static GtkMenu * build_data_popup (GuppiData * data, GuppiDataTreeView * tv, GuppiDataTreeNode * node) { GtkMenu *menu = GTK_MENU (gtk_menu_new ()); GtkWidget *mi; mi = gtk_menu_item_new_with_label (guppi_data_get_label (data)); gtk_menu_append (menu, mi); gtk_widget_show (mi); mi = gtk_menu_item_new (); gtk_container_add (GTK_CONTAINER (mi), gtk_hseparator_new ()); gtk_menu_append (menu, mi); gtk_widget_show_all (mi); guppi_data_build_menu (data, menu, tv->tree); return menu; } static GtkTargetEntry drag_target_table = { "guppi/data", 0, 0 }; /* Unselect the data object we are manipulating when the popup menu disappears. */ static void popup_destroy_cb (GtkWidget *menu, gpointer user_data) { GtkCTree *ctree; GtkCTreeNode *ctree_node; ctree = GTK_CTREE (user_data); ctree_node = (GtkCTreeNode *)gtk_object_get_data (GTK_OBJECT (menu), "ctree_node"); /* If our popup action deleted the element from the tree, it might not still be there to unselect... */ if (!gtk_ctree_find (ctree, NULL, ctree_node)) return; gtk_ctree_unselect (ctree, ctree_node); gtk_ctree_node_set_selectable (ctree, ctree_node, FALSE); } static gint button_press_event (GtkWidget * w, GdkEventButton * ev) { GuppiDataTreeView *tv; GtkCTreeNode *ctree_node; GuppiDataTreeNode *node; gint row, col; GList *lnode; g_return_val_if_fail (w != NULL && GUPPI_IS_DATA_TREE_VIEW (w), FALSE); g_return_val_if_fail (ev != NULL, FALSE); tv = GUPPI_DATA_TREE_VIEW (w); gtk_clist_get_selection_info (GTK_CLIST (w), ev->x, ev->y, &row, &col); /* * Let clicks on the tree expanders chain up to the CTree event handlers. */ if (gtk_ctree_is_hot_spot (GTK_CTREE (w), ev->x, ev->y)) { if (GTK_WIDGET_CLASS (parent_class)->button_press_event) GTK_WIDGET_CLASS (parent_class)->button_press_event (w, ev); return TRUE; } lnode = g_list_nth (GTK_CLIST (w)->row_list, row); if (lnode == NULL || lnode->data == NULL) return FALSE; ctree_node = GTK_CTREE_NODE (lnode); node = (GuppiDataTreeNode *) gtk_ctree_node_get_row_data (GTK_CTREE (w), ctree_node); g_return_val_if_fail (node != NULL, FALSE); if (ev->button == 1) { GtkTargetList *target_list; gtk_ctree_node_set_selectable (GTK_CTREE (w), ctree_node, TRUE); gtk_ctree_select (GTK_CTREE (w), ctree_node); target_list = gtk_target_list_new (&drag_target_table, 1); gtk_drag_begin (w, target_list, GDK_ACTION_COPY, ev->button, (GdkEvent *) ev); tv->pending_dragged_data = node->data; tv->pending_dragged_node = ctree_node; return TRUE; } else if (ev->button == 3) { GtkMenu *popup_menu = build_data_popup (node->data, tv, node); gtk_widget_show (GTK_WIDGET (popup_menu)); gtk_menu_popup (popup_menu, NULL, NULL, NULL, NULL, ev->button, ev->time); gtk_ctree_node_set_selectable (GTK_CTREE (w), ctree_node, TRUE); gtk_ctree_select (GTK_CTREE (w), ctree_node); gtk_object_set_data (GTK_OBJECT (popup_menu), "ctree_node", ctree_node); /* Connect a callback to selection-done that will un-select the particular ctree node when our popup menu goes away. */ gtk_signal_connect (GTK_OBJECT (popup_menu), "selection-done", GTK_SIGNAL_FUNC (popup_destroy_cb), w); return TRUE; } return FALSE; } static void drag_end (GtkWidget * w, GdkDragContext * c) { GtkCTreeNode *ctree_node; g_return_if_fail (w != NULL && GUPPI_IS_DATA_TREE_VIEW (w)); ctree_node = GUPPI_DATA_TREE_VIEW (w)->pending_dragged_node; gtk_ctree_unselect (GTK_CTREE (w), ctree_node); gtk_ctree_node_set_selectable (GTK_CTREE (w), ctree_node, FALSE); } static void drag_data_get (GtkWidget * w, GdkDragContext * context, GtkSelectionData * selection_data, guint info, guint time) { GuppiDataTreeView *tv; g_return_if_fail (w != NULL && GUPPI_IS_DATA_TREE_VIEW (w)); tv = GUPPI_DATA_TREE_VIEW (w); g_return_if_fail (tv->pending_dragged_data != NULL); gtk_selection_data_set (selection_data, gdk_atom_intern ("guppi/data", FALSE), (sizeof (tv->pending_dragged_data) == 4 ? 32 : 64), /* ? */ (guchar *) & (tv->pending_dragged_data), sizeof (tv->pending_dragged_data)); tv->pending_dragged_data = NULL; } static void guppi_data_tree_view_class_init (GuppiDataTreeViewClass * klass) { GtkObjectClass *object_class = (GtkObjectClass *) klass; GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); parent_class = gtk_type_class (GTK_TYPE_CTREE); object_class->destroy = guppi_data_tree_view_destroy; object_class->finalize = guppi_data_tree_view_finalize; widget_class->button_press_event = button_press_event; widget_class->drag_data_get = drag_data_get; widget_class->drag_end = drag_end; } static void guppi_data_tree_view_init (GuppiDataTreeView * obj) { } GtkType guppi_data_tree_view_get_type (void) { static GtkType guppi_data_tree_view_type = 0; if (!guppi_data_tree_view_type) { static const GtkTypeInfo guppi_data_tree_view_info = { "GuppiDataTreeView", sizeof (GuppiDataTreeView), sizeof (GuppiDataTreeViewClass), (GtkClassInitFunc) guppi_data_tree_view_class_init, (GtkObjectInitFunc) guppi_data_tree_view_init, NULL, NULL, (GtkClassInitFunc) NULL }; guppi_data_tree_view_type = gtk_type_unique (GTK_TYPE_CTREE, &guppi_data_tree_view_info); } return guppi_data_tree_view_type; } /**************************************************************************/ static GtkCTreeNode * guppi_data_tree_view_build_node (GuppiDataTreeView * tv, GtkCTreeNode * parent, GuppiDataTreeNode * node) { static GdkPixmap *lock_pixmap = NULL; static GdkBitmap *lock_bitmap = NULL; gchar *text[5]; GtkCTreeNode *sib = NULL; GtkCTreeNode *us; gint mem; float mem_val; gchar buf[32]; gchar *sz_info; if (lock_pixmap == NULL) { gchar *path = guppi_find_pixmap ("lock.png"); GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (path); gdk_pixbuf_render_pixmap_and_mask (pixbuf, &lock_pixmap, &lock_bitmap, 20); guppi_free (path); } if (node->sibling_next) sib = guppi_data_tree_view_build_node (tv, parent, node->sibling_next); text[0] = (gchar *) guppi_data_get_label (node->data); text[1] = " "; /* text[2] = (gchar *) guppi_data_get_type_name (node->data); */ text[2] = "-TypeName-"; sz_info = guppi_data_get_size_info (node->data); text[3] = sz_info; mem = guppi_data_get_size_in_bytes (node->data); if (mem > 0) { if (mem < 1024) g_snprintf (buf, 32, "%db", mem); else { mem_val = mem / 1024.0; if (mem_val < 1024) g_snprintf (buf, 32, "%0.2fk", mem_val); else g_snprintf (buf, 32, "%0.2fM", mem_val / 1024); } } text[4] = mem > 0 ? buf : "?"; us = gtk_ctree_insert_node (GTK_CTREE (tv), parent, sib, text, 6, NULL, NULL, NULL, NULL, node->child == NULL, TRUE); if (guppi_data_is_read_only (node->data)) { gtk_ctree_node_set_pixmap (GTK_CTREE (tv), us, 1, lock_pixmap, lock_bitmap); } gtk_ctree_node_set_selectable (GTK_CTREE (tv), us, FALSE); gtk_ctree_node_set_row_data (GTK_CTREE (tv), us, node); if (node->child) guppi_data_tree_view_build_node (tv, us, node->child); g_free (sz_info); return us; } static void guppi_data_tree_view_build (GuppiDataTreeView * tv) { GuppiDataTreeNode *root; g_return_if_fail (tv != NULL); gtk_clist_freeze (GTK_CLIST (tv)); gtk_clist_clear (GTK_CLIST (tv)); root = tv->tree->root; if (root) guppi_data_tree_view_build_node (tv, NULL, root); gtk_clist_thaw (GTK_CLIST (tv)); } void guppi_data_tree_view_construct (GuppiDataTreeView * tv, GuppiDataTree * tree) { static gchar *titles[5] = { NULL }; gint column_count = 5; gint i; if (titles[0] == NULL) { titles[0] = _("Data"); titles[1] = ""; titles[2] = _("Type"); titles[3] = _("Size"); titles[4] = _("Mem"); } g_return_if_fail (tv != NULL); g_return_if_fail (tree != NULL); g_return_if_fail (tv->tree == NULL); tv->tree = tree; guppi_ref (tv->tree); gtk_signal_connect_object (GTK_OBJECT (tv->tree), "changed", GTK_SIGNAL_FUNC (guppi_data_tree_view_build), GTK_OBJECT (tv)); gtk_ctree_construct (GTK_CTREE (tv), column_count, 0, titles); for (i = 0; i < column_count; ++i) gtk_clist_set_column_auto_resize (GTK_CLIST (tv), i, TRUE); gtk_ctree_set_line_style (GTK_CTREE (tv), GTK_CTREE_LINES_DOTTED); gtk_ctree_set_expander_style (GTK_CTREE (tv), GTK_CTREE_EXPANDER_TRIANGLE); guppi_data_tree_view_build (tv); } GtkWidget * guppi_data_tree_view_new (GuppiDataTree * tree) { GuppiDataTreeView *tv; g_return_val_if_fail (tree != NULL, NULL); tv = GUPPI_DATA_TREE_VIEW (guppi_type_new (guppi_data_tree_view_get_type ())); guppi_data_tree_view_construct (tv, tree); return GTK_WIDGET (tv); } /* For debugging */ void guppi_data_tree_view_popup (GuppiDataTree * tree) { GtkWidget *w = gtk_window_new (GTK_WINDOW_TOPLEVEL); GtkWidget *tv = guppi_data_tree_view_new (tree); gtk_container_add (GTK_CONTAINER (w), tv); gtk_widget_show_all (w); } /* $Id: guppi-data-tree-view.c,v 1.22 2001/09/27 05:07:29 trow Exp $ */