--- gigedit/trunk/src/gigedit/mainwindow.cpp 2017/05/11 20:59:46 3177 +++ gigedit/trunk/src/gigedit/mainwindow.cpp 2017/08/02 10:39:46 3344 @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -62,6 +63,16 @@ #include "gfx/builtinpix.h" #include "MacroEditor.h" #include "MacrosSetup.h" +#if defined(__APPLE__) +# include "MacHelper.h" +#endif + +static const Gdk::ModifierType primaryModifierKey = + #if defined(__APPLE__) + Gdk::META_MASK; // Cmd key on Mac + #else + Gdk::CONTROL_MASK; // Ctrl key on all other OSs + #endif MainWindow::MainWindow() : m_DimRegionChooser(*this), @@ -77,8 +88,14 @@ { loadBuiltInPix(); + this->file = NULL; + // set_border_width(5); -// set_default_size(400, 200); + + if (!Settings::singleton()->autoRestoreWindowDimension) { + set_default_size(800, 600); + set_position(Gtk::WIN_POS_CENTER); + } add(m_VBox); @@ -105,7 +122,16 @@ m_TreeViewNotebook.set_size_request(300); - m_HPaned.add1(m_TreeViewNotebook); + m_searchLabel.set_text(Glib::ustring(" ") + _("Filter:")); + m_searchField.pack_start(m_searchLabel, Gtk::PACK_SHRINK); + m_searchField.pack_start(m_searchText); + m_searchField.set_spacing(5); + + m_left_vbox.pack_start(m_TreeViewNotebook); + m_left_vbox.pack_start(m_searchField, Gtk::PACK_SHRINK); + + m_HPaned.add1(m_left_vbox); + dimreg_hbox.add(dimreg_label); dimreg_hbox.add(dimreg_all_regions); dimreg_hbox.add(dimreg_all_dimregs); @@ -207,6 +233,7 @@ sigc::mem_fun(*this, &MainWindow::show_scripts_tab) ); actionGroup->add(Gtk::Action::create("AllInstruments", _("_Select"))); + actionGroup->add(Gtk::Action::create("AssignScripts", _("Assign Script"))); actionGroup->add(Gtk::Action::create("MenuEdit", _("_Edit"))); @@ -232,6 +259,16 @@ Gtk::AccelKey(GDK_KEY_x, Gdk::MOD1_MASK), sigc::mem_fun(*this, &MainWindow::adjust_clipboard_content)); + actionGroup->add(Gtk::Action::create("SelectPrevInstr", + _("Select Previous Instrument")), + Gtk::AccelKey(GDK_KEY_Up, primaryModifierKey), + sigc::mem_fun(*this, &MainWindow::select_prev_instrument)); + + actionGroup->add(Gtk::Action::create("SelectNextInstr", + _("Select Next Instrument")), + Gtk::AccelKey(GDK_KEY_Down, primaryModifierKey), + sigc::mem_fun(*this, &MainWindow::select_next_instrument)); + actionGroup->add(Gtk::Action::create("SelectPrevRegion", _("Select Previous Region")), Gtk::AccelKey(GDK_KEY_Left, primaryModifierKey), @@ -333,6 +370,11 @@ sigc::mem_fun(*this, &MainWindow::on_action_duplicate_instrument) ); actionGroup->add( + Gtk::Action::create("CombInstruments", _("_Combine Instruments ...")), + Gtk::AccelKey(GDK_KEY_j, primaryModifierKey), + sigc::mem_fun(*this, &MainWindow::on_action_combine_instruments) + ); + actionGroup->add( Gtk::Action::create("RemoveInstrument", Gtk::Stock::REMOVE), sigc::mem_fun(*this, &MainWindow::on_action_remove_instrument) ); @@ -455,6 +497,9 @@ " " " " " " + " " + " " + " " " " " " " " @@ -489,8 +534,10 @@ " " " " " " + " " " " " " + " " " " " " " " @@ -527,6 +574,7 @@ " " " " " " + " " " " " " " " @@ -621,6 +669,9 @@ instrument_menu = static_cast( uiManager->get_widget("/MenuBar/MenuInstrument/AllInstruments"))->get_submenu(); + assign_scripts_menu = static_cast( + uiManager->get_widget("/MenuBar/MenuInstrument/AssignScripts"))->get_submenu(); + Gtk::Widget* menuBar = uiManager->get_widget("/MenuBar"); m_VBox.pack_start(*menuBar, Gtk::PACK_SHRINK); m_VBox.pack_start(m_HPaned); @@ -644,7 +695,12 @@ // Create the Tree model: m_refTreeModel = Gtk::ListStore::create(m_Columns); - m_TreeView.set_model(m_refTreeModel); + m_refTreeModelFilter = Gtk::TreeModelFilter::create(m_refTreeModel); + m_refTreeModelFilter->set_visible_func( + sigc::mem_fun(*this, &MainWindow::instrument_row_visible) + ); + m_TreeView.set_model(m_refTreeModelFilter); + m_TreeView.get_selection()->set_mode(Gtk::SELECTION_MULTIPLE); m_TreeView.set_tooltip_text(_("Right click here for actions on instruments & MIDI Rules. Drag & drop to change the order of instruments.")); instrument_name_connection = m_refTreeModel->signal_row_changed().connect( @@ -654,6 +710,7 @@ // Add the TreeView's view columns: m_TreeView.append_column(_("Nr"), m_Columns.m_col_nr); m_TreeView.append_column_editable(_("Instrument"), m_Columns.m_col_name); + m_TreeView.append_column(_("Scripts"), m_Columns.m_col_scripts); m_TreeView.set_headers_visible(true); // establish drag&drop within the instrument tree view, allowing to reorder @@ -819,6 +876,10 @@ dimreg_stereo.signal_toggled().connect( sigc::mem_fun(*this, &MainWindow::update_dimregs)); + m_searchText.signal_changed().connect( + sigc::mem_fun(m_refTreeModelFilter.operator->(), &Gtk::TreeModelFilter::refilter) + ); + file = 0; file_is_changed = false; @@ -827,6 +888,10 @@ // start with a new gig file by default on_action_file_new(); + m_TreeViewNotebook.signal_switch_page().connect( + sigc::mem_fun(*this, &MainWindow::on_notebook_tab_switched) + ); + // select 'Instruments' tab by default // (gtk allows this only if the tab childs are visible, thats why it's here) m_TreeViewNotebook.set_current_page(1); @@ -843,13 +908,6 @@ uiManager->get_widget("/MenuBar/MenuMacro") )->get_submenu(); - const Gdk::ModifierType primaryModifierKey = -#if defined(__APPLE__) - Gdk::META_MASK; // Cmd key on Mac -#else - Gdk::CONTROL_MASK; // Ctrl key on all other OSs -#endif - const Gdk::ModifierType noModifier = (Gdk::ModifierType)0; Gtk::AccelMap::add_entry("/macro_0", GDK_KEY_F1, noModifier); Gtk::AccelMap::add_entry("/macro_1", GDK_KEY_F2, noModifier); @@ -870,12 +928,44 @@ updateMacroMenu(); } + + // setup "Assign Scripts" keyboard accelerators + { + Gtk::AccelMap::add_entry("/script_0", GDK_KEY_F1, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_1", GDK_KEY_F2, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_2", GDK_KEY_F3, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_3", GDK_KEY_F4, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_4", GDK_KEY_F5, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_5", GDK_KEY_F6, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_6", GDK_KEY_F7, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_7", GDK_KEY_F8, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_8", GDK_KEY_F9, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_9", GDK_KEY_F10, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_10", GDK_KEY_F11, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_11", GDK_KEY_F12, Gdk::SHIFT_MASK); + + Glib::RefPtr accelGroup = this->get_accel_group(); + assign_scripts_menu->set_accel_group(accelGroup); + } + + Glib::signal_idle().connect_once( + sigc::mem_fun(*this, &MainWindow::bringToFront), + 200 + ); } MainWindow::~MainWindow() { } +void MainWindow::bringToFront() { + #if defined(__APPLE__) + macRaiseAppWindow(); + #endif + raise(); + present(); +} + void MainWindow::updateMacroMenu() { Gtk::Menu* menuMacro = dynamic_cast( uiManager->get_widget("/MenuBar/MenuMacro") @@ -974,6 +1064,14 @@ updateMacroMenu(); } +//NOTE: the actual signal's first argument for argument 'page' is on some gtkmm version GtkNotebookPage* and on some Gtk::Widget*. Since we don't need that argument, it is simply void* here for now. +void MainWindow::on_notebook_tab_switched(void* page, guint page_num) { + bool isInstrumentsPage = (page_num == 1); + // so far we only support filtering for the instruments list, so hide the + // filter text entry field if another tab is selected + m_searchField.set_visible(isInstrumentsPage); +} + bool MainWindow::on_delete_event(GdkEventAny* event) { return !file_is_shared && file_is_changed && !close_confirmation_dialog(); @@ -1074,6 +1172,8 @@ } } + updateScriptListOfMenu(); + m_RegionChooser.set_instrument(get_instrument()); if (Settings::singleton()->syncSamplerInstrumentSelection) { @@ -1204,7 +1304,7 @@ // save the file as separate temporary file first, // then move the saved file over the old file // (may result in performance speedup during save) - String tmpname = filename + ".TMP"; + gig::String tmpname = filename + ".TMP"; gig->Save(tmpname, &progress); #if defined(WIN32) if (!DeleteFile(filename.c_str())) { @@ -1212,14 +1312,14 @@ } #else // POSIX ... if (unlink(filename.c_str())) { - throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file): " + String(strerror(errno))); + throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file): " + gig::String(strerror(errno))); } #endif if (rename(tmpname.c_str(), filename.c_str())) { #if defined(WIN32) throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file)."); #else - throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file): " + String(strerror(errno))); + throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file): " + gig::String(strerror(errno))); #endif } } @@ -1815,6 +1915,7 @@ dialog.set_comments(sComment.c_str()); dialog.set_website("http://www.linuxsampler.org"); dialog.set_website_label("http://www.linuxsampler.org"); + dialog.set_position(Gtk::WIN_POS_CENTER); dialog.run(); } @@ -1840,6 +1941,11 @@ table(2, 1), m_file(NULL) { + if (!Settings::singleton()->autoRestoreWindowDimension) { + set_default_size(470, 390); + set_position(Gtk::WIN_POS_MOUSE); + } + set_title(_("File Properties")); eName.set_width_chars(50); @@ -1974,6 +2080,11 @@ eDimensionKeyRangeLow(_("Keyswitching range low")), eDimensionKeyRangeHigh(_("Keyswitching range high")) { + if (!Settings::singleton()->autoRestoreWindowDimension) { + //set_default_size(470, 390); + set_position(Gtk::WIN_POS_MOUSE); + } + set_title(_("Instrument Properties")); eDimensionKeyRangeLow.set_tip( @@ -2098,12 +2209,14 @@ for (gig::Instrument* instrument = gig->GetFirstInstrument() ; instrument ; instrument = gig->GetNextInstrument(), ++index) { Glib::ustring name(gig_to_utf8(instrument->pInfo->Name)); + const int iScriptSlots = instrument->ScriptSlotCount(); Gtk::TreeModel::iterator iter = m_refTreeModel->append(); Gtk::TreeModel::Row row = *iter; row[m_Columns.m_col_nr] = index; row[m_Columns.m_col_name] = name; row[m_Columns.m_col_instr] = instrument; + row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; add_instrument_to_menu(name); } @@ -2242,10 +2355,50 @@ ScriptSlots* window = new ScriptSlots; window->setInstrument(instrument); + window->signal_script_slots_changed().connect( + sigc::mem_fun(*this, &MainWindow::onScriptSlotsModified) + ); //window->reparent(*this); window->show(); } +void MainWindow::onScriptSlotsModified(gig::Instrument* pInstrument) { + if (!pInstrument) return; + const int iScriptSlots = pInstrument->ScriptSlotCount(); + + //NOTE: This is a big mess! Sometimes GTK requires m_TreeView.get_model(), here we need m_refTreeModelFilter->get_model(), otherwise accessing children below causes an error! + //Glib::RefPtr model = m_TreeView.get_model(); + Glib::RefPtr model = m_refTreeModelFilter->get_model(); + + for (int i = 0; i < model->children().size(); ++i) { + Gtk::TreeModel::Row row = model->children()[i]; + if (row[m_Columns.m_col_instr] != pInstrument) continue; + row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; + break; + } + + // causes the sampler to reload the instrument with the new script + on_sel_change(); +} + +void MainWindow::assignScript(gig::Script* pScript) { + if (!pScript) { + printf("assignScript() : !script\n"); + return; + } + printf("assignScript('%s')\n", pScript->Name.c_str()); + + gig::Instrument* pInstrument = get_instrument(); + if (!pInstrument) { + printf("!instrument\n"); + return; + } + + pInstrument->AddScriptSlot(pScript); + + onScriptSlotsModified(pInstrument); +} + void MainWindow::on_action_refresh_all() { __refreshEntireGUI(); } @@ -2348,7 +2501,10 @@ void MainWindow::select_instrument(gig::Instrument* instrument) { if (!instrument) return; + //NOTE: This is a big mess! Sometimes GTK requires m_refTreeModelFilter->get_model(), here we need m_TreeView.get_model(), otherwise treeview selection below causes an error! Glib::RefPtr model = m_TreeView.get_model(); + //Glib::RefPtr model = m_refTreeModelFilter->get_model(); + for (int i = 0; i < model->children().size(); ++i) { Gtk::TreeModel::Row row = model->children()[i]; if (row[m_Columns.m_col_instr] == instrument) { @@ -2370,7 +2526,10 @@ gig::Region* pRegion = (gig::Region*) dimRgn->GetParent(); gig::Instrument* pInstrument = (gig::Instrument*) pRegion->GetParent(); + //NOTE: This is a big mess! Sometimes GTK requires m_refTreeModelFilter->get_model(), here we need m_TreeView.get_model(), otherwise treeview selection below causes an error! Glib::RefPtr model = m_TreeView.get_model(); + //Glib::RefPtr model = m_refTreeModelFilter->get_model(); + for (int i = 0; i < model->children().size(); ++i) { Gtk::TreeModel::Row row = model->children()[i]; if (row[m_Columns.m_col_instr] == pInstrument) { @@ -2518,6 +2677,52 @@ } } +void MainWindow::updateScriptListOfMenu() { + // remove all entries from "Assign Script" menu + { + const std::vector children = assign_scripts_menu->get_children(); + for (int i = 0; i < children.size(); ++i) { + Gtk::Widget* child = children[i]; + assign_scripts_menu->remove(*child); + delete child; + } + } + + int iTotalScripts = 0; + + if (!file) goto noScripts; + + // add all configured macros as menu items to the "Macro" menu + for (int iGroup = 0; file->GetScriptGroup(iGroup); ++iGroup) { + gig::ScriptGroup* pGroup = file->GetScriptGroup(iGroup); + for (int iScript = 0; pGroup->GetScript(iScript); ++iScript, ++iTotalScripts) { + gig::Script* pScript = pGroup->GetScript(iScript); + std::string name = pScript->Name; + + Gtk::MenuItem* item = new Gtk::MenuItem(name); + item->signal_activate().connect( + sigc::bind( + sigc::mem_fun(*this, &MainWindow::assignScript), pScript + ) + ); + assign_scripts_menu->append(*item); + item->set_accel_path("/script_" + ToString(iTotalScripts)); + //item->set_tooltip_text(comment); + } + } + + noScripts: + + // if there are no macros configured at all, then show a dummy entry instead + if (!iTotalScripts) { + Gtk::MenuItem* item = new Gtk::MenuItem(_("No Scripts")); + item->set_sensitive(false); + assign_scripts_menu->append(*item); + } + + assign_scripts_menu->show_all_children(); +} + Gtk::RadioMenuItem* MainWindow::add_instrument_to_menu( const Glib::ustring& name, int position) { @@ -2560,12 +2765,11 @@ rowInstr[m_Columns.m_col_nr] = m_refTreeModel->children().size() - 1; rowInstr[m_Columns.m_col_name] = name; rowInstr[m_Columns.m_col_instr] = instrument; + rowInstr[m_Columns.m_col_scripts] = ""; instrument_name_connection.unblock(); add_instrument_to_menu(name); - - m_TreeView.get_selection()->select(iterInstr); - + select_instrument(instrument); file_changed(); } @@ -3535,10 +3739,47 @@ } } +bool MainWindow::instrument_row_visible(const Gtk::TreeModel::const_iterator& iter) { + if (!iter) + return true; + + Glib::ustring pattern = m_searchText.get_text().lowercase(); + trim(pattern); + if (pattern.empty()) return true; + + Gtk::TreeModel::Row row = *iter; + Glib::ustring name = row[m_Columns.m_col_name]; + name = name.lowercase(); + + std::vector tokens = Glib::Regex::split_simple(" ", pattern); + for (int t = 0; t < tokens.size(); ++t) + if (name.find(tokens[t]) == Glib::ustring::npos) + return false; + + return true; +} + void MainWindow::on_action_combine_instruments() { CombineInstrumentsDialog* d = new CombineInstrumentsDialog(*this, file); + + // take over selection from instruments list view for the combine dialog's + // list view as pre-selection + std::set indeces; + { + Glib::RefPtr sel = m_TreeView.get_selection(); + std::vector rows = sel->get_selected_rows(); + for (int r = 0; r < rows.size(); ++r) { + Gtk::TreeModel::iterator it = m_refTreeModel->get_iter(rows[r]); + if (it) { + Gtk::TreeModel::Row row = *it; + int index = row[m_Columns.m_col_nr]; + indeces.insert(index); + } + } + } + d->setSelectedInstruments(indeces); + d->show_all(); - d->resize(500, 400); d->run(); if (d->fileWasChanged()) { // update GUI with new instrument just created @@ -3800,6 +4041,29 @@ m_TreeViewNotebook.set_current_page(2); } +void MainWindow::select_instrument_by_dir(int dir) { + if (!file) return; + gig::Instrument* pInstrument = get_instrument(); + if (!pInstrument) { + select_instrument( file->GetInstrument(0) ); + return; + } + for (int i = 0; file->GetInstrument(i); ++i) { + if (file->GetInstrument(i) == pInstrument) { + select_instrument( file->GetInstrument(i + dir) ); + return; + } + } +} + +void MainWindow::select_prev_instrument() { + select_instrument_by_dir(-1); +} + +void MainWindow::select_next_instrument() { + select_instrument_by_dir(1); +} + void MainWindow::select_prev_region() { m_RegionChooser.select_prev_region(); }