/*================================================================== * SwamiUIObject.c - Swami main user interface 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. * * To contact the author of this program: * Email: Josh Green * Swami homepage: http://swami.sourceforge.net *==================================================================*/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include /* for broken plugins */ #include #include #include /* for broken plugins */ #include "SwamiUIGenView.h" #include "SwamiUIGenCtrl.h" #include "SwamiUIMidiCtrl.h" #include "SwamiUIModEdit.h" #include "SwamiUIMultiList.h" #include "SwamiUIObject.h" #include "SwamiUIProp.h" #include "SwamiUISampleView.h" #include "SwamiUISpanWin.h" #include "SwamiUITree.h" #include "glade_interface.h" #include "help.h" #include "i18n.h" #include "menutbar.h" #include "splash.h" #include "util.h" #include "widgets/multi_filesel.h" #ifdef CANVAS_SUPPORT #include "SwamiUIGenGraph.h" #endif /* signals */ enum { PATCH_LOAD, LAST_SIGNAL }; /* define size for minimum negotiated sound font tree window size */ #define TREEWIN_MIN_WIDTH 260 #define TREEWIN_MIN_HEIGHT 160 /* used if frame sizes are negotiated the max added vertical height of all elements in the swami window except the sound font tree */ #define VERTWIN_JUNK_HEIGHT 300 /* maximum notebook tab length (in characters). Only used for item properties dialog currently. */ #define MAX_NOTEBOOK_TAB_LENGTH 20 enum { CONFIG_QUIT_CONFIRM_ALWAYS, /* always pop a quit confirmation */ CONFIG_QUIT_CONFIRM_UNSAVED, /* only if there are unsaved files */ CONFIG_QUIT_CONFIRM_NEVER /* spontaneous combust */ }; static SwamiConfigStaticVars gui_config_vars[] = { { "gui_state", "win_width", G_TOKEN_INT, {GINT_TO_POINTER (620)} }, { NULL, "win_height", G_TOKEN_INT, {GINT_TO_POINTER (440)} }, { NULL, "treewin_width", G_TOKEN_INT, {GINT_TO_POINTER (0)} }, { NULL, "treewin_height", G_TOKEN_INT, {GINT_TO_POINTER (0)} }, { NULL, "tips_position", G_TOKEN_INT, {GINT_TO_POINTER (0)} }, { NULL, "global_modulators", G_TOKEN_STRING, {""} }, { "gui", "patch_path", G_TOKEN_STRING, {""} }, { NULL, "sample_path", G_TOKEN_STRING, {""} }, { NULL, "quit_confirm", G_TOKEN_INT, {GINT_TO_POINTER (CONFIG_QUIT_CONFIRM_ALWAYS)} }, { NULL, "splash", G_TOKEN_INT, {GINT_TO_POINTER (TRUE)} }, { NULL, "tips", G_TOKEN_INT, {GINT_TO_POINTER (TRUE)} }, { NULL, "save_geometry", G_TOKEN_INT, {GINT_TO_POINTER (TRUE)} }, { NULL, "restore_geometry", G_TOKEN_INT, {GINT_TO_POINTER (FALSE)} }, { NULL, "velbar_scolor", G_TOKEN_STRING, {"#000040"} }, { NULL, "velbar_ecolor", G_TOKEN_STRING, {"#0000FF"} }, { NULL, "sample_left_postfix", G_TOKEN_STRING, {"_L"} }, { NULL, "sample_right_postfix", G_TOKEN_STRING, {"_R"} }, { NULL, "piano_lowoctkeys", G_TOKEN_STRING, {""} }, { NULL, "piano_hioctkeys", G_TOKEN_STRING, {""} } }; #define GUI_CONFIG_VAR_COUNT (sizeof (gui_config_vars) \ / sizeof (SwamiConfigStaticVars)) static char *path_patch_load = NULL; /* last loaded patch path */ static char *path_patch_save = NULL; /* last saved patch path */ static char *path_sample_load = NULL; /* laste sample load path */ static guint swamiui_signals[LAST_SIGNAL] = {0}; /* global variables */ SwamiUIObject *swamiui_object = NULL; SwamiObject *swami_object = NULL; int lowpane_active_view; /* generator clipboard vars */ gboolean gen_clipboard_preset = FALSE; GList *gen_clipboard = NULL; /* list of IPGen values */ /* block spanwin zone selections (stops recursive crap between spanwin and tree selections) */ static gboolean block_spanwin_select = FALSE; /* characters not included at the end of paired stereo sample names */ static char *sample_stereo_skipchars = "\t _-([{"; /* escaped characters in exported sample names */ static char *sample_escchars = "\t\\/|"; /* Local Prototypes */ static void swamiui_object_class_init (SwamiUIObjectClass *klass); static void swamiui_object_init (SwamiUIObject *obj); static void swamiui_init_plugin_guis (void); static void real_quit (void); static gint cb_main_win_delete_event (GtkWidget *widget, GdkEvent *event); static void update_swami_config_state (void); static void restore_global_modulators (void); static gboolean cb_key_event (GtkWidget *widg, GdkEventKey *event, gpointer data); static void cb_tree_item_select_changed (SwamiUITree *tree, IPItem *item); static void cb_span_zone_select (SwamiUISpanWin *spanwin, IPZone *zone); static void swamiui_load_selected_files (GtkWidget *multisel); static void swamiui_cb_load_files_okay (GtkWidget *filewin); static void swamiui_real_patch_load (SwamiUIObject *uiobject, char *file_name); static void cb_close_files_save (SwamiUIMultiList *multi, gpointer func_data); static void cb_close_files_ok (GtkWidget *btn, SwamiUIMultiList *multi); static void cb_close_files_do_all (GtkWidget *btn, SwamiUIMultiList *multi); static void cb_save_files_ok (GtkWidget *btn, gpointer data); static void cb_save_files_browse (SwamiUIMultiList *multi, gpointer func_data); static void cb_save_files_browse_ok (GtkWidget *btn, gpointer data); static void swamiui_cb_item_properties_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, gint pagenum, GtkWidget *dialog); static void swamiui_cb_item_properties_okay (GtkWidget *btn, GtkWidget *dialog); static void swamiui_cb_load_sample_okay (GtkWidget *multisel); static void swamiui_cb_add_samples (GtkWidget *multisel); static void cb_sample_type_selection_done (GtkWidget *menu, gpointer data); static void swamiui_export_samples_ok (GtkWidget *btn, GtkWidget *filesel); static char *make_sample_filename (IPSample *sample, char *path, char *ext); guint swamiui_object_get_type (void) { static guint obj_type = 0; if (!obj_type) { GtkTypeInfo obj_info = { "SwamiUIObject", sizeof (SwamiUIObject), sizeof (SwamiUIObjectClass), (GtkClassInitFunc) swamiui_object_class_init, (GtkObjectInitFunc) swamiui_object_init, (GtkArgSetFunc) NULL, (GtkArgGetFunc) NULL, }; obj_type = gtk_type_unique (swami_object_get_type (), &obj_info); } return obj_type; } static void swamiui_object_class_init (SwamiUIObjectClass *klass) { GtkObjectClass *object_class; object_class = (GtkObjectClass *)klass; swamiui_signals[PATCH_LOAD] = gtk_signal_new ("patch-load", GTK_RUN_LAST, object_class->type, GTK_SIGNAL_OFFSET (SwamiUIObjectClass, patch_load), gtk_marshal_NONE__STRING, GTK_TYPE_NONE, 1, GTK_TYPE_STRING); gtk_object_class_add_signals (object_class, swamiui_signals, LAST_SIGNAL); klass->patch_load = swamiui_real_patch_load; } static void swamiui_object_init (SwamiUIObject *obj) { GtkWidget *widg, *handlebox, *vpane, *hpane, *box; GObject *gobj, *wavetbl; SwamiMidi *midi; int width, height; swamiui_object = obj; swami_object = SWAMI_OBJECT (obj); swami_config_add_domain ("gui", SWAMI_CONFIG_CATEGORY_MAIN); swami_config_add_domain ("gui_state", SWAMI_CONFIG_CATEGORY_STATE); swami_config_add_static_variables (gui_config_vars, GUI_CONFIG_VAR_COUNT); swamiui_object->global_mods = NULL; swamiui_object->enable_global_mods = TRUE; restore_global_modulators (); /* temporary static creation of object types (until a better way is found) */ if (g_type_from_name ("WavetblFluidSynth") != 0) { wavetbl = swami_register_object_new (swami_object, "WavetblFluidSynth"); swami_wavetbl_init_driver (SWAMI_WAVETBL (wavetbl)); gobj = swami_register_object_new (swami_object, "MidiFluidSynth"); g_object_set (gobj, "wavetbl", wavetbl, NULL); swami_midi_init_driver (SWAMI_MIDI (gobj)); } if (g_type_from_name ("SamplelibLibsndfile") != 0) swami_register_object_new (swami_object, "SamplelibLibsndfile"); else if (g_type_from_name ("SamplelibAudiofile") != 0) swami_register_object_new (swami_object, "SamplelibAudiofile"); obj->main_window = create_mainwin (); widg = swamiui_tbar_new (); /* create the toolbar */ gtk_widget_show (widg); handlebox = gtk_object_get_data (GTK_OBJECT (obj->main_window), "HNDLtbar"); gtk_container_add (GTK_CONTAINER (handlebox), widg); /* create MIDI control handle box */ handlebox = gtk_handle_box_new (); gtk_widget_show (handlebox); /* add to toolbar box */ box = gtk_object_get_data (GTK_OBJECT (obj->main_window), "HBoxToolBar"); gtk_box_pack_start (GTK_BOX (box), handlebox, FALSE, FALSE, 0); /* create MIDI control object and add to handle box */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_MIDICTRL)); gtk_container_set_border_width (GTK_CONTAINER (widg), 4); gtk_widget_show (widg); gtk_container_add (GTK_CONTAINER (handlebox), widg); midi = SWAMI_MIDI (swami_get_object_by_type (G_OBJECT (swami_object), "SwamiMidi")); swamiui_midictrl_set_midi_driver (SWAMIUI_MIDICTRL (widg), midi); swamiui_midictrl_set (SWAMIUI_MIDICTRL (widg), "bank", 127); swamiui_midictrl_set (SWAMIUI_MIDICTRL (widg), "preset", 127); vpane = gtk_object_get_data (GTK_OBJECT (obj->main_window), "VPANE"); hpane = gtk_object_get_data (GTK_OBJECT (obj->main_window), "HPANE"); /* restore window's size from config */ gtk_widget_set_usize (obj->main_window, swami_config_get_int ("gui_state", "win_width", NULL), swami_config_get_int ("gui_state", "win_height", NULL)); gtk_signal_connect (GTK_OBJECT (obj->main_window), "delete_event", GTK_SIGNAL_FUNC (cb_main_win_delete_event), NULL); /* we want key release events for piano */ gtk_widget_add_events (obj->main_window, GDK_KEY_RELEASE_MASK); gtk_signal_connect (GTK_OBJECT (obj->main_window), "key-press-event", GTK_SIGNAL_FUNC (cb_key_event), GINT_TO_POINTER (TRUE)); gtk_signal_connect (GTK_OBJECT (obj->main_window), "key-release-event", GTK_SIGNAL_FUNC (cb_key_event), GINT_TO_POINTER (FALSE)); /* does config say to set sound font tree pane size? */ if (swami_config_get_int ("gui", "restore_geometry", NULL)) { width = swami_config_get_int ("gui_state", "treewin_width", NULL); height = swami_config_get_int ("gui_state", "treewin_height", NULL); } else { /* ?: No, force auto sizing */ width = 0; height = 0; } if (!width) { width = swami_config_get_int ("gui_state", "win_width", NULL) - KEYSPAN_WIDTH - 12; if (width < TREEWIN_MIN_WIDTH) width = TREEWIN_MIN_WIDTH; } if (!height) { height = swami_config_get_int ("gui_state", "win_height", NULL) - VERTWIN_JUNK_HEIGHT; if (height < TREEWIN_MIN_HEIGHT) height = TREEWIN_MIN_HEIGHT; } /* set pane separator positions */ gtk_paned_set_position (GTK_PANED (hpane), width); gtk_paned_set_position (GTK_PANED (vpane), height); /* create the tree object and add it to the hpane */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_TREE)); gtk_widget_show (widg); gtk_signal_connect (GTK_OBJECT (widg), "single_select_changed", GTK_SIGNAL_FUNC (cb_tree_item_select_changed), NULL); gtk_paned_pack1 (GTK_PANED (hpane), widg, TRUE, TRUE); /* create spanwin widget */ lowpane_active_view = SWAMIUI_LOWPANE_VIEW; widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_SPANWIN)); SWAMIUI_SPANWIN (widg)->midi = midi; gtk_widget_show (widg); gtk_signal_connect (GTK_OBJECT (widg), "select-zone", GTK_SIGNAL_FUNC (cb_span_zone_select), NULL); gtk_paned_pack2 (GTK_PANED (hpane), widg, TRUE, TRUE); box = gtk_vbox_new (FALSE, 0); gtk_widget_show (box); gtk_paned_pack2 (GTK_PANED (vpane), box, TRUE, TRUE); /* create gen view object */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_GENVIEW)); gtk_widget_show (widg); gtk_box_pack_start (GTK_BOX (box), widg, TRUE, TRUE, 0); /* create gen ctrl object */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_GENCTRL)); gtk_box_pack_start (GTK_BOX (box), widg, TRUE, TRUE, 0); #ifdef CANVAS_SUPPORT /* create gen graph object */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_GENGRAPH)); gtk_box_pack_start (GTK_BOX (box), widg, TRUE, TRUE, 0); #else /* destroy gen graph menu radio button if gengraph is disabled */ widg = gtk_object_get_data (GTK_OBJECT (obj->main_window), "MNUgengraph"); gtk_widget_destroy (widg); #endif /* create sample view object */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_SAMPLEVIEW)); gtk_box_pack_start (GTK_BOX (box), widg, TRUE, TRUE, 0); /* create modulator edit object */ widg = GTK_WIDGET (swamiui_register_object_new (SWAMIUI_TYPE_MODEDIT)); gtk_box_pack_start (GTK_BOX (box), widg, TRUE, TRUE, 0); /* select initial menu items since glade messes it up sometimes */ widg = gtk_object_get_data (GTK_OBJECT (obj->main_window), "MNUgenview"); gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widg), TRUE); widg = gtk_object_get_data (GTK_OBJECT (obj->main_window), "MNUpiano"); gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widg), TRUE); swamiui_init_plugin_guis (); /* initialize plugin GUIs */ gtk_widget_show (obj->main_window); /* pop up swami tip window if enabled */ if (swami_config_get_int ("gui", "tips", NULL)) swamiui_help_swamitips_create (); /* display splash only if not disabled */ if (swami_config_get_int ("gui", "splash", NULL)) swamiui_splash_display (TRUE); /* Display splash with timeout */ } /** * Create a new Swami user interface object * Returns: New Swami user interface object (should really only be called once) */ SwamiUIObject * swamiui_object_new (void) { return (SWAMIUI_OBJECT (gtk_type_new (swamiui_object_get_type ()))); } static void swamiui_init_plugin_guis (void) { GList *plugins, *p; SwamiUIPluginDesc *desc; SwamiPlugin *plugin; plugins = swami_plugin_get_list (); p = plugins; while (p) { plugin = (SwamiPlugin *)(p->data); if (swami_plugin_is_loaded (plugin) && g_module_symbol (plugin->module, "swamiui_plugin_desc", (gpointer *)&desc)) { if (desc->gui_init) if ((*desc->gui_init)(plugin->module, plugin) != SWAMI_OK) g_critical (_("GUI init routine failed for plugin %s"), plugin->name); } p = g_list_next (p); } g_list_free (plugins); } void swamiui_quit (void) { GtkWidget *popup; gboolean unsaved = FALSE; IPItem *item; int quit_confirm; char *s; item = swami_get_patch_list (swami_object); while (item) { if (swami_item_get_boolean (swami_object, item, "changed")) { unsaved = TRUE; break; } item = instp_item_next (item); } quit_confirm = swami_config_get_int ("gui", "quit_confirm", NULL); if (quit_confirm == CONFIG_QUIT_CONFIRM_NEVER || (quit_confirm == CONFIG_QUIT_CONFIRM_UNSAVED && !unsaved)) { real_quit (); return; } if (unsaved) s = _("Unsaved sound fonts, and you want to quit?"); else s = _("Are you sure you want to quit?"); popup = swamiui_util_quick_popup (s, _("Quit"), real_quit, NULL, _("Cancel"), NULL, NULL, NULL); } static void real_quit (void) { /* update variables before saving them */ update_swami_config_state (); swami_config_save (TRUE); /* save config */ #if 0 midi_close (); seq_close (); wtbl_close (); #endif gtk_main_quit (); } static gint cb_main_win_delete_event (GtkWidget *widget, GdkEvent *event) { swamiui_quit (); return (TRUE); } static void cb_tree_item_select_changed (SwamiUITree *tree, IPItem *item) { GtkObject *obj; GObject *wavetbl; IPItem *parent = NULL; if (item && item->type == IPITEM_ZONE) /* if its a zone, fetch parent */ parent = instp_item_parent (item); /* if there is a wavetable driver and item is not a dummy item, load it */ wavetbl = swami_get_object_by_type (G_OBJECT (swami_object), "SwamiWavetbl"); if (wavetbl && item && !swamiui_tree_item_is_dummy (item)) swami_wavetbl_load_temp_item (SWAMI_WAVETBL (wavetbl), parent ? parent : item); if (!block_spanwin_select) { obj = swamiui_lookup_object ("SwamiUISpanWin"); swamiui_spanwin_set_item (SWAMIUI_SPANWIN (obj), parent ? parent : item); if (parent) { gtk_signal_handler_block_by_func (obj, cb_span_zone_select, NULL); swamiui_spanwin_unselect_all_keyspans (SWAMIUI_SPANWIN (obj)); swamiui_spanwin_select_keyspan (SWAMIUI_SPANWIN (obj), INSTP_ZONE (item)); gtk_signal_handler_unblock_by_func (obj, cb_span_zone_select, NULL); } } obj = swamiui_lookup_object ("SwamiUIGenView"); swamiui_genview_set_item (SWAMIUI_GENVIEW (obj), item); obj = swamiui_lookup_object ("SwamiUIGenCtrl"); swamiui_genctrl_set_item (SWAMIUI_GENCTRL (obj), item); #ifdef CANVAS_SUPPORT obj = swamiui_lookup_object ("SwamiUIGenGraph"); swamiui_gengraph_set_item (SWAMIUI_GENGRAPH (obj), item); #endif obj = swamiui_lookup_object ("SwamiUISampleView"); swamiui_sampleview_set_item (SWAMIUI_SAMPLEVIEW (obj), item); obj = swamiui_lookup_object ("SwamiUIModEdit"); swamiui_modedit_set_item (SWAMIUI_MODEDIT (obj), item); } /* callback for when a zone in the span window gets selected */ static void cb_span_zone_select (SwamiUISpanWin *spanwin, IPZone *zone) { GtkObject *obj; obj = swamiui_lookup_object ("SwamiUITree"); block_spanwin_select = TRUE; swamiui_tree_spotlight_item (SWAMIUI_TREE (obj), (IPItem *)zone); block_spanwin_select = FALSE; } static void update_swami_config_state (void) { GtkObject *tree; SwamiUIObject *obj = swamiui_object; IPMod *mod; char *modlist = "", *s = NULL, *sep = ""; /* see if window geometry should be updated */ if (swami_config_get_int ("gui", "save_geometry", NULL) && obj->main_window->window) /* make sure window exists */ { swami_config_set_int ("gui_state", "win_width", obj->main_window->allocation.width); swami_config_set_int ("gui_state", "win_height", obj->main_window->allocation.height); tree = swamiui_lookup_object ("SwamiUITree"); swami_config_set_int ("gui_state", "treewin_width", GTK_WIDGET (tree)->allocation.width); swami_config_set_int ("gui_state", "treewin_height", GTK_WIDGET (tree)->allocation.height); } /* save global modulator list */ mod = swamiui_object->global_mods; while (mod) { s = g_strdup_printf ("%s%s%d %d %d %d %d", modlist, sep, mod->src, mod->dest, mod->amount, mod->amtsrc, mod->trans); g_free (modlist); modlist = s; sep = ","; mod = mod->next; } swami_config_set_string ("gui_state", "global_modulators", modlist); g_free (modlist); } static void restore_global_modulators (void) { char *modstr; char **modstrv; IPMod **mods = &swamiui_object->global_mods, *mod; int i = 0; modstr = swami_config_get_string ("gui_state", "global_modulators"); if (!modstr) return; modstrv = g_strsplit (modstr, ",", -1); while (modstrv[i]) { mod = instp_mod_new (); if (sscanf (modstrv[i], "%hd %hd %hd %hd %hd", &mod->src, &mod->dest, &mod->amount, &mod->amtsrc, &mod->trans) == 5) *mods = instp_mod_list_insert (*mods, mod, -1); else instp_mod_free (mod); i++; } g_strfreev (modstrv); swamiui_update_global_mods (); } static gboolean cb_key_event (GtkWidget *widg, GdkEventKey *event, gpointer data) { SwamiUISpanWin *spanwin; guint8 key; gboolean press = GPOINTER_TO_INT (data); int note; GdkEvent *pevent; if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK)) return (FALSE); spanwin = SWAMIUI_SPANWIN (swamiui_lookup_object ("SwamiUISpanWin")); key = gdk_keyval_to_lower (event->keyval); note = swamiui_spanwin_piano_key_to_note (spanwin, key); if (note == -1) return (FALSE); /* return if not a valid key */ if (press) /* press event */ swamiui_spanwin_piano_note_on (spanwin, note); else { /* release event */ /* hack to weed out auto-repeat events (RELEASE followed immediately by a key PRESS event) */ if (gdk_events_pending ()) { pevent = gdk_event_get (); if (pevent) { /* Auto repeat event? */ if (pevent->type == GDK_KEY_PRESS && ((GdkEventKey *)(pevent))->keyval == event->keyval) { gdk_event_free (pevent); return (FALSE); /* don't do anything */ } /* put event back and fall through to turn note off */ gdk_event_put (pevent); gdk_event_free (pevent); } } swamiui_spanwin_piano_note_off (spanwin, note); } /* release event */ return (FALSE); } /** * Set view window type in lower pane * @view View type to set lower pane to (SWAMIUI_LOWPANE_*) */ void swamiui_lowpane_set_view (int view) { GtkWidget *new_win, *old_win; if (view == lowpane_active_view) return; switch (view) { case SWAMIUI_LOWPANE_VIEW: new_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIGenView")); break; case SWAMIUI_LOWPANE_CTRL: new_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIGenCtrl")); break; #ifdef CANVAS_SUPPORT case SWAMIUI_LOWPANE_GRAPH: new_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIGenGraph")); break; #endif case SWAMIUI_LOWPANE_SAMVIEW: new_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUISampleView")); break; case SWAMIUI_LOWPANE_MODEDIT: new_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIModEdit")); break; default: return; } switch (lowpane_active_view) { case SWAMIUI_LOWPANE_VIEW: old_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIGenView")); break; case SWAMIUI_LOWPANE_CTRL: old_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIGenCtrl")); break; #ifdef CANVAS_SUPPORT case SWAMIUI_LOWPANE_GRAPH: old_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIGenGraph")); break; #endif case SWAMIUI_LOWPANE_SAMVIEW: old_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUISampleView")); break; case SWAMIUI_LOWPANE_MODEDIT: old_win = GTK_WIDGET (swamiui_lookup_object ("SwamiUIModEdit")); break; } gtk_widget_hide (old_win); gtk_widget_show (new_win); lowpane_active_view = view; } /** * Register an existing child object to a Swami UI object * @object Existing GTK object to register * * The child related SwamiUIObject functions link child objects to the * main SwamiUIObject and allow lookup of children by their type. * Child objects are registered to the SwamiUIObject by setting a variable * using the type name in the parent SwamiUIObject and points to a GList * holding one or more objects of that type. */ void swamiui_register_object (GtkObject *object) { GList *p; char *s; g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_OBJECT (object)); s = gtk_type_name (GTK_OBJECT_TYPE (object)); if (s) { /* get existing list (if any) */ p = gtk_object_get_data (GTK_OBJECT (swamiui_object), s); p = g_list_append (p, object); /* append object to list */ gtk_object_set_data (GTK_OBJECT (swamiui_object), s, p); /* store root */ } else SWAMI_CRITICAL ("Parameter 'object' has no type name!"); } /** * Create a new object and register it as a child of the SwamiUI object * @type_name Type name of object to create and register * Returns: The new GtkObject created or NULL on error * \see swamiui_register_object * * Like #swamiui_register_object but creates a new object rather than using * an existing one. */ GtkObject * swamiui_register_object_new (GtkType type) { GtkObject *obj; obj = gtk_type_new (type); if (!obj) return (NULL); swamiui_register_object (obj); return (obj); } /** * Lookup the child list of a given type in the SwamiUI object * @type_name Type name string (example: "SwamiUITree") * Returns: The child list for the given type name or NULL if no items of * that type. List is a copy and should be freed with g_list_free when * finished with it. */ GList * swamiui_lookup_object_list (const char *type_name) { g_return_val_if_fail (type_name != NULL, NULL); return (gtk_object_get_data (GTK_OBJECT (swamiui_object), type_name)); } /** * Get the first child of a given type in a SwamiUI object * @type_name Type name string (example: "SwamiUIMidiCtrl") * Returns: The first object of the given type or NULL if none * \see swamiui_lookup_object_list * * A convenience function to get the first item of the given type, if one * expects only one object and doesn't want to deal with a list. */ GtkObject * swamiui_lookup_object (const char *type_name) { GList *p; p = swamiui_lookup_object_list (type_name); if (p) return ((GtkObject *)(p->data)); else return (NULL); } /** * Apply changes to global modulator list. */ void swamiui_update_global_mods (void) { if (swamiui_object->enable_global_mods) instp_mod_list_apply_global (swamiui_object->global_mods); else instp_mod_list_apply_global (NULL); } /** * Open files routine * * Displays a file selection dialog to open patch files with. */ void swamiui_load_files (void) { GtkWidget *multisel; char *s; if (swamiui_util_activate_unique_dialog ("load_files", 0)) return; multisel = multi_filesel_new (_("Open Files")); swamiui_util_register_unique_dialog (multisel, "load_files", 0); /* if load path isn't set, use default patch path from swami.cfg, duplicate it of course :) */ if (!path_patch_load) { s = swami_config_get_string ("gui", "patch_path"); if (s) path_patch_load = g_strdup (s); } if (path_patch_load && strlen (path_patch_load)) gtk_file_selection_set_filename (GTK_FILE_SELECTION (multisel), path_patch_load); gtk_signal_connect_object (GTK_OBJECT (MULTI_FILESEL (multisel)->add_button), "clicked", GTK_SIGNAL_FUNC (swamiui_load_selected_files), GTK_OBJECT (multisel)); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (multisel)-> ok_button), "clicked", (GtkSignalFunc) swamiui_cb_load_files_okay, GTK_OBJECT (multisel)); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (multisel)-> cancel_button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (multisel)); gtk_widget_show (multisel); } /* loads the list of selected files in a multi file selection widget */ static void swamiui_load_selected_files (GtkWidget *multisel) { GList *files, *p; char *dir, *fname; dir = multi_filesel_get_path (MULTI_FILESEL (multisel)); files = multi_filesel_get_selected_files (MULTI_FILESEL (multisel)); g_free (path_patch_load); /* free old load path */ path_patch_load = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL); p = files; while (p) { fname = g_strconcat (dir, G_DIR_SEPARATOR_S, (char *)(p->data), NULL); gtk_signal_emit (GTK_OBJECT (swamiui_object), swamiui_signals[PATCH_LOAD], fname); g_free (fname); p = g_list_next (p); } g_list_free (files); g_free (dir); } /* callback for okay button on open file selection dialog */ static void swamiui_cb_load_files_okay (GtkWidget *multisel) { swamiui_load_selected_files (multisel); gtk_widget_destroy (multisel); /* destroy file sel dialog on success */ } /* SwamiUIObject Class load patch routine */ static void swamiui_real_patch_load (SwamiUIObject *uiobject, char *file_name) { swami_patch_load (swami_object, file_name); } /** * Close files user interface * @items List of patch items to close (only IPSFont used currently). */ void swamiui_close_files (GList *items) { GtkWidget *multi; GtkWidget *btn; GtkCList *list; GList *p, *newlist = NULL; IPItem *item; gboolean patch_found = FALSE; char *text[4]; gboolean changed, saved; int row; /* see if there are any patch items to close and if they have been changed */ p = items; while (p) { item = INSTP_ITEM (p->data); if (item->type == IPITEM_SFONT) { patch_found = TRUE; if (swami_item_get_boolean (swami_object, item, "changed")) break; } p = g_list_next (p); } if (!patch_found) return; /* no patches to close, return */ /* if no items changed, then go ahead and close files */ if (!p) { p = items; while (p) { item = INSTP_ITEM (p->data); if (item->type == IPITEM_SFONT) /* removing, closes sound font */ swami_item_remove (swami_object, item); p = g_list_next (p); } return; } /* item(s) have been changed, pop user interactive dialog */ multi = swamiui_multilist_new (_("Close files"), _("Save changed files before closing?"), 4, _("Save"), _("Status"), _("Name"), _("File")); swamiui_multilist_new_listbtn (SWAMIUI_MULTILIST (multi), _("Yes"), cb_close_files_save, GINT_TO_POINTER (TRUE)); swamiui_multilist_new_listbtn (SWAMIUI_MULTILIST (multi), _("No"), cb_close_files_save, GINT_TO_POINTER (FALSE)); swamiui_multilist_new_listbtn (SWAMIUI_MULTILIST (multi), _("Browse"), cb_save_files_browse, GINT_TO_POINTER (3)); /* col for Filename */ gtk_signal_connect (GTK_OBJECT (SWAMIUI_MULTILIST (multi)->ok_button), "clicked", cb_close_files_ok, multi); btn = gtk_button_new_with_label (_("Save All")); gtk_object_set_data (GTK_OBJECT (btn), "save", GINT_TO_POINTER (TRUE)); gtk_signal_connect (GTK_OBJECT (btn), "clicked", GTK_SIGNAL_FUNC (cb_close_files_do_all), multi); gtk_box_pack_start (GTK_BOX (SWAMIUI_MULTILIST (multi)->action_btnbox), btn, FALSE, FALSE, 0); btn = gtk_button_new_with_label (_("Discard All")); gtk_object_set_data (GTK_OBJECT (btn), "save", GINT_TO_POINTER (FALSE)); gtk_signal_connect (GTK_OBJECT (btn), "clicked", GTK_SIGNAL_FUNC (cb_close_files_do_all), multi); gtk_box_pack_start (GTK_BOX (SWAMIUI_MULTILIST (multi)->action_btnbox), btn, FALSE, FALSE, 0); list = GTK_CLIST (SWAMIUI_MULTILIST (multi)->clist); /* create the rows in the multi list */ p = items; while (p) { item = (IPItem *)(p->data); if (item->type == IPITEM_SFONT) { newlist = g_list_append (newlist, item); /* add to temp list */ changed = swami_item_get_boolean (swami_object, item, "changed"); saved = swami_item_get_boolean (swami_object, item, "saved"); text[0] = (changed && saved) ? _("Yes") : _("No"); if (!changed) text[1] = _("Not changed"); else if (!saved) text[1] = _("First save"); text[2] = swami_item_get_string (swami_object, item, "name"); text[3] = swami_item_get_string (swami_object, item, "file_name"); row = gtk_clist_append (list, text); /* use row data as a boolean to indicate save or not */ gtk_clist_set_row_data (list, row, GINT_TO_POINTER ((changed && saved))); } p = g_list_next (p); } /* reference the list of items for the duration of the multi list dialog */ swamiui_multilist_set_items (SWAMIUI_MULTILIST (multi), newlist); gtk_widget_show (multi); } /* callback for Yes and No save choice buttons */ static void cb_close_files_save (SwamiUIMultiList *multi, gpointer func_data) { gboolean save = GPOINTER_TO_INT (func_data); GtkCList *clist; GList *sel; int row; clist = GTK_CLIST (multi->clist); sel = clist->selection; while (sel) { row = GPOINTER_TO_INT (sel->data); gtk_clist_set_row_data (clist, row, GINT_TO_POINTER (save)); gtk_clist_set_text (clist, row, 0, /* <- column for Save Yes/No */ save ? _("Yes") : ("No")); sel = g_list_next (sel); } } /* "OK" button callback for close file multi list object */ static void cb_close_files_ok (GtkWidget *btn, SwamiUIMultiList *multi) { GList *p; IPItem *item; char *filename; int row = 0; /* FIXME! - More error checking.. */ p = multi->items; while (p) { item = INSTP_ITEM (p->data); /* row data used as boolean to indicate save or not */ if (gtk_clist_get_row_data (GTK_CLIST (multi->clist), row) && gtk_clist_get_text (GTK_CLIST (multi->clist), row, 3, /* <- column for filename */ &filename)) swami_patch_save (swami_object, item, filename); swami_item_remove (swami_object, item); /* close the patch */ row++; p = g_list_next (p); } gtk_widget_destroy (GTK_WIDGET (multi)); } /* callback for the "Save All" or "Discard All" buttons */ static void cb_close_files_do_all (GtkWidget *btn, SwamiUIMultiList *multi) { GtkCList *clist; GList *p; gboolean save; int row; save = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (btn), "save")); clist = GTK_CLIST (multi->clist); p = clist->selection; while (p) { row = GPOINTER_TO_INT (p->data); gtk_clist_set_row_data (clist, row, GINT_TO_POINTER (save)); gtk_clist_set_text (clist, row, 0, /* <- column for Save Yes/No */ save ? _("Yes") : ("No")); p = g_list_next (p); } cb_close_files_ok (btn, multi); /* use ok callback to save/close files */ } /** * Save files user interface * @items List of items to save (only IPITEM_SFONT items used currently) * @saveas TRUE forces popup of dialog even if all files have previously * been saved (forces "Save As"). */ void swamiui_save_files (GList *items, gboolean saveas) { GtkWidget *multi; GtkCList *list; GList *p, *newlist = NULL; IPItem *item; char *text[2], *filename; gboolean popup = FALSE, match = FALSE; int row; /* see if any items have been changed */ p = items; while (p) { item = (IPItem *)(p->data); if (item->type == IPITEM_SFONT) /* only save sound font items */ { match = TRUE; /* found a sound font item */ if (!swami_item_get_boolean (swami_object, item, "saved")) popup = TRUE; /* file has never been saved, force dialog */ } p = g_list_next (p); } if (!match) return; /* return, if there are no items to save */ popup |= saveas; /* force dialog popup, "Save As"? */ /* no dialog required? (all items previously saved and !saveas) */ if (!popup) { p = items; while (p) { item = (IPItem *)(p->data); filename = swami_item_get_string (swami_object, item, "file_name"); swami_patch_save (swami_object, item, filename); g_free (filename); p = g_list_next (p); } return; } /* item(s) have been changed, pop user interactive dialog */ multi = swamiui_multilist_new (_("Save files"), _("Save files"), 2, _("Name"), _("File")); gtk_signal_connect (GTK_OBJECT (SWAMIUI_MULTILIST (multi)->ok_button), "clicked", cb_save_files_ok, multi); swamiui_multilist_new_listbtn (SWAMIUI_MULTILIST (multi), _("Browse"), cb_save_files_browse, GINT_TO_POINTER (1)); list = GTK_CLIST (SWAMIUI_MULTILIST (multi)->clist); /* create the rows in the multi list */ p = items; while (p) { item = (IPItem *)(p->data); if (item->type == IPITEM_SFONT) { newlist = g_list_append (newlist, item); /* add to temp list */ text[0] = swami_item_get_string (swami_object, item, "name"); text[1] = swami_item_get_string (swami_object, item, "file_name"); row = gtk_clist_append (list, text); } p = g_list_next (p); } /* reference the list of items for the duration of the multi list dialog */ swamiui_multilist_set_items (SWAMIUI_MULTILIST (multi), newlist); gtk_widget_show (multi); } /* "OK" button callback for save file multi list object */ static void cb_save_files_ok (GtkWidget *btn, gpointer data) { SwamiUIMultiList *multi = SWAMIUI_MULTILIST (data); GList *p; char *filename; int row = 0; /* FIXME! - More error checking.. */ p = multi->items; while (p) { if (gtk_clist_get_text (GTK_CLIST (multi->clist), row, 1, &filename)) swami_patch_save (swami_object, INSTP_ITEM (p->data), filename); row++; p = g_list_next (p); } gtk_widget_destroy (GTK_WIDGET (multi)); } /* "Browse" button callback for save file multi list object */ static void cb_save_files_browse (SwamiUIMultiList *multi, gpointer func_data) { GtkWidget *filesel; GList *sel; int row; char *s; sel = GTK_CLIST (multi->clist)->selection; if (!sel) return; row = GPOINTER_TO_INT (sel->data); filesel = gtk_file_selection_new (_("Set file name")); gtk_object_set_data (GTK_OBJECT (filesel), "multi", multi); gtk_object_set_data (GTK_OBJECT (filesel), "row", GINT_TO_POINTER (row)); /* func_data is actually an integer of the list column of the filename */ gtk_object_set_data (GTK_OBJECT (filesel), "col", func_data); /* if save path isn't set, use default patch path from config */ if (!path_patch_save) { s = swami_config_get_string ("gui", "patch_path"); if (s) path_patch_save = g_strdup (s); } if (path_patch_save && strlen (path_patch_save)) gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), path_patch_save); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), "clicked", GTK_SIGNAL_FUNC (cb_save_files_browse_ok), filesel); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel)); gtk_widget_show (filesel); } /* callback for okay button on save file selector dialog */ static void cb_save_files_browse_ok (GtkWidget *btn, gpointer data) { GtkWidget *filesel = GTK_WIDGET (data); SwamiUIMultiList *multi; char *filename, *dir; int row, col; multi = SWAMIUI_MULTILIST (gtk_object_get_data (GTK_OBJECT (filesel), "multi")); row = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (filesel), "row")); col = GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (filesel), "col")); filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)); gtk_clist_set_text (GTK_CLIST (multi->clist), row, col, filename); dir = g_dirname (filename); if (dir && strlen (dir)) { g_free (path_patch_save); path_patch_save = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL); } g_free (dir); gtk_widget_destroy (filesel); } /** * Delete patch items * @items List of items to delete */ void swamiui_delete_items (GList *items) { GList *p, *p2, *refitems; IPItem *item, *refitem; swamiui_tree_freeze_all (); p = items; while (p) { item = (IPItem *)(p->data); if (!swamiui_tree_item_is_dummy (item) && item->type != IPITEM_SFONT) { if (item->type == IPITEM_INST || item->type == IPITEM_SAMPLE) { refitems = swami_item_get_zone_references (swami_object, item); p2 = refitems; while (p2) { refitem = (IPItem *)(p2->data); swami_item_remove (swami_object, refitem); p2 = g_list_next (p2); } } swami_item_remove (swami_object, item); } p = g_list_next (p); } swamiui_tree_thaw_all (); } /** * Load a patch item * @item Patch to load into wavetable. */ void swamiui_wtbl_load_patch (IPItem *item) { GObject *wavetbl; /* sound fonts only please */ if (!INSTP_IS_SFONT (item)) return; wavetbl = swami_get_object_by_type (G_OBJECT (swami_object), "SwamiWavetbl"); if (wavetbl) swami_wavetbl_load_patch (SWAMI_WAVETBL (wavetbl), item); } /** * Patch item properties user interface * @items List of items to edit properties of. */ void swamiui_item_properties (GList *items) { GList *p; IPItem *item; GtkWidget *dialog; GtkWidget *notebook; GtkWidget *btn; GtkWidget *prop; GtkWidget *label; gboolean valid_item = FALSE; char *s; dialog = gtk_dialog_new (); gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), 4); notebook = gtk_notebook_new (); gtk_notebook_set_scrollable (GTK_NOTEBOOK (notebook), TRUE); gtk_notebook_popup_enable (GTK_NOTEBOOK (notebook)); gtk_signal_connect (GTK_OBJECT (notebook), "switch-page", GTK_SIGNAL_FUNC (swamiui_cb_item_properties_switch_page), dialog); gtk_widget_show (notebook); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), notebook, TRUE, TRUE, 0); gtk_object_set_data (GTK_OBJECT (dialog), "notebook", notebook); label = gtk_label_new (NULL); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, FALSE, FALSE, 2); gtk_object_set_data (GTK_OBJECT (dialog), "err_lbl", label); btn = gtk_button_new_with_label (_("OK")); gtk_signal_connect (GTK_OBJECT (btn), "clicked", GTK_SIGNAL_FUNC (swamiui_cb_item_properties_okay), dialog); gtk_widget_show (btn); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), btn, TRUE, FALSE, 0); btn = gtk_button_new_with_label (_("Cancel")); gtk_signal_connect_object (GTK_OBJECT (btn), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (dialog)); gtk_widget_show (btn); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), btn, TRUE, FALSE, 0); p = items; while (p) { item = INSTP_ITEM (p->data); if (swamiui_prop_item_supported (item->type)) { valid_item = TRUE; prop = swamiui_prop_new (); gtk_container_set_border_width (GTK_CONTAINER (prop), 2); swamiui_prop_set_item (SWAMIUI_PROP (prop), item); gtk_widget_show (prop); s = swami_item_get_formatted_name (swami_object, item); /* truncate string if greater than max notebook tab length */ if (strlen (s) > MAX_NOTEBOOK_TAB_LENGTH) { char *s2, *s3; int half = (MAX_NOTEBOOK_TAB_LENGTH - 2) / 2; s2 = g_strndup (s, half); s3 = g_strconcat (s2, "..", &s[strlen (s) - half], NULL); g_free (s2); g_free (s); s = s3; } label = gtk_label_new (s); g_free (s); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), prop, label); } p = g_list_next (p); } if (!valid_item) { gtk_widget_destroy (dialog); return; } gtk_widget_show (dialog); } /* callback for item properties notebook switch page signal */ static void swamiui_cb_item_properties_switch_page (GtkNotebook *notebook, GtkNotebookPage *page, gint pagenum, GtkWidget *dialog) { SwamiUIProp *prop; GtkWidget *label; char *err_msg; label = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dialog), "err_lbl")); prop = SWAMIUI_PROP (page->child); err_msg = gtk_object_get_data (GTK_OBJECT (prop), "err_msg"); /* if this page has an error, display it, otherwise hide label */ if (err_msg) { gtk_label_set_text (GTK_LABEL (label), err_msg); gtk_widget_show (label); } else gtk_widget_hide (label); } /* callback for item properties OK button clicked */ static void swamiui_cb_item_properties_okay (GtkWidget *btn, GtkWidget *dialog) { GList *children, *p; GtkWidget *notebook; SwamiUIProp *prop; char *err_msg; gboolean problem = FALSE; notebook = GTK_WIDGET (gtk_object_get_data (GTK_OBJECT (dialog),"notebook")); children = gtk_container_children (GTK_CONTAINER (notebook)); p = children; while (p) { prop = SWAMIUI_PROP (p->data); /* commit properties to item, if fail store error message, otherwise remove page from notebook */ if (!swamiui_prop_commit (prop, &err_msg)) { problem = TRUE; gtk_object_set_data (GTK_OBJECT (prop), "err_msg", err_msg); } else gtk_container_remove (GTK_CONTAINER (notebook), GTK_WIDGET (prop)); p = g_list_next (p); } g_list_free (children); if (!problem) /* if no problems, destroy dialog */ gtk_widget_destroy (dialog); } /** * Create a new patch item * @parent_hint The parent of the new item or a hint item. An example of * a hint item is a SWAMIUI_TREE_PRESET_MELODIC item which would allow the * real IPPreset parent to be found, and would also indicate that the new * zone should be in the melodic branch. Can (and should be) NULL for * toplevel patch objects (IPSFont, etc). * @type An IPItemType of the item to create. * Returns: The new item or NULL on error */ IPItem * swamiui_new_item (IPItem *parent_hint, int type) { IPItem *new_item, *parent = NULL; if (!parent_hint && type != IPITEM_SFONT) return (NULL); /* determine parent of new item */ if (type == IPITEM_ZONE) { if (parent_hint->type == IPITEM_ZONE) parent = instp_item_parent (parent_hint); else if (parent_hint->type == IPITEM_PRESET || parent_hint->type == IPITEM_INST) parent = parent_hint; else return (NULL); } else if (type != IPITEM_SFONT) { parent = instp_item_find_root (parent_hint); if (parent->type != IPITEM_SFONT) return (NULL); } /* check if item type is a preset and hint relates to percussion branch */ if (type == IPITEM_PRESET && (parent_hint->type == SWAMIUI_TREE_PRESET_PERCUSS || (parent_hint->type == IPITEM_PRESET && swami_item_get_int (swami_object, parent_hint, "bank") == 128))) new_item = swami_item_new (swami_object, type, parent, "bank", 128, NULL); /* new percussion preset */ else /* create new item */ new_item = swami_item_new (swami_object, type, parent, NULL); return (new_item); } /** * Goto a zone's referenced item in a #SwamiUITree object * @zone Zone to find and goto it's referenced item. * @tree Swami tree object * * Moves the view and selects the item in a #SwamiUITree that is referenced * by a zone. */ void swamiui_goto_zone_refitem (IPZone *zone, SwamiUITree *tree) { IPItem *refitem; g_return_if_fail (zone != NULL); g_return_if_fail (tree != NULL); if (!(refitem = swami_item_get_pointer (swami_object, INSTP_ITEM (zone), "refitem"))) return; /* clear rclick item since it points to the old node */ swamiui_tree_set_rclick_item (tree, NULL); swamiui_tree_spotlight_item (tree, refitem); } /** * Load sample user interface * @parent_hint Parent of new sample or a child thereof. */ void swamiui_load_samples (IPItem *parent_hint) { GtkWidget *multisel; IPItem *parent; char *s; g_return_if_fail (parent_hint != NULL); parent = instp_item_find_root (parent_hint); if (!parent || !INSTP_IS_SFONT (parent)) return; multisel = multi_filesel_new (_("Load samples")); /* if sample load path isn't set, use default from config */ if (!path_sample_load) { s = swami_config_get_string ("gui", "sample_path"); if (s) path_sample_load = g_strdup (s); } if (path_sample_load && strlen (path_sample_load)) gtk_file_selection_set_filename (GTK_FILE_SELECTION (multisel), path_sample_load); instp_item_ref (parent); gtk_object_set_data_full (GTK_OBJECT (multisel), "item", parent, (GtkDestroyNotify)instp_item_unref); gtk_signal_connect_object (GTK_OBJECT (MULTI_FILESEL (multisel)->add_button), "clicked", GTK_SIGNAL_FUNC (swamiui_cb_add_samples), GTK_OBJECT (multisel)); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (multisel)->ok_button), "clicked", GTK_SIGNAL_FUNC (swamiui_cb_load_sample_okay), GTK_OBJECT (multisel)); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (multisel)->cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (multisel)); gtk_widget_show (multisel); } /* callback for okay button on sample load multi selection dialog */ static void swamiui_cb_load_sample_okay (GtkWidget *multisel) { swamiui_cb_add_samples (multisel); gtk_widget_destroy (multisel); } /* routine that loads the samples from the multisel object */ static void swamiui_cb_add_samples (GtkWidget *multisel) { GList *files, *p; IPItem *left_sample, *right_sample, *parent; SwamiSamplelib *samplelib; SwamiSamplelibHandle *handle; IPSampleData *left_data, *right_data; gboolean loaded = FALSE; char *dir, *filename, *name, *s; char *lstr, *rstr; int i; parent = gtk_object_get_data (GTK_OBJECT (multisel), "item"); samplelib = SWAMI_SAMPLELIB (swami_get_object_by_type (G_OBJECT (swami_object), "SwamiSamplelib")); g_return_if_fail (samplelib != NULL); lstr = swami_config_get_string ("gui", "sample_left_postfix"); if (!lstr || !strlen (lstr)) lstr = "_L"; rstr = swami_config_get_string ("gui", "sample_right_postfix"); if (!rstr || !strlen (rstr)) rstr = "_R"; dir = multi_filesel_get_path (MULTI_FILESEL (multisel)); files = multi_filesel_get_selected_files (MULTI_FILESEL (multisel)); p = files; while (p) { name = (char *)(p->data); p = g_list_next (p); filename = g_strconcat (dir, G_DIR_SEPARATOR_S, name, NULL); handle = swami_samplelib_open (samplelib, filename, 'r', NULL); g_free (filename); if (!handle) continue; if (swami_samplelib_load_sampledata (handle, &left_data, &right_data) != INSTP_OK) { swami_samplelib_close (handle); continue; } loaded = TRUE; /* only set sample path on successful load */ /* get a name for the samples by stripping off the extension, etc */ i = strlen (name); s = strrchr (name, '.'); if (s) i = (s - name); if (i > 12) i = 12; /* ! max name length ! */ if (i > 0) name = g_strndup (name, i); else name = g_strndup (_("sample"), 12); /* ! max name length ! */ if (left_data) { if (right_data) s = g_strconcat (name, lstr, NULL); else s = name; left_sample = swami_item_new (swami_object, IPITEM_SAMPLE, parent, "name", s, NULL); if (right_data) g_free (s); instp_sample_set_sample_data (INSTP_SAMPLE (left_sample), left_data); swami_samplelib_init_sample (handle, INSTP_SAMPLE (left_sample)); } if (right_data) { s = g_strconcat (name, rstr, NULL); right_sample = swami_item_new (swami_object, IPITEM_SAMPLE, parent, "name", s, NULL); g_free (s); instp_sample_set_sample_data (INSTP_SAMPLE (right_sample), right_data); swami_samplelib_init_sample (handle, INSTP_SAMPLE (right_sample)); } g_free (name); swami_samplelib_close (handle); } if (loaded) { g_free (path_sample_load); /* free old load path */ path_sample_load = g_strconcat (dir, G_DIR_SEPARATOR_S, NULL); } g_free (dir); g_list_free (files); } /** * swamiui_export_samples: * @items: List of sample items to export to files. */ void swamiui_export_samples (GList *items) { GtkWidget *filesel; GtkWidget *opmenu; GtkWidget *menu; GtkWidget *mitem; GtkWidget *hbox; GtkWidget *label; GList *newlist; filesel = gtk_file_selection_new (_("Choose directory")); /* pack a box with file type option menu */ hbox = gtk_hbox_new (FALSE, 4); gtk_box_pack_start (GTK_BOX (GTK_FILE_SELECTION (filesel)->main_vbox), hbox, FALSE, FALSE, 0); label = gtk_label_new ("File type:"); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); opmenu = gtk_option_menu_new (); gtk_widget_show (opmenu); gtk_box_pack_start (GTK_BOX (hbox), opmenu, FALSE, TRUE, 0); gtk_object_set_data (GTK_OBJECT (filesel), "opmenu", opmenu); label = gtk_label_new ("Note: Loop/tuning info saved only with" " libaudiofile and AIFF files"); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0); menu = gtk_menu_new (); /* hard coded file types, woo hoo! */ mitem = gtk_menu_item_new_with_label ("AIFF"); gtk_object_set_data (GTK_OBJECT (mitem), "type", GUINT_TO_POINTER (SWAMI_SAMPLELIB_TYPE_AIFF)); gtk_widget_show (mitem); gtk_menu_append (GTK_MENU (menu), mitem); mitem = gtk_menu_item_new_with_label ("WAV"); gtk_object_set_data (GTK_OBJECT (mitem), "type", GUINT_TO_POINTER (SWAMI_SAMPLELIB_TYPE_WAVE)); gtk_widget_show (mitem); gtk_menu_append (GTK_MENU (menu), mitem); mitem = gtk_menu_item_new_with_label ("AU"); gtk_object_set_data (GTK_OBJECT (mitem), "type", GUINT_TO_POINTER (SWAMI_SAMPLELIB_TYPE_AU)); gtk_widget_show (mitem); gtk_menu_append (GTK_MENU (menu), mitem); gtk_option_menu_set_menu (GTK_OPTION_MENU (opmenu), menu); /* ugggh, GTK 1.2 really sucked with the option menu */ gtk_signal_connect (GTK_OBJECT (menu), "selection-done", GTK_SIGNAL_FUNC (cb_sample_type_selection_done), filesel); gtk_widget_show_all (hbox); newlist = g_list_copy (items); gtk_object_set_data_full (GTK_OBJECT (filesel), "items", newlist, (GtkDestroyNotify)g_list_free); /* default file type */ gtk_object_set_data (GTK_OBJECT (filesel), "type", GUINT_TO_POINTER (SWAMI_SAMPLELIB_TYPE_AIFF)); gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button), "clicked", (GtkSignalFunc) swamiui_export_samples_ok, filesel); gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)-> cancel_button), "clicked", (GtkSignalFunc) gtk_widget_destroy, GTK_OBJECT (filesel)); gtk_widget_show (filesel); } /* much hate and death to the fucking GTK 1.2 option menu */ static void cb_sample_type_selection_done (GtkWidget *menu, gpointer data) { GtkWidget *filesel = GTK_WIDGET (data); GtkWidget *mitem; int filetype; mitem = gtk_menu_get_active (GTK_MENU (menu)); filetype = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (mitem), "type")); gtk_object_set_data (GTK_OBJECT (filesel), "type", GUINT_TO_POINTER (filetype)); } /* callback when user clicks OK on sample export file (directory) selector */ static void swamiui_export_samples_ok (GtkWidget *btn, GtkWidget *filesel) { const gchar *filename; IPSample *sample; IPSampleData *left, *right; SwamiSamplelibParams params; SwamiSamplelib *samplelib; SwamiSamplelibHandle *handle; GHashTable *saved_links; /* prevent multiple saves of stereo pairs */ GList *items, *p; struct stat buf; char *path, *filepath; char *ext; int filetype; samplelib = SWAMI_SAMPLELIB (swami_get_object_by_type (G_OBJECT (swami_object), "SwamiSamplelib")); g_return_if_fail (samplelib != NULL); saved_links = g_hash_table_new (NULL, NULL); filename = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)); path = g_dirname (filename); /* !! allocated string */ items = gtk_object_get_data (GTK_OBJECT (filesel), "items"); filetype = GPOINTER_TO_UINT (gtk_object_get_data (GTK_OBJECT (filesel), "type")); ext = swami_samplelib_type_ext (filetype); /* check for write access to the path */ if (stat (path, &buf) != 0 || !S_ISDIR (buf.st_mode) || access (path, W_OK) != 0) { g_critical ("Selected path not a directory or no write access"); g_free (path); } for (p = items; p; p = p->next) { /* not a sample or already saved its stereo linked sample? - skip */ if (!INSTP_IS_SAMPLE (p->data) || g_hash_table_lookup (saved_links, p->data)) continue; sample = INSTP_SAMPLE (p->data); swami_samplelib_set_params_from_sample (sample, ¶ms); params.file_type = filetype; /* make a unique filename for the sample */ filepath = make_sample_filename (sample, path, ext); handle = swami_samplelib_open (samplelib, filepath, 'w', ¶ms); if (!handle) { g_critical ("Failed to create sample file '%s'", filepath); g_free (filepath); continue; } if (!sample->linked) { left = sample->sampledata; right = NULL; } else /* stereo */ { if (sample->sampletype & IPSAMPLE_TYPE_RIGHT) { right = sample->sampledata; left = sample->linked->sampledata; } else { left = sample->sampledata; right = sample->linked->sampledata; } g_hash_table_insert (saved_links, sample->linked, GUINT_TO_POINTER (TRUE)); } if (swami_samplelib_save_sampledata (handle, left, right) != SWAMI_OK) { swami_samplelib_close (handle); g_critical ("Failed to save sample data to file '%s'", filename); continue; } swami_samplelib_close (handle); } g_free (path); g_hash_table_destroy (saved_links); gtk_widget_destroy (filesel); } static char * make_sample_filename (IPSample *sample, char *path, char *ext) { struct stat buf; char *filepath; char *name; char *s1, *s2; char numstr[6]; int len, i; if (sample->linked) /* stereo? */ { /* attempt to strip off any L/R postfixes by using identical portions of sample names */ s1 = sample->name; s2 = sample->linked->name; for (; *s1 && *s1 == *s2; s1++, s2++); /* find matching portions */ if (s1 > sample->name) /* anything in common? */ { /* find last character not in skip chars */ do { s1--; if (!strchr (sample_stereo_skipchars, *s1)) { s1++; break; } } while (s1 > sample->name); } if (s1 > sample->name) name = g_strndup (sample->name, s1 - sample->name); else /* nothing in common? - use this sample's name */ name = g_strdup (sample->name); } else /* mono */ name = g_strdup (sample->name); if (strlen (name) == 0) { g_free (name); name = g_strdup ("untitled"); } /* escape unwanted characters */ for (s1 = name; *s1; s1++) if (strchr (sample_escchars, *s1)) *s1 = '_'; /* construct complete path with 5 chars padding in case a unique number needs to be appended */ filepath = g_strconcat (path, G_DIR_SEPARATOR_S, name, ".", ext, " ", NULL); len = strlen (filepath); filepath[len - 5] = '\0'; /* kill padding for now */ /* already exists? - find a unique number to append */ if (stat (filepath, &buf) == 0) { filepath[len - 5] = ' '; /* set back to space */ filepath[len - strlen (ext) - 6] = '_'; filepath[len - strlen (ext) - 1] = '.'; strcpy (&filepath[len - strlen (ext)], ext); s1 = &filepath[len - strlen (ext) - 5]; for (i = 0; i < 10000; i++) { sprintf (numstr, "%04u", i); strncpy (s1, numstr, 4); if (stat (filepath, &buf) != 0) break; } if (i == 10000) { g_critical ("Failed to find unique sample file name for '%s'", name); g_free (filepath); g_free (name); return (NULL); } } g_free (name); return (filepath); } /** * swamiui_unset_gens: * @items: List of patch items * * Unsets the generators for a list of zones. Only the general purpose set * of generators is cleared (ones displayed in #SwamiUIGenView objects). */ void swamiui_unset_gens (GList *items) { guint8 *genids; int count, i; GList *p; count = swamiui_genview_get_genids (&genids); p = items; while (p) { if (INSTP_IS_ZONE (p->data)) { for (i=0; i < count; i++) instp_zone_unset_gen ((IPZone *)(p->data), genids[i]); g_signal_emit_by_name (swami_object, "zone_gen_change", p->data); } p = g_list_next (p); } } /** * swamiui_copy_gens: * @item: #IPZone to copy gens from * * Copy general purpose generators (the ones displayed in #SwamiUIGenView * objects) of a zone to the generator clipboard. */ void swamiui_copy_gens (IPItem *item) { IPZone *zone; IPGenAmount amt; guint8 *genids; int count, i; GList *p; if (!INSTP_IS_ZONE (item)) return; /* clear generator clipboard */ p = gen_clipboard; while (p) { instp_gen_free ((IPGen *)(p->data)); p = g_list_next (p); } g_list_free (gen_clipboard); gen_clipboard = NULL; zone = INSTP_ZONE (item); count = swamiui_genview_get_genids (&genids); /* set preset boolean variable depending on if this is a preset/inst zone */ gen_clipboard_preset = INSTP_IS_PARENT_PRESET (zone); for (i=0; i < count; i++) { if (instp_zone_get_gen (zone, genids[i], &amt)) /* if gen set */ { /* add to clipboard */ IPGen *gen = instp_gen_new (); gen->id = genids[i]; gen->amount = amt; gen_clipboard = g_list_append (gen_clipboard, gen); } } } /** * swamiui_paste_gens: * @item: #IPZone to paste gens into * * Sets, and unsets, generators of a zone to those in the generator * clipboard. */ void swamiui_paste_gens (IPItem *item) { IPZone *zone; IPGen *gen = NULL; guint8 *genids; int count, i; GList *p; if (!INSTP_IS_ZONE (item) || gen_clipboard_preset != INSTP_IS_PARENT_PRESET (item)) return; zone = INSTP_ZONE (item); count = swamiui_genview_get_genids (&genids); p = gen_clipboard; if (p) gen = (IPGen *)(p->data); for (i=0; i < count; i++) { if (gen && gen->id == genids[i]) { instp_zone_set_gen (zone, genids[i], gen->amount); p = p->next; gen = p ? (IPGen *)(p->data) : NULL; } else instp_zone_unset_gen (zone, genids[i]); } g_signal_emit_by_name (swami_object, "zone_gen_change", zone); }