/* * SwamiUITree.c - Swami patch GTK tree object * * Swami * Copyright (C) 1999-2003 Josh Green * * 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 or point your web browser to http://www.gnu.org. */ #include #include #include #include #include #include #include "SwamiUITree.h" #include "SwamiUIObject.h" #include "SwamiUITreeMenu.h" #include "pixmap.h" #include "i18n.h" /* signals */ enum { ITEM_SELECT, ITEM_UNSELECT, SINGLE_SELECT_CHANGED, LAST_SIGNAL }; #define ITEM_GET_NODE(item, tree) \ (GTK_CTREE_NODE (g_hash_table_lookup (tree->item_hash, item))) #define NODE_GET_ITEM(node, tree) \ (INSTP_ITEM (gtk_ctree_node_get_row_data (tree->tree_widg, node))) #define ITEM_SET_NODE(item, node, tree) \ (g_hash_table_insert (tree->item_hash, item, node)) #define NODE_SET_ITEM(node, item, tree) \ (gtk_ctree_node_set_row_data (tree->tree_widg, node, item)) #define ITEM_UNSET_NODE(item, tree) \ (g_hash_table_remove (tree->item_hash, item)) #define ERRMSG_PARENT_NOT_IN_TREE "Item's parent is not in SwamiUITree" #define ERRMSG_ITEM_NOT_IN_TREE "Item is not in SwamiUITree" /* store class for use in functions, is this okay? */ static SwamiUITreeClass *tree_class = NULL; static guint tree_signals[LAST_SIGNAL] = {0}; /* Local Prototypes */ static void swamiui_tree_class_init (SwamiUITreeClass *klass); static void swamiui_tree_init (SwamiUITree *tree); static gboolean tree_cb_button_press (GtkWidget *widg, GdkEventButton *event, SwamiUITree *tree); static void tree_cb_select_row (GtkWidget *widg, GtkCTreeNode *node, int col, SwamiUITree *tree); static void tree_cb_unselect_row (GtkWidget *widg, GtkCTreeNode *node, int col, SwamiUITree *tree); static void tree_cb_item_add (SwamiObject *swami, IPItem *item, SwamiUITree *tree); static void tree_cb_item_remove (SwamiObject *swami, IPItem *item, SwamiUITree *tree); static void tree_cb_item_prop_change (SwamiObject *swami, IPItem *item, const char *prop, SwamiUITree *tree); static void tree_ctree_func_item_remove (GtkCTree *ctree, GtkCTreeNode *node, gpointer data); static GtkCTreeNode *swamiui_tree_add_sfont (SwamiUITree *tree, IPSFont *sf); static GtkCTreeNode *create_dummy_node (SwamiUITree *tree, int type, char *label, GtkCTreeNode *parent); static void swamiui_tree_remove_sfont (SwamiUITree *tree, IPSFont *sf); static GtkCTreeNode *swamiui_tree_add_preset (SwamiUITree *tree, IPPreset *preset); static GtkCTreeNode *get_preset_sibling (SwamiUITree *tree, GtkCTreeNode *pnode,IPPreset *preset); static void swamiui_tree_remove_preset (SwamiUITree *tree, IPPreset *preset); static GtkCTreeNode *swamiui_tree_add_inst (SwamiUITree *tree, IPInst *inst); static void swamiui_tree_remove_inst (SwamiUITree *tree, IPInst *inst); static GtkCTreeNode *swamiui_tree_add_sample (SwamiUITree *tree, IPSample *sample); static void swamiui_tree_remove_sample (SwamiUITree *tree, IPSample *sample); static GtkCTreeNode *swamiui_tree_add_zone (SwamiUITree *tree, IPZone *zone); static void swamiui_tree_remove_zone (SwamiUITree *tree, IPZone *zone); static void set_node_label (SwamiUITree *tree, GtkCTreeNode *node, const char *text); static gboolean is_node_selected (SwamiUITree *tree, GtkCTreeNode *node); static GtkCTreeNode *insert_node (SwamiUITree *tree, gchar *label, gchar **closed_xpm, gchar **opened_xpm, GtkCTreeNode *parent, GtkCTreeNode *sibling); /* functions */ guint swamiui_tree_get_type (void) { static guint obj_type = 0; if (!obj_type) { GtkTypeInfo obj_info = { "SwamiUITree", sizeof (SwamiUITree), sizeof (SwamiUITreeClass), (GtkClassInitFunc) swamiui_tree_class_init, (GtkObjectInitFunc) swamiui_tree_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; obj_type = gtk_type_unique (gtk_scrolled_window_get_type (), &obj_info); } return obj_type; } static void swamiui_tree_class_init (SwamiUITreeClass *klass) { GtkObjectClass *object_class; object_class = (GtkObjectClass *)klass; g_type_class_ref (swamiui_tree_get_type ()); /* FIXME? */ tree_class = klass; klass->tree_list = NULL; tree_signals[ITEM_SELECT] = gtk_signal_new ("item-select", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (SwamiUITreeClass, item_select), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); tree_signals[ITEM_UNSELECT] = gtk_signal_new ("item-unselect", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (SwamiUITreeClass, item_unselect), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); tree_signals[SINGLE_SELECT_CHANGED] = gtk_signal_new ("single-select-changed", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (SwamiUITreeClass, single_select_changed), gtk_marshal_NONE__POINTER, GTK_TYPE_NONE, 1, GTK_TYPE_POINTER); gtk_object_class_add_signals (object_class, tree_signals, LAST_SIGNAL); klass->item_select = NULL; klass->item_unselect = NULL; klass->single_select_changed = NULL; } static void swamiui_tree_init (SwamiUITree *tree) { // GtkAdjustment *hadj, *vadj; tree->item_hash = g_hash_table_new (NULL, NULL); tree->rclick_item = NULL; tree->select_item = NULL; gtk_scrolled_window_set_hadjustment (GTK_SCROLLED_WINDOW (tree), NULL); gtk_scrolled_window_set_vadjustment (GTK_SCROLLED_WINDOW (tree), NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (tree), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); tree->tree_widg = GTK_CTREE (gtk_ctree_new (2, 1)); gtk_clist_set_selection_mode (GTK_CLIST (tree->tree_widg), GTK_SELECTION_EXTENDED); gtk_ctree_set_indent (tree->tree_widg, 10); gtk_ctree_set_spacing (tree->tree_widg, 4); /* set the width of the auxiliary pixmap column */ gtk_clist_set_column_width (GTK_CLIST (tree->tree_widg), 0, 12); /* remove this to cause all sorts of weired ctree shit!! */ gtk_clist_set_column_auto_resize (GTK_CLIST (tree->tree_widg), 1, TRUE); /* attach row select/unselect routines */ gtk_signal_connect_after (GTK_OBJECT (tree->tree_widg), "tree-select-row", GTK_SIGNAL_FUNC (tree_cb_select_row), tree); gtk_signal_connect_after (GTK_OBJECT (tree->tree_widg), "tree-unselect-row", GTK_SIGNAL_FUNC (tree_cb_unselect_row), tree); /* for right click menus */ gtk_signal_connect (GTK_OBJECT (tree->tree_widg), "button-press-event", GTK_SIGNAL_FUNC (tree_cb_button_press), tree); gtk_widget_show (GTK_WIDGET (tree->tree_widg)); gtk_container_add (GTK_CONTAINER (tree), GTK_WIDGET (tree->tree_widg)); g_signal_connect (G_OBJECT (swami_object), "item_add", G_CALLBACK (tree_cb_item_add), tree); g_signal_connect (G_OBJECT (swami_object), "item_remove", G_CALLBACK (tree_cb_item_remove), tree); g_signal_connect (G_OBJECT (swami_object), "item_prop_change", G_CALLBACK (tree_cb_item_prop_change), tree); /* create right click menu widget */ tree->treemenu = swamiui_treemenu_new (tree); swamiui_tree_refresh (tree); } /** * Create a new Swami tree object * Returns: Swami tree object */ GtkWidget * swamiui_tree_new (void) { SwamiUITree *tree; tree = gtk_type_new (swamiui_tree_get_type ()); tree_class->tree_list = g_list_append (tree_class->tree_list, tree); return (GTK_WIDGET (tree)); } static gboolean tree_cb_button_press (GtkWidget *widg, GdkEventButton *event, SwamiUITree *tree) { gint row, col; gint x, y; GtkCTreeNode *node; if (event->button == 3) { /* right-click? */ x = event->x; /* x and y coordinates are of type double */ y = event->y; /* convert to integer */ /* ?clicked on a valid ctree row? */ if (!gtk_clist_get_selection_info (GTK_CLIST (tree->tree_widg), x, y, &row, &col)) return (FALSE); /* ?: No, return */ /* fetch the ctree node that belongs to clicked row */ if (!(node = gtk_ctree_node_nth (tree->tree_widg, row))) return (FALSE); tree->rclick_item = NODE_GET_ITEM (node, tree); /* stop button press event propagation */ gtk_signal_emit_stop_by_name (GTK_OBJECT (widg), "button-press-event"); swamiui_tree_highlight_item (tree, tree->rclick_item, GTK_STATE_PRELIGHT); swamiui_treemenu_popup (SWAMIUI_TREEMENU (tree->treemenu), event->button, event->time); return (TRUE); } return (TRUE); } static void tree_cb_select_row (GtkWidget *widg, GtkCTreeNode *node, int col, SwamiUITree *tree) { IPItem *item; item = NODE_GET_ITEM (node, tree); if (item) gtk_signal_emit (GTK_OBJECT (tree), tree_signals[ITEM_SELECT], item); item = swamiui_tree_get_selection_single (tree); if (item != tree->select_item) { tree->select_item = item; gtk_signal_emit (GTK_OBJECT (tree), /* emit "item_select_changed" */ tree_signals[SINGLE_SELECT_CHANGED], item); } } static void tree_cb_unselect_row (GtkWidget *widg, GtkCTreeNode *node, int col, SwamiUITree *tree) { IPItem *item; item = NODE_GET_ITEM (node, tree); if (item) gtk_signal_emit (GTK_OBJECT (tree), tree_signals[ITEM_UNSELECT], item); /* disabled for now to stop NULL item toggling when selecting items */ #if 0 item = swamiui_tree_get_selection_single (tree); if (item != tree->select_item) { tree->select_item = item; gtk_signal_emit (GTK_OBJECT (tree), /* emit "item_select_changed" */ tree_signals[SINGLE_SELECT_CHANGED], item); } #endif } /* callback for when an item gets added to the swami object */ static void tree_cb_item_add (SwamiObject *swami, IPItem *item, SwamiUITree *tree) { swamiui_tree_item_add (tree, item); } /* callback for when an item gets removed from the swami object */ static void tree_cb_item_remove (SwamiObject *swami, IPItem *item, SwamiUITree *tree) { swamiui_tree_item_remove (tree, item); } /* callback for when an item's property gets changed */ static void tree_cb_item_prop_change (SwamiObject *swami, IPItem *item, const char *prop, SwamiUITree *tree) { gboolean changed = FALSE; GtkCTreeNode *node, *parent = NULL, *sibling; GList *refitems = NULL, *p; char *s; switch (item->type) { case IPITEM_SFONT: if (strcmp (prop, "name") == 0 || strcmp (prop, "file_name") == 0) changed = TRUE; break; case IPITEM_PRESET: if (strcmp (prop, "bank") == 0 || strcmp (prop, "psetnum") == 0) /* relocate if changed bank/psetnum */ { SwamiUITreeIPSFontNodes *sfnodes; g_return_if_fail (item->parent != NULL); sfnodes = g_dataset_get_data (item->parent, "nodes"); if (!sfnodes) { SWAMI_CRITICAL (ERRMSG_PARENT_NOT_IN_TREE); return; } if (swami_item_get_int (swami_object, item, "bank") != 128) parent = sfnodes->melodic; else parent = sfnodes->percuss; sibling = get_preset_sibling (tree, parent, INSTP_PRESET (item)); changed = TRUE; } else if (strcmp (prop, "name") == 0) changed = TRUE; break; case IPITEM_INST: if (strcmp (prop, "name") == 0) { changed = TRUE; refitems = swami_item_get_zone_references (swami_object, item); } break; case IPITEM_SAMPLE: if (strcmp (prop, "name") == 0) { changed = TRUE; refitems = swami_item_get_zone_references (swami_object, item); } break; } if (changed) { gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); s = swami_item_get_formatted_name (swami_object, item); node = ITEM_GET_NODE (item, tree); set_node_label (tree, node, s); if (parent) gtk_ctree_move (GTK_CTREE (tree->tree_widg), node, parent, sibling); /* update all zone references (if any) */ p = refitems; while (p) { node = ITEM_GET_NODE (INSTP_ITEM (p->data), tree); set_node_label (tree, node, s); p = g_list_next (p); } g_free (s); g_list_free (refitems); gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); } } /** * Refresh a Swami tree object * @tree Swami tree object to refresh * * Refreshes a Swami tree object by re-synchronizing to the Swami object. */ void swamiui_tree_refresh (SwamiUITree *tree) { IPItem *p; g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); /* remove all toplevel patches */ gtk_ctree_post_recursive_to_depth (tree->tree_widg, NULL, 0, (GtkCTreeFunc)tree_ctree_func_item_remove, tree); /* loop over toplevel objects */ p = swami_get_patch_list (SWAMI_OBJECT (swamiui_object)); while (p) { swamiui_tree_item_add (tree, p); p = instp_item_next (p); } gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); } static void tree_ctree_func_item_remove (GtkCTree *ctree, GtkCTreeNode *node, gpointer data) { SwamiUITree *tree = SWAMIUI_TREE (data); IPItem *item; item = NODE_GET_ITEM (node, tree); swamiui_tree_item_remove (tree, item); } void swamiui_tree_freeze (SwamiUITree *tree) { g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); } void swamiui_tree_thaw (SwamiUITree *tree) { g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); } void swamiui_tree_freeze_all (void) { SwamiUITree *tree; GList *p; p = tree_class->tree_list; while (p) { tree = (SwamiUITree *)(p->data); gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); p = g_list_next (p); } } void swamiui_tree_thaw_all (void) { SwamiUITree *tree; GList *p; p = tree_class->tree_list; while (p) { tree = (SwamiUITree *)(p->data); gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); p = g_list_next (p); } } /** * Add a patch item to a SwamiUITree object * @tree Swami tree object * @item Patch item to add */ void swamiui_tree_item_add (SwamiUITree *tree, IPItem *item) { g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); g_return_if_fail (item != NULL); switch (item->type) { case IPITEM_SFONT: swamiui_tree_add_sfont (tree, INSTP_SFONT (item)); break; case IPITEM_PRESET: swamiui_tree_add_preset (tree, INSTP_PRESET (item)); break; case IPITEM_INST: swamiui_tree_add_inst (tree, INSTP_INST (item)); break; case IPITEM_SAMPLE: swamiui_tree_add_sample (tree, INSTP_SAMPLE (item)); break; case IPITEM_ZONE: swamiui_tree_add_zone (tree, INSTP_ZONE (item)); break; case IPITEM_VBANK: break; case IPITEM_VBANK_MAP: break; default: break; } } /** * Remove a patch item from a SwamiUITree object * @tree Swami tree object * @item Patch item to remove */ void swamiui_tree_item_remove (SwamiUITree *tree, IPItem *item) { g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); g_return_if_fail (item != NULL); switch (item->type) { case IPITEM_SFONT: swamiui_tree_remove_sfont (tree, INSTP_SFONT (item)); break; case IPITEM_PRESET: swamiui_tree_remove_preset (tree, INSTP_PRESET (item)); break; case IPITEM_INST: swamiui_tree_remove_inst (tree, INSTP_INST (item)); break; case IPITEM_SAMPLE: swamiui_tree_remove_sample (tree, INSTP_SAMPLE (item)); break; case IPITEM_ZONE: swamiui_tree_remove_zone (tree, INSTP_ZONE (item)); break; case IPITEM_VBANK: break; case IPITEM_VBANK_MAP: break; default: break; } } /** * Check if a sound font item is a tree dummy type * @item Item to check type of * Returns: TRUE if it is a tree dummy type (place holders in tree), * FALSE otherwise */ gboolean swamiui_tree_item_is_dummy (IPItem *item) { g_return_val_if_fail (item != NULL, FALSE); return (item->type >= SWAMIUI_TREE_FIRST && item->type <= SWAMIUI_TREE_LAST); } /* add a sound font to Swami tree */ static GtkCTreeNode * swamiui_tree_add_sfont (SwamiUITree *tree, IPSFont *sf) { SwamiUITreeIPSFontNodes *sfnodes; IPItem *p; char *s; sfnodes = g_malloc (sizeof (SwamiUITreeIPSFontNodes)); g_dataset_set_data (sf, "nodes", sfnodes); gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); s = swami_item_get_formatted_name (swami_object, INSTP_ITEM (sf)); sfnodes->sfdata = insert_node (tree, s, folder_xpm, folder_open_xpm, NULL, NULL); g_free (s); ITEM_SET_NODE (sf, sfnodes->sfdata, tree); NODE_SET_ITEM (sfnodes->sfdata, sf, tree); sfnodes->preset = create_dummy_node (tree, SWAMIUI_TREE_PRESET_ROOT, _("Presets"), sfnodes->sfdata); sfnodes->melodic = create_dummy_node (tree, SWAMIUI_TREE_PRESET_MELODIC, _("Melodic"), sfnodes->preset); sfnodes->percuss = create_dummy_node (tree, SWAMIUI_TREE_PRESET_PERCUSS, _("Percussion"), sfnodes->preset); sfnodes->inst = create_dummy_node (tree, SWAMIUI_TREE_INST_ROOT, _("Instruments"), sfnodes->sfdata); sfnodes->sample = create_dummy_node(tree, SWAMIUI_TREE_SAMPLE_ROOT, _("Samples"), sfnodes->sfdata); sfnodes->loaded = create_dummy_node (tree, SWAMIUI_TREE_SAMPLE_USER, _("User"), sfnodes->sample); sfnodes->rom = create_dummy_node (tree, SWAMIUI_TREE_SAMPLE_ROM, _("ROM"), sfnodes->sample); p = INSTP_ITEM (sf->preset); while (p) { swamiui_tree_add_preset (tree, INSTP_PRESET (p)); p = instp_item_next (p); } p = INSTP_ITEM (sf->inst); while (p) { swamiui_tree_add_inst (tree, INSTP_INST (p)); p = instp_item_next (p); } p = INSTP_ITEM (sf->sample); while (p) { swamiui_tree_add_sample (tree, INSTP_SAMPLE (p)); p = instp_item_next (p); } gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); return (sfnodes->sfdata); } static GtkCTreeNode * create_dummy_node (SwamiUITree *tree, int type, char *label, GtkCTreeNode *parent) { GtkCTreeNode *node; IPItem *item; node = insert_node (tree, label, folder_xpm, folder_open_xpm, parent, NULL); item = g_malloc0 (sizeof (IPItem)); item->type = type; item->parent = NODE_GET_ITEM (parent, tree); ITEM_SET_NODE (item, node, tree); NODE_SET_ITEM (node, item, tree); return (node); } /* remove a sound font from a Swami tree */ static void swamiui_tree_remove_sfont (SwamiUITree *tree, IPSFont *sf) { GtkCTreeNode *node; IPItem *p; gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); p = INSTP_ITEM (sf->preset); while (p) { swamiui_tree_remove_preset (tree, INSTP_PRESET (p)); p = instp_item_next (p); } p = INSTP_ITEM (sf->inst); while (p) { swamiui_tree_remove_inst (tree, INSTP_INST (p)); p = instp_item_next (p); } p = INSTP_ITEM (sf->sample); while (p) { swamiui_tree_remove_sample (tree, INSTP_SAMPLE (p)); p = instp_item_next (p); } /* free SwamiUITreeIPSFontNodes structure */ g_free (g_dataset_get_data (sf, "nodes")); g_dataset_set_data (sf, "nodes", NULL); node = ITEM_GET_NODE (sf, tree); /* remove all dummy nodes in tree */ gtk_ctree_post_recursive (tree->tree_widg, node, (GtkCTreeFunc) gtk_ctree_remove_node, NULL); ITEM_UNSET_NODE (sf, tree); /* clear user pointer */ gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); } /* add a preset to Swami tree */ static GtkCTreeNode * swamiui_tree_add_preset (SwamiUITree *tree, IPPreset *preset) { SwamiUITreeIPSFontNodes *sfnodes; GtkCTreeNode *parent, *sibling; GtkCTreeNode *node; IPItem *item; char *s; g_return_val_if_fail (preset->sfitem.parent != NULL, NULL); sfnodes = g_dataset_get_data (preset->sfitem.parent, "nodes"); if (!sfnodes) { SWAMI_CRITICAL (ERRMSG_PARENT_NOT_IN_TREE); return (NULL); } if (preset->bank != 128) parent = sfnodes->melodic; else parent = sfnodes->percuss; sibling = get_preset_sibling (tree, parent, preset); s = swami_item_get_formatted_name (swami_object, INSTP_ITEM (preset)); node = insert_node (tree, s, preset_xpm, NULL, parent, sibling); g_free (s); ITEM_SET_NODE (preset, node, tree); NODE_SET_ITEM (node, preset, tree); item = INSTP_ITEM (preset->zone); while (item) { swamiui_tree_add_zone (tree, INSTP_ZONE (item)); item = instp_item_next (item); } return (node); } /* determine sibling node to a Preset (sort) */ static GtkCTreeNode * get_preset_sibling (SwamiUITree *tree, GtkCTreeNode *pnode, IPPreset *preset) { GtkCTreeNode *n; IPPreset *p; n = GTK_CTREE_ROW (pnode)->children; while (n) { p = INSTP_PRESET (NODE_GET_ITEM (n, tree)); if (instp_preset_compare (preset, p) < 0) break; n = GTK_CTREE_ROW (n)->sibling; } return (n); } /* remove a preset from a Swami tree */ static void swamiui_tree_remove_preset (SwamiUITree *tree, IPPreset *preset) { IPItem *item; GtkCTreeNode *node; gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); item = INSTP_ITEM (preset->zone); while (item) { swamiui_tree_remove_zone (tree, INSTP_ZONE (item)); item = instp_item_next (item); } node = ITEM_GET_NODE (preset, tree); gtk_ctree_remove_node (tree->tree_widg, node); /* remove preset node */ ITEM_UNSET_NODE (preset, tree); /* clear node pointer */ gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); } /* add an instrument to Swami tree */ static GtkCTreeNode * swamiui_tree_add_inst (SwamiUITree *tree, IPInst *inst) { GtkCTreeNode *node, *sibling = NULL; SwamiUITreeIPSFontNodes *sfnodes; IPItem *item; char *s; g_return_val_if_fail (inst->sfitem.parent != NULL, NULL); sfnodes = g_dataset_get_data (inst->sfitem.parent, "nodes"); if (!sfnodes) { SWAMI_CRITICAL (ERRMSG_PARENT_NOT_IN_TREE); return (NULL); } item = INSTP_ITEM (inst)->next; if (item) sibling = ITEM_GET_NODE (item, tree); s = swami_item_get_formatted_name (swami_object, INSTP_ITEM (inst)); node = insert_node (tree, s, inst_xpm, NULL, sfnodes->inst, sibling); g_free (s); ITEM_SET_NODE (inst, node, tree); NODE_SET_ITEM (node, inst, tree); item = INSTP_ITEM (inst->zone); while (item) { swamiui_tree_add_zone (tree, INSTP_ZONE (item)); item = instp_item_next (item); } return (node); } /* remove an instrument from a Swami tree */ static void swamiui_tree_remove_inst (SwamiUITree *tree, IPInst *inst) { IPItem *item; GtkCTreeNode *node; gtk_clist_freeze (GTK_CLIST (tree->tree_widg)); item = INSTP_ITEM (inst->zone); while (item) { swamiui_tree_remove_zone (tree, INSTP_ZONE (item)); item = instp_item_next (item); } node = ITEM_GET_NODE (inst, tree); gtk_ctree_remove_node (tree->tree_widg, node); /* remove inst node */ ITEM_UNSET_NODE (inst, tree); /* clear node pointer */ gtk_clist_thaw (GTK_CLIST (tree->tree_widg)); } /* add a sample to Swami tree */ static GtkCTreeNode * swamiui_tree_add_sample (SwamiUITree *tree, IPSample *sample) { GtkCTreeNode *node, *parent, *sibling = NULL; SwamiUITreeIPSFontNodes *sfnodes; IPItem *item; char **xpm, *s; g_return_val_if_fail (sample->sfitem.parent != NULL, NULL); sfnodes = g_dataset_get_data (sample->sfitem.parent, "nodes"); if (!sfnodes) { SWAMI_CRITICAL (ERRMSG_PARENT_NOT_IN_TREE); return (NULL); } /* find next sibling of same branch (ROM or User) */ item = INSTP_ITEM (sample)->next; while (item && (sample->sampletype & IPSAMPLE_TYPE_ROM) != (INSTP_SAMPLE (item)->sampletype & IPSAMPLE_TYPE_ROM)) item = instp_item_next (item); if (item) sibling = ITEM_GET_NODE (item, tree); if (!(sample->sampletype & IPSAMPLE_TYPE_ROM)) { parent = sfnodes->loaded; xpm = sample_xpm; } else { parent = sfnodes->rom; xpm = rom_xpm; } s = swami_item_get_formatted_name (swami_object, INSTP_ITEM (sample)); node = insert_node (tree, s, xpm, NULL, parent, sibling); g_free (s); ITEM_SET_NODE (sample, node, tree); NODE_SET_ITEM (node, sample, tree); return (node); } /* remove a sample from a Swami tree */ static void swamiui_tree_remove_sample (SwamiUITree *tree, IPSample *sample) { GtkCTreeNode *node; node = ITEM_GET_NODE (sample, tree); gtk_ctree_remove_node (tree->tree_widg, node); /* remove sample node */ ITEM_UNSET_NODE (sample, tree); /* clear node pointer */ } /* add a zone to Swami tree */ static GtkCTreeNode * swamiui_tree_add_zone (SwamiUITree *tree, IPZone *zone) { GtkCTreeNode *node, *parent, *sibling = NULL; IPItem *pitem; char *s; char **xpm; g_return_val_if_fail (zone->sfitem.parent != NULL, NULL); pitem = INSTP_ITEM (zone)->parent; parent = ITEM_GET_NODE (pitem, tree); if (!parent) { SWAMI_CRITICAL (ERRMSG_PARENT_NOT_IN_TREE); return (NULL); } if (!zone->refitem) { s = _("Global Zone"); xpm = gzone_xpm; sibling = GTK_CTREE_ROW (parent)->children; } else { if (pitem->type == IPITEM_PRESET) { s = INSTP_INST (zone->refitem)->name; xpm = inst_xpm; } else if (pitem->type == IPITEM_INST) { IPSample *sam = INSTP_SAMPLE (zone->refitem); s = sam->name; if (!(sam->sampletype & IPSAMPLE_TYPE_ROM)) xpm = sample_xpm; else xpm = rom_xpm; } else g_assert_not_reached (); if (INSTP_ITEM (zone)->next) sibling = ITEM_GET_NODE (INSTP_ITEM (zone)->next, tree); } node = insert_node (tree, s, xpm, NULL, parent, sibling); ITEM_SET_NODE (zone, node, tree); NODE_SET_ITEM (node, zone, tree); return (node); } /* remove a zone from a Swami tree */ static void swamiui_tree_remove_zone (SwamiUITree *tree, IPZone *zone) { GtkCTreeNode *node; node = ITEM_GET_NODE (zone, tree); gtk_ctree_remove_node (tree->tree_widg, node); /* remove zone node */ ITEM_UNSET_NODE (zone, tree); /* clear node pointer */ } #if 0 GtkCTreeNode * sftree_add_vbank (UIVBank * uivb) { VBnkData *vbnk; VBnkNodes *vbnodes; GSList *p; gchar *s, *s2; vbnk = uivb->vbnk; vbnodes = g_malloc (sizeof (VBnkNodes)); uivb->nodes = vbnodes; SFTREE_FREEZE (); s2 = g_basename (vbnk->fname); /* Does not need to be freed */ s = g_strdup_printf (_(" %s (%s)"), s2, vbnk->fname); vbnodes->vbank = sftree_insert_node (s, NULL, NULL, NULL, NULL); sftree_set_node_ref (vbnodes->vbank, NODE_VBANK, uivb, SFITEMID_NONE); g_free (s); s2 = (vbnk->defname != NULL && *vbnk->defname != '\0') ? vbnk->defname : _(""); s = g_strdup_printf (_(" %s"), s2); vbnodes->defbank = sftree_insert_node (s, NULL, NULL, vbnodes->vbank, NULL); sftree_set_node_ref (vbnodes->defbank, NODE_VBNK_DEFBANK, NULL, SFITEMID_NONE); g_free (s); vbnodes->maproot = sftree_insert_node (_("Mappings"), NULL, NULL, vbnodes->vbank, NULL); sftree_set_node_ref (vbnodes->maproot, NODE_VBNK_MAPROOT, NULL,vbnk->itemid); vbnk->itemid = SFTREE_NODE_REF (vbnodes->maproot)->itemid; p = vbnk->items; while (p) { sftree_add_vbank_map (p, NULL, vbnodes); p = g_slist_next (p); } SFTREE_THAW (); return (vbnodes->vbank); } GtkCTreeNode * sftree_add_vbank_map (GSList *lmap, GtkCTreeNode *pos, VBnkNodes *nodes) { VBnkItem *item; GtkCTreeNode *node; gchar *s, *s2, *s3, *s4; item = (VBnkItem *)(lmap->data); s2 = (item->map.keynote != -1) ? g_strdup_printf ("(%03d)", item->map.keynote) : g_strdup (""); s3 = (item->src.keynote != -1) ? g_strdup_printf ("(%03d)", item->src.keynote) : g_strdup (""); if (item->sfname) s4 = item->sfname; else s4 = ""; s = g_strdup_printf ("%03d-%03d%s > %03d-%03d%s %s", item->map.bank, item->map.psetnum, s2, item->src.bank, item->src.psetnum, s3, s4); g_free (s2); g_free (s3); node = sftree_insert_node (s, NULL, NULL, nodes->maproot, NULL); sftree_set_node_ref (node, NODE_VBNK_MAP, lmap, item->itemid); g_free (s); item->itemid = SFTREE_NODE_REF (node)->itemid; return (node); } GtkCTreeNode * sftree_add_vbank_map_sorted (GSList *lmap, VBnkNodes *nodes) { UIVBank *uivb; GtkCTreeNode *pos; GSList *p; uivb = SFTREE_SFNODE_UIVB (nodes->vbank); p = g_slist_next (lmap); if (p) pos = gtk_ctree_find_by_row_data_custom (sftree_widg, nodes->maproot, p, (GCompareFunc) sftree_sftreeref_data_compare); else p = NULL; return (sftree_add_vbank_map (lmap, pos, nodes)); } void sftree_remove_vbank (UIVBank *uivb) { gtk_ctree_remove_node (sftree_widg, uivb->nodes->vbank); g_free (uivb->nodes); uivb->nodes = NULL; } #endif /** * Get and insure single item selection in Swami tree object * @tree Swami tree object * Returns: The currently selected single item or NULL if multiple or no items * are selected. Right clicked item is included as part of selection. Unlike * #swamiui_tree_get_selection the caller is responsible for referencing the * returned item. */ IPItem * swamiui_tree_get_selection_single (SwamiUITree *tree) { GList *nsel; IPItem *sitem = NULL; nsel = GTK_CLIST (tree->tree_widg)->selection; if (nsel && !nsel->next) /* if single node in selection, fetch item */ sitem = NODE_GET_ITEM (GTK_CTREE_NODE (nsel->data), tree); /* check if a node is right clicked and is the only one selected */ if (tree->rclick_item && (!nsel || sitem == tree->rclick_item)) return (tree->rclick_item); /* right click item is not the same as single selected tree item? */ if (sitem && tree->rclick_item) return (NULL); return (sitem); } /** * Get Swami tree selection * @tree Swami tree object * Returns: List of IPItems currently selected, \b free with * #swamiui_tree_free_selection when done using it. * \see swamiui_tree_free_selection */ GList * swamiui_tree_get_selection (SwamiUITree *tree) { GList *sel = NULL, *p; IPItem *item; p = GTK_CLIST (tree->tree_widg)->selection; while (p) { item = NODE_GET_ITEM (GTK_CTREE_NODE (p->data), tree); if (!swamiui_tree_item_is_dummy (item)) instp_item_ref (item); sel = g_list_append (sel, item); p = g_list_next (p); } return (sel); } /** * Get Swami tree selection and right click item as first item * @tree Swami tree object * Returns: List of selected IPItems with right clicked item as first item. * Must \b free list with #swamiui_tree_free_selection when finished with it. * \see swamiui_tree_get_selection * * Get Swami tree selection, including right clicked item. Like * #sftree_get_selection but makes the right clicked item the first item. Right * clicked item will always be the first item in list, a list item with a NULL * data field is used if right click is not active. */ GList * swamiui_tree_get_selection_rclick (SwamiUITree *tree) { GList *sel; sel = swamiui_tree_get_selection (tree); if (tree->rclick_item) /* right click item active? */ { if (!swamiui_tree_item_is_dummy (tree->rclick_item)) instp_item_ref (tree->rclick_item); sel = g_list_remove (sel, tree->rclick_item); /* remove duplicate */ sel = g_list_prepend (sel, tree->rclick_item); /* prepend item */ } else sel = g_list_prepend (sel, NULL); /* no rclick, prepend NULL item */ return (sel); } /** * Free a selection list * @sel_list A selection list returned by #swamiui_tree_get_selection and * #swamiui_tree_get_selection_complete. */ void swamiui_tree_free_selection (GList *sel_list) { GList *p; IPItem *item; p = sel_list; while (p) { item = (IPItem *)(p->data); if (item && !swamiui_tree_item_is_dummy (item)) instp_item_unref (item); p = g_list_next (p); } g_list_free (sel_list); } /** * Get tree active right clicked item * @tree Swami tree object * Returns: Current active right click item or NULL if none */ IPItem * swamiui_tree_get_rclick_item (SwamiUITree *tree) { g_return_val_if_fail (tree != NULL, NULL); g_return_val_if_fail (SWAMIUI_IS_TREE (tree), NULL); return (tree->rclick_item); } /** * Set value of active right clicked item * @tree Swami tree object * @item Item to set right clicked item to or NULL for no active * right click */ void swamiui_tree_set_rclick_item (SwamiUITree *tree, IPItem *item) { g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); tree->rclick_item = item; } /** * Clear tree selection (unselect all items) * @tree Swami tree object */ void swamiui_tree_clear_selection (SwamiUITree *tree) { GList *p, *copy; copy = g_list_copy (GTK_CLIST (tree->tree_widg)->selection); p = copy; while (p) { gtk_ctree_unselect (tree->tree_widg, GTK_CTREE_NODE (p->data)); p = g_list_next (p); } g_list_free (copy); } /** * "Spotlights" a sound font item in a Swami tree object * @tree Swami tree object * @item Item to spotlight * * Spotlights an item in a Swami tree object by recursively expanding all * nodes up the tree from item and moving the view to position item in the * center of the view. */ void swamiui_tree_spotlight_item (SwamiUITree *tree, IPItem *item) { GtkCTreeNode *item_node, *n; g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); g_return_if_fail (item != NULL); item_node = ITEM_GET_NODE (item, tree); g_return_if_fail (item_node != NULL); n = GTK_CTREE_ROW (item_node)->parent; while (n) { /* expand up the tree */ gtk_ctree_expand (tree->tree_widg, n); n = GTK_CTREE_ROW (n)->parent; } /* move the node into view if its not visible */ if (!gtk_ctree_node_is_visible (tree->tree_widg, item_node)) gtk_ctree_node_moveto (tree->tree_widg, item_node, 1, 0.5, 0.0); gtk_clist_unselect_all (GTK_CLIST (tree->tree_widg)); gtk_ctree_select (tree->tree_widg, item_node); } void swamiui_tree_highlight_item (SwamiUITree *tree, IPItem *item, GtkStateType state) { GtkCTreeNode *node; g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); g_return_if_fail (item != NULL); node = ITEM_GET_NODE (item, tree); if (!node || is_node_selected (tree, node)) return; gtk_ctree_node_set_background(tree->tree_widg, node, >K_WIDGET (tree->tree_widg)->style->bg[state]); gtk_ctree_node_set_foreground(tree->tree_widg, node, >K_WIDGET (tree->tree_widg)->style->fg[state]); } void swamiui_tree_unhighlight_item (SwamiUITree *tree, IPItem *item) { GtkCTreeNode *node; g_return_if_fail (tree != NULL); g_return_if_fail (SWAMIUI_IS_TREE (tree)); g_return_if_fail (item != NULL); node = ITEM_GET_NODE (item, tree); if (!node || is_node_selected (tree, node)) return; gtk_ctree_node_set_background (tree->tree_widg, node, NULL); gtk_ctree_node_set_foreground (tree->tree_widg, node, NULL); } /** * Set pixmap of an item in the tree's first column * @tree Tree object * @item Item in tree to set pixmap of * @xpm Pixmap to set first column of item to */ void swamiui_tree_item_set_pixmap (SwamiUITree *tree, IPItem *item, gchar **xpm) { GtkCTreeNode *node; GdkPixmap *pixi; GdkBitmap *maski; node = ITEM_GET_NODE (item, tree); if (!node) { SWAMI_CRITICAL (ERRMSG_ITEM_NOT_IN_TREE); return; } /* sure.. Using NULL as the pixmap argument would seem like an obvious way to clear it, but nooooooooo... We set to NULL text instead to clear */ if (xpm != NULL) { pixmap_get (xpm, &pixi, &maski); gtk_ctree_node_set_pixmap (tree->tree_widg, node, 0, pixi, maski); } else gtk_ctree_node_set_text (tree->tree_widg, node, 0, NULL); } /* set the label of a node */ static void set_node_label (SwamiUITree *tree, GtkCTreeNode *node, const char *text) { guint8 spacing; GdkPixmap *pixmap_closed, *pixmap_opened; GdkBitmap *mask_closed, *mask_opened; gboolean is_leaf, expanded; gtk_ctree_get_node_info (GTK_CTREE (tree->tree_widg), node, NULL, &spacing, &pixmap_closed, &mask_closed, &pixmap_opened, &mask_opened, &is_leaf, &expanded); gtk_ctree_set_node_info (GTK_CTREE (tree->tree_widg), node, text, spacing, pixmap_closed, mask_closed, pixmap_opened, mask_opened, is_leaf, expanded); } static gboolean is_node_selected (SwamiUITree *tree, GtkCTreeNode *node) { return (g_list_find (GTK_CLIST (tree->tree_widg)->selection, node) != NULL); } static GtkCTreeNode * insert_node (SwamiUITree *tree, gchar *label, gchar **closed_xpm, gchar **opened_xpm, GtkCTreeNode *parent, GtkCTreeNode *sibling) { gchar *text[2] = {NULL, label}; GtkCTreeNode *node = NULL; GdkPixmap *closed_pixmap = NULL, *opened_pixmap = NULL; GdkBitmap *closed_mask = NULL, *opened_mask = NULL; if (closed_xpm) pixmap_get (closed_xpm, &closed_pixmap, &closed_mask); if (!opened_xpm) /* opened XPM NOT specified? */ { opened_pixmap = closed_pixmap; /* use closed pixmap and mask */ opened_mask = closed_mask; } /* get opened pixmap/mask from XPM */ else pixmap_get (opened_xpm, &opened_pixmap, &opened_mask); node = gtk_ctree_insert_node (tree->tree_widg, parent, sibling, text, 2, closed_pixmap, closed_mask, opened_pixmap, opened_mask, FALSE, FALSE); return (node); }