/* This is -*- C -*- */ /* $Id: guppi-data.c,v 1.44 2001/11/19 05:40:40 trow Exp $ */ /* * guppi-data.c * * Copyright (C) 2000 EMC Capital Management, Inc. * Copyright (C) 2001 The Free Software Foundation * * 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 "guppi-data.h" #include #include /* #include */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "guppi-data-plug-in.h" #if 0 #include "guppi-data-tree.h" #include "guppi-data-transform.h" #endif static GtkObjectClass *parent_class = NULL; enum { ARG_0 }; enum { CHANGED, CHANGED_LABEL, CHANGED_SUBDATA, LAST_SIGNAL }; static guint data_signals[LAST_SIGNAL] = { 0 }; static void process_pending_op (GuppiData *data) { GList *op_list; GuppiDataOp *op; g_return_if_fail (data != NULL && GUPPI_IS_DATA (data)); op_list = (GList *) data->pending_ops; g_return_if_fail (op_list != NULL); op = (GuppiDataOp *) op_list->data; g_return_if_fail (op != NULL); if (op->op) op->op (data, op); data->pending_ops = g_list_remove_link (op_list, op_list); g_list_free_1 (op_list); } static void changed (GuppiData *data) { process_pending_op (data); } static void changed_label (GuppiData *data) { /* no op */ } static void changed_subdata (GuppiData *data) { process_pending_op (data); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ GuppiData * guppi_data_new (const gchar *type) { GuppiPlugIn *pi; GuppiData *data; GuppiDataClass *klass; GtkType t; g_return_val_if_fail (type && *type, NULL); t = gtk_type_from_name (type); if (t) { if (gtk_type_is_a (t, GUPPI_TYPE_DATA)) { data = (GuppiData *) guppi_type_new (t); } else { g_warning ("Type '%s' is-not-a GuppiData", type); return NULL; } } else { pi = guppi_plug_in_lookup ("data_impl", type); if (pi == NULL) { g_warning("Plug-in data_impl::%s not found", type); return NULL; } g_return_val_if_fail (GUPPI_IS_DATA_PLUG_IN (pi), NULL); data = guppi_data_plug_in_create_data (GUPPI_DATA_PLUG_IN (pi)); if (data == NULL) { g_warning ("%s's constructor returned NULL", type); return NULL; } } g_return_val_if_fail (GUPPI_IS_DATA (data), NULL); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); if (!klass->class_validated && klass->validate_class != NULL) { klass->class_validated = klass->validate_class (klass); if (! klass->class_validated) g_warning ("Class '%s' is not fully specified.\n", gtk_type_name (GTK_OBJECT_CLASS (klass)->type)); klass->class_validated = TRUE; /* only complain once */ } if (!klass->is_leaf_type) { g_warning ("Constructed non-leaf type '%s'", type); guppi_unref (data); return NULL; } if (klass->plug_in_code == NULL) { klass->plug_in_code = guppi_strdup (type); guppi_permanent_alloc (klass->plug_in_code); } return data; } /** * guppi_data_unique_id * @data: A #GuppiData object. * * Returns a unique identifier associated with @data. */ guppi_uniq_t guppi_data_unique_id (GuppiData *data) { g_return_val_if_fail (data && GUPPI_IS_DATA (data), 0); return data->id; } /** * guppi_data_get_label * @data: A #GuppiData object. * * Returns the label associated with @data. */ const gchar * guppi_data_get_label (GuppiData *data) { g_return_val_if_fail (GUPPI_IS_DATA (data), NULL); return data->label; } /** * guppi_data_set_label * @data: A #GuppiData object. * @label: A descriptive label. * * Associates the label @label with @data. */ void guppi_data_set_label (GuppiData *data, const gchar *label) { g_return_if_fail (GUPPI_IS_DATA (data)); g_return_if_fail (label != NULL); if (data->label && strcmp (label, data->label)) { guppi_free (data->label); data->label = guppi_strdup (label); gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED_LABEL]); } } /** * guppi_data_is_read_only * @data: A #GuppiData object. * * Returns %TRUE is @data is a read-only object. If an object is read-only, * all potentially state-altering operations are illegal. */ gboolean guppi_data_is_read_only (GuppiData *data) { GuppiDataClass *klass; g_return_val_if_fail (GUPPI_IS_DATA (data), FALSE); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); return data->read_only || klass->read_only; } /** * guppi_data_can_change * @data: A #GuppiData object. * * Returns %TRUE is @data is not a read-only object. This is the opposite * of guppi_data_can_change(). */ gboolean guppi_data_can_change (GuppiData *data) { return !guppi_data_is_read_only (data); } /** * guppi_data_add_pending_op * @data: A #GuppiData object. * @op: A chunk of black magic. * * You should only call this function if you really know what you are doing. */ void guppi_data_add_pending_op (GuppiData *data, GuppiDataOp *op) { GList *op_list; g_return_if_fail (data != NULL && GUPPI_IS_DATA (data)); g_return_if_fail (op != NULL); op_list = (GList *) data->pending_ops; data->pending_ops = g_list_prepend (op_list, op); } /** * guppi_data_changed * @data: A #GuppiData object. * * To force @data to emit a "changed" signal, don't call * this function. Call guppi_data_touch() instead. * * If you ignore this advice, bad things will probably happen. */ void guppi_data_changed (GuppiData *data) { g_return_if_fail (data != NULL && GUPPI_IS_DATA (data)); g_assert (guppi_data_can_change (data)); g_assert (data->pending_ops != NULL); gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED]); } static void op_nop (GuppiData *d, GuppiDataOp *op) { } /** * guppi_data_touch * @data: A #GuppiData object. * * Force @data to emit a "changed" signal. */ void guppi_data_touch (GuppiData *data) { GuppiDataOp op; g_return_if_fail (data != NULL && GUPPI_IS_DATA (data)); op.op = op_nop; guppi_data_add_pending_op (data, &op); gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED]); } /** * guppi_data_touch_subdata * @data: A #GuppiData object. * * Force @data to emit a "changed_subdata" signal. */ void guppi_data_touch_subdata (GuppiData *data) { g_return_if_fail (data && GUPPI_IS_DATA (data)); gtk_signal_emit (GTK_OBJECT (data), data_signals[CHANGED_SUBDATA]); } /** * guppi_data_copy * @data: A #GuppiData object. * * Returns a copy of @data. */ GuppiData * guppi_data_copy (GuppiData *data) { GuppiDataClass *klass; GuppiData *copy; g_return_val_if_fail (GUPPI_IS_DATA (data), NULL); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); g_return_val_if_fail (klass->copy != NULL, NULL); copy = klass->copy (data); copy->read_only = data->read_only; copy->label = guppi_strdup_printf (_("Copy of %s"), guppi_data_get_label (data)); return copy; } /** * guppi_data_get_size_in_bytes * @data: A #GuppiData object. * * Returns the total quantity of memory being used by @data, in bytes. * For some data interfaces and implementations, this is only an estimate, * not an exact figure. */ gint guppi_data_get_size_in_bytes (GuppiData *data) { GuppiDataClass *klass; g_return_val_if_fail (GUPPI_IS_DATA (data), -1); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); return klass->get_size_in_bytes ? klass->get_size_in_bytes (data) : -1; } /** * guppi_data_get_size_info * @data: A #GuppiData object. * * Returns a short, human-readable description of @data. * The exact nature of the description depends on the * @data's type. */ gchar * guppi_data_get_size_info (GuppiData *data) { GuppiDataClass *klass; g_return_val_if_fail (GUPPI_IS_DATA (data), NULL); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); return klass->get_size_info ? klass->get_size_info (data) : NULL; } /**************************************************************************/ /** * guppi_data_export_xml * @data: A #GuppiData object. * @doc: The #GuppiXMLDocument that will contain the exported XML. * * Build an XML tree describing @data. * Returns: the top node of the constructed XML. */ xmlNodePtr guppi_data_export_xml (GuppiData *data, GuppiXMLDocument *doc) { GuppiDataClass *klass; xmlNodePtr data_node = NULL; g_return_val_if_fail (data && GUPPI_IS_DATA (data), NULL); g_return_val_if_fail (doc, NULL); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); data_node = xmlNewNode (doc->ns, "Data"); xmlNewProp (data_node, "Type", klass->plug_in_code); if (data->label) xmlNewTextChild (data_node, doc->ns, "Label", data->label); if (klass->export_xml) klass->export_xml (data, doc, data_node); return data_node; } /** * guppi_data_import_xml * @doc: An XML document. * @node: The top node of a description of some data from @doc. * * Scan the XML tree contained in @node and use it to construct a #GuppiData object. * If the XML is malformed or any other errors occur in processing, %NULL is returned. * * Returns: the #GuppiData object constructed from the XML in @node. */ GuppiData * guppi_data_import_xml (GuppiXMLDocument *doc, xmlNodePtr node) { gchar *type_name; GuppiDataClass *klass; GuppiData *data = NULL; gboolean loaded_label = FALSE; g_return_val_if_fail (doc != NULL, NULL); g_return_val_if_fail (node != NULL, NULL); if (strcmp (node->name, "Data")) return NULL; /* Construct base object from type and impl information */ type_name = xmlGetProp (node, "Type"); if (type_name == NULL) { g_message ("Missing data Type property"); return NULL; } data = guppi_data_new (type_name); if (data == NULL) { g_message ("Unknown type \"%s\"", type_name); xmlFree (type_name); return NULL; } xmlFree (type_name); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); node = node->xmlChildrenNode; while (node != NULL) { if (!loaded_label && !strcmp (node->name, "Label")) { /* We do this song & dance to avoid g_free vs. xmlFree problems. */ gchar *tmp = xmlNodeGetContent (node->xmlChildrenNode); guppi_free (data->label); data->label = g_strdup (tmp); xmlFree (tmp); loaded_label = TRUE; } else if (klass->import_xml) { /* If we fail, fail silently. */ klass->import_xml (data, doc, node); } node = node->next; } return data; } /** * guppi_data_spew_xml * @data: A #GuppiData object. * * Build an XML tree describing @data and write it out to the console. * (This can be useful for debugging purposes.) */ void guppi_data_spew_xml (GuppiData *data) { GuppiXMLDocument *doc; g_return_if_fail (data && GUPPI_IS_DATA (data)); doc = guppi_xml_document_new (); guppi_xml_document_set_root (doc, guppi_data_export_xml (data, doc)); guppi_xml_document_spew (doc); guppi_xml_document_free (doc); } /* Subdata handling */ /** * guppi_data_has_subdata * @data: A #GuppiData object. * * Returns: %TRUE is @data contains any embedded subdata objects. */ gboolean guppi_data_has_subdata (GuppiData *data) { g_return_val_if_fail (data && GUPPI_IS_DATA (data), FALSE); return guppi_data_subdata_count (data) != 0; } static void count_subdata_fn (GuppiData *d, gpointer user_data) { ++*(gint *)user_data; } /** * guppi_data_subdata_count * @data: A #GuppiData object. * * Returns: the number of embedded subdata objects in @data. */ gint guppi_data_subdata_count (GuppiData *data) { gint count = 0; g_return_val_if_fail (data && GUPPI_IS_DATA (data), 0); guppi_data_foreach_subdata (data, count_subdata_fn, &count); return count; } /** * guppi_data_foreach_subdata * @data: A #GuppiData object. * @fn: A function. * @user_data: * * Iterate over @data's embedded subdata objects, applying the function @fn * to each. For every call, @user_data is passed to @fn as its second argument. */ void guppi_data_foreach_subdata (GuppiData *data, GuppiDataFn fn, gpointer closure) { GuppiDataClass *klass; g_return_if_fail (GUPPI_IS_DATA (data)); g_return_if_fail (fn != NULL); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); /* If foreach_subdata isn't defined in the implementation class, we assume that that type can never have subdata, and the guppi_data_foreach_subdata () call is just a NOP. */ if (klass->foreach_subdata) klass->foreach_subdata (data, fn, closure); } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /*** Data Menu Items ***/ /** * guppi_data_build_menu * @data: A #GuppiData object. * @menu: A menu. * @data_tree: * * Add menu items to @menu that correspond to data-type-specific operations * on @data. */ void guppi_data_build_menu (GuppiData *data, GtkMenu *menu, gpointer data_tree) { GuppiDataClass *klass; g_return_if_fail (data && GUPPI_IS_DATA (data)); g_return_if_fail (menu && GTK_IS_MENU (menu)); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); if (klass->build_menu) klass->build_menu (data, menu, data_tree); } /* * Implement a well-behaved "rename data" dialog. */ struct rename_info { GuppiData *data; gchar *original_label; GtkWidget *entry; }; static void rename_info_free (struct rename_info *ri) { gtk_signal_disconnect_by_data (GTK_OBJECT (ri->data), ri->entry); guppi_unref (ri->data); guppi_free (ri->original_label); guppi_free (ri); } static void rename_clicked_cb (GtkWidget *w, gint num, gpointer user_data) { struct rename_info *ri = (struct rename_info *)user_data; if (num == 0) { /* OK */ /* We don't need to do anything in this case. */ } else if (num == 1) { /* Cancel */ guppi_data_set_label (ri->data, ri->original_label); } else { g_assert_not_reached (); } rename_info_free (ri); gtk_widget_destroy (w); } static void rename_close_cb (GtkWidget *w, gpointer user_data) { struct rename_info *ri = (struct rename_info *)user_data; /* Revert to our original label */ guppi_data_set_label (ri->data, ri->original_label); rename_info_free (ri); } static void rename_edit_cb (GtkWidget *w, gpointer user_data) { struct rename_info *ri = (struct rename_info *)user_data; gchar *s; s = gtk_editable_get_chars (GTK_EDITABLE (w), 0, -1); guppi_outside_alloc (s); guppi_data_set_label (ri->data, s); guppi_free (s); } static void rename_changed_cb (GuppiData *data, const gchar* label, gpointer user_data) { GtkEntry *entry = GTK_ENTRY (user_data); gchar *s; s = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1); if (strcmp (s, label)) gtk_entry_set_text (entry, label); } static void rename_cb (GtkWidget *w, GuppiData *data) { GtkWidget *dialog; GtkWidget *entry; struct rename_info *ri; ri = guppi_new0 (struct rename_info, 1); ri->data = data; guppi_ref (ri->data); ri->original_label = guppi_strdup (guppi_data_get_label (data)); dialog = gnome_dialog_new (_("Edit Data Label"), GNOME_STOCK_BUTTON_OK, GNOME_STOCK_BUTTON_CANCEL, NULL); ri->entry = entry = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (entry), ri->original_label); gtk_box_pack_start(GTK_BOX(GNOME_DIALOG(dialog)->vbox), entry, TRUE, TRUE, 2); gtk_signal_connect (GTK_OBJECT (entry), "changed", GTK_SIGNAL_FUNC (rename_edit_cb), ri); gtk_signal_connect (GTK_OBJECT (dialog), "clicked", GTK_SIGNAL_FUNC (rename_clicked_cb), ri); gtk_signal_connect (GTK_OBJECT (dialog), "close", GTK_SIGNAL_FUNC (rename_close_cb), ri); gtk_signal_connect (GTK_OBJECT (data), "changed_label", GTK_SIGNAL_FUNC (rename_changed_cb), entry); gtk_widget_show_all (dialog); } static void data_info_cb (GtkWidget *w, GuppiData *data) { GtkWidget *win; GtkWidget *box; GtkWidget *info; GtkWidget *close_button; win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title (GTK_WINDOW (win), "Data Information"); info = guppi_data_info_display (data); if (info == NULL) info = gtk_label_new (_("No information available.")); box = gtk_vbox_new (FALSE, 2); gtk_box_pack_start (GTK_BOX (box), info, TRUE, TRUE, 2); gtk_box_pack_start (GTK_BOX (box), gtk_hseparator_new (), FALSE, TRUE, 3); close_button = gnome_stock_button (GNOME_STOCK_BUTTON_CLOSE); gtk_box_pack_start (GTK_BOX (box), close_button, FALSE, FALSE, 2); gtk_container_add (GTK_CONTAINER (win), box); gtk_signal_connect_object (GTK_OBJECT (close_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (win)); gtk_widget_show_all (win); } struct save_info { GtkFileSelection *fs; GuppiData *data; }; static void save_info_free (struct save_info *si) { gtk_widget_destroy (GTK_WIDGET (si->fs)); guppi_unref (si->data); guppi_free (si); } static void save_ok_cb (GtkWidget *w, gpointer user_data) { struct save_info *si = (struct save_info *)user_data; GuppiXMLDocument *doc; gchar *filename; filename = gtk_file_selection_get_filename (si->fs); doc = guppi_xml_document_new (); guppi_xml_document_set_root (doc, guppi_data_export_xml (si->data, doc)); guppi_xml_document_write_file (doc, filename); guppi_xml_document_free (doc); save_info_free (si); } static void save_cancel_cb (GtkWidget *w, gpointer user_data) { struct save_info *si = (struct save_info *)user_data; save_info_free (si); } static gboolean save_delete_event_cb (GtkWidget *w, GdkEvent *ev, gpointer user_data) { struct save_info *si = (struct save_info *)user_data; save_info_free (si); return TRUE; } static void data_save_cb (GtkWidget *w, GuppiData *data) { GtkWidget *fs; gchar *s; gchar *tmp; gchar *filename; struct save_info *si; s = guppi_strdup_printf (_("Save \"%s\""), guppi_data_get_label (data)); fs = gtk_file_selection_new (s); guppi_free (s); si = guppi_new0 (struct save_info, 1); si->fs = GTK_FILE_SELECTION (fs); si->data = data; guppi_ref (data); tmp = guppi_string_canonize_filename (guppi_data_get_label (data)); filename = guppi_strdup_printf ("%s.gpi", tmp); guppi_free (tmp); gtk_file_selection_set_filename (si->fs, filename); guppi_free (filename); gtk_signal_connect (GTK_OBJECT (si->fs->ok_button), "clicked", GTK_SIGNAL_FUNC (save_ok_cb), si); gtk_signal_connect (GTK_OBJECT (si->fs->cancel_button), "clicked", GTK_SIGNAL_FUNC (save_cancel_cb), si); gtk_signal_connect (GTK_OBJECT (fs), "delete_event", GTK_SIGNAL_FUNC (save_delete_event_cb), si); gtk_widget_show_all (fs); } #if 0 static void data_delete_cb (GtkWidget *w, GuppiData *data) { GuppiDataTree *tree; tree = GUPPI_DATA_TREE (gtk_object_get_data (GTK_OBJECT (w), "tree")); guppi_data_tree_remove (tree, data); } static void data_copy_cb (GtkWidget *w, GuppiData *data) { GuppiData *copy; GuppiDataTree *tree; copy = guppi_data_copy (data); if (copy == NULL) { guppi_warning_dialog ("Can't copy \"%s\"", guppi_data_get_label (data)); } else { tree = GUPPI_DATA_TREE (gtk_object_get_data (GTK_OBJECT (w), "tree")); guppi_data_tree_add (tree, copy); } } static void data_transform_cb (GtkWidget *w, GuppiData *data) { GuppiDataTransform *xform; GuppiDataTree *tree; GuppiData *result; xform = GUPPI_DATA_TRANSFORM (gtk_object_get_data (GTK_OBJECT (w), "xform")); tree = GUPPI_DATA_TREE (gtk_object_get_data (GTK_OBJECT (w), "tree")); result = guppi_data_transform_process (xform, data); if (result) guppi_data_tree_add_beside (tree, data, result); } #endif static void build_menu (GuppiData *data, GtkMenu *menu, gpointer data_tree) { GtkWidget *mi; GuppiDataClass *klass; GList *xforms; GList *iter; g_return_if_fail (data && GUPPI_IS_DATA (data)); g_return_if_fail (menu && GTK_IS_MENU (menu)); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); /* Need to hook up signals... */ if (klass->info_display) { mi = gtk_menu_item_new_with_label (_("Information")); gtk_menu_append (menu, mi); gtk_signal_connect (GTK_OBJECT (mi), "activate", GTK_SIGNAL_FUNC (data_info_cb), data); gtk_widget_show (mi); } mi = gtk_menu_item_new_with_label (_("Rename")); gtk_menu_append (menu, mi); gtk_signal_connect (GTK_OBJECT (mi), "activate", GTK_SIGNAL_FUNC (rename_cb), data); gtk_widget_show (mi); #if 0 mi = gtk_menu_item_new_with_label (_("Save")); gtk_menu_append (menu, mi); gtk_signal_connect (GTK_OBJECT (mi), "activate", GTK_SIGNAL_FUNC (data_save_cb), data); gtk_widget_show (mi); mi = gtk_menu_item_new_with_label (_("Delete")); gtk_object_set_data (GTK_OBJECT (mi), "tree", data_tree); gtk_menu_append (menu, mi); gtk_signal_connect (GTK_OBJECT (mi), "activate", GTK_SIGNAL_FUNC (data_delete_cb), data); gtk_widget_show (mi); mi = gtk_menu_item_new_with_label (_("Copy")); gtk_object_set_data (GTK_OBJECT (mi), "tree", data_tree); gtk_menu_append (menu, mi); gtk_signal_connect (GTK_OBJECT (mi), "activate", GTK_SIGNAL_FUNC (data_copy_cb), data); gtk_widget_show (mi); #endif #if 0 xforms = iter = guppi_data_transforms_find_supported (data); if (xforms) { GtkMenu *xmenu = GTK_MENU (gtk_menu_new ()); while (iter) { GuppiDataTransform *xform = GUPPI_DATA_TRANSFORM (iter->data); mi = gtk_menu_item_new_with_label (xform->name); gtk_object_set_data (GTK_OBJECT (mi), "xform", xform); gtk_object_set_data (GTK_OBJECT (mi), "tree", data_tree); gtk_signal_connect (GTK_OBJECT (mi), "activate", GTK_SIGNAL_FUNC (data_transform_cb), data); gtk_menu_append (xmenu, mi); gtk_widget_show (mi); iter = g_list_next (iter); } g_list_free (xforms); mi = gtk_menu_item_new_with_label (_("Copy & Transform")); gtk_menu_item_set_submenu (GTK_MENU_ITEM (mi), GTK_WIDGET (xmenu)); gtk_widget_show (GTK_WIDGET (xmenu)); gtk_menu_append (menu, mi); gtk_widget_show (mi); } #endif } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ /** * guppi_data_info_display * @data: A #GuppiData object. * * Returns: a widget containing useful human-readable information about * #data. */ GtkWidget * guppi_data_info_display (GuppiData *data) { GuppiDataClass *klass; GtkWidget *w = NULL; GtkWidget *frame; GtkWidget *box = NULL; g_return_val_if_fail (data && GUPPI_IS_DATA (data), NULL); klass = GUPPI_DATA_CLASS (GTK_OBJECT (data)->klass); if (klass->info_display) w = klass->info_display (data); if (w == NULL) return NULL; frame = gtk_frame_new (guppi_data_get_label (data)); box = gtk_vbox_new (FALSE, 2); gtk_container_add (GTK_CONTAINER (frame), box); if (w) gtk_box_pack_start (GTK_BOX (box), w, TRUE, TRUE, 0); return frame; } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ static void guppi_data_finalize (GtkObject *obj) { GuppiData *gd = GUPPI_DATA (obj); guppi_finalized (obj); /* We should never die with pending operations! */ g_assert (gd->pending_ops == NULL); guppi_free0 (gd->label); if (parent_class->finalize) parent_class->finalize (obj); } static gboolean validate_class (GuppiDataClass *klass) { gboolean ok = TRUE; if (klass->copy == NULL) { g_warning ("Method GuppiData::copy not defined."); ok = FALSE; } if (klass->get_size_in_bytes == NULL) { g_warning ("Method GuppiData::get_size_in_bytes not defined."); ok = FALSE; } if (klass->get_size_info == NULL) { g_warning ("Method GuppiData::get_size_info not defined."); ok = FALSE; } return ok; } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ static void guppi_data_class_init (GuppiDataClass *klass) { GtkObjectClass *object_class = (GtkObjectClass *) klass; parent_class = gtk_type_class (GTK_TYPE_OBJECT); klass->changed = changed; klass->changed_label = changed_label; klass->changed_subdata = changed_subdata; klass->build_menu = build_menu; klass->validate_class = validate_class; data_signals[CHANGED] = gtk_signal_new ("changed", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataClass, changed), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); data_signals[CHANGED_LABEL] = gtk_signal_new ("changed_label", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataClass, changed_label), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); data_signals[CHANGED_SUBDATA] = gtk_signal_new ("changed_subdata", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (GuppiDataClass, changed_subdata), gtk_marshal_NONE__NONE, GTK_TYPE_NONE, 0); gtk_object_class_add_signals (object_class, data_signals, LAST_SIGNAL); object_class->finalize = guppi_data_finalize; } static void guppi_data_init (GuppiData *gd) { gd->id = guppi_unique_id (); gd->label = guppi_strdup (_("Unlabled")); gd->read_only = FALSE; } GtkType guppi_data_get_type (void) { static GtkType guppi_data_type = 0; if (!guppi_data_type) { static const GtkTypeInfo guppi_data_info = { "GuppiData", sizeof (GuppiData), sizeof (GuppiDataClass), (GtkClassInitFunc) guppi_data_class_init, (GtkObjectInitFunc) guppi_data_init, NULL, NULL, (GtkClassInitFunc) NULL }; guppi_data_type = gtk_type_unique (GTK_TYPE_OBJECT, &guppi_data_info); } return guppi_data_type; } /* $Id: guppi-data.c,v 1.44 2001/11/19 05:40:40 trow Exp $ */