--- gigedit/trunk/src/gigedit/mainwindow.cpp 2017/11/14 18:07:25 3364 +++ gigedit/trunk/src/gigedit/mainwindow.cpp 2019/10/24 12:06:18 3636 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2017 Andreas Persson + * Copyright (C) 2006-2019 Andreas Persson * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -21,12 +21,6 @@ #include #include "compat.h" -// threads.h must be included first to be able to build with -// G_DISABLE_DEPRECATED -#if (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION == 31 && GLIBMM_MICRO_VERSION >= 2) || \ - (GLIBMM_MAJOR_VERSION == 2 && GLIBMM_MINOR_VERSION > 31) || GLIBMM_MAJOR_VERSION > 2 -#include -#endif #include #include @@ -101,7 +95,11 @@ // set_border_width(5); if (!Settings::singleton()->autoRestoreWindowDimension) { - set_default_size(800, 600); +#if GTKMM_MAJOR_VERSION >= 3 + set_default_size(960, 600); +#else + set_default_size(865, 600); +#endif set_position(Gtk::WIN_POS_CENTER); } @@ -132,8 +130,9 @@ m_ScrolledWindowScripts.add(m_TreeViewScripts); m_ScrolledWindowScripts.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); - +#if GTKMM_MAJOR_VERSION < 3 m_TreeViewNotebook.set_size_request(300); +#endif m_searchLabel.set_text(Glib::ustring(" ") + _("Filter:")); m_searchField.pack_start(m_searchLabel, Gtk::PACK_SHRINK); @@ -452,6 +451,10 @@ m_actionGroup->add_action_bool("Statusbar", sigc::mem_fun(*this, &MainWindow::on_action_view_status_bar), true); m_actionToggleRestoreWinDim = m_actionGroup->add_action_bool("AutoRestoreWinDim", sigc::mem_fun(*this, &MainWindow::on_auto_restore_win_dim), Settings::singleton()->autoRestoreWindowDimension); + m_actionToggleShowTooltips = m_actionGroup->add_action_bool( + "ShowTooltips", sigc::mem_fun(*this, &MainWindow::on_action_show_tooltips), + Settings::singleton()->showTooltips + ); m_actionToggleSaveWithTempFile = m_actionGroup->add_action_bool("SaveWithTemporaryFile", sigc::mem_fun(*this, &MainWindow::on_save_with_temporary_file), Settings::singleton()->saveWithTemporaryFile); m_actionGroup->add_action("RefreshAll", sigc::mem_fun(*this, &MainWindow::on_action_refresh_all)); @@ -475,6 +478,14 @@ *this, &MainWindow::on_auto_restore_win_dim)); toggle_action = + Gtk::ToggleAction::create("ShowTooltips", _("Tooltips for Beginners")); + toggle_action->set_active(Settings::singleton()->showTooltips); + actionGroup->add( + toggle_action, + sigc::mem_fun(*this, &MainWindow::on_action_show_tooltips) + ); + + toggle_action = Gtk::ToggleAction::create("SaveWithTemporaryFile", _("Save with _temporary file")); toggle_action->set_active(Settings::singleton()->saveWithTemporaryFile); actionGroup->add(toggle_action, @@ -916,6 +927,10 @@ " Statusbar" " AppMenu.Statusbar" " " + " " + " Tooltips for Beginners" + " AppMenu.ShowTooltips" + " " " " " Auto restore Window Dimensions" " AppMenu.AutoRestoreWinDim" @@ -1146,6 +1161,7 @@ " " " " " " + " " " " " " " " @@ -1232,7 +1248,7 @@ { Gtk::MenuItem* item = dynamic_cast( uiManager->get_widget("/MenuBar/MenuEdit/CopySampleLoop")); - item->set_tooltip_text(_("Used when dragging a sample to a region's sample reference field. You may disable this for example if you want to replace an existing sample in a region with a new sample, but don't want that the region's current loop informations to be altered by this action.")); + item->set_tooltip_text(_("Used when dragging a sample to a region's sample reference field. You may disable this for example if you want to replace an existing sample in a region with a new sample, but don't want that the region's current loop information to be altered by this action.")); } { Gtk::MenuItem* item = dynamic_cast( @@ -1247,7 +1263,7 @@ { Gtk::MenuItem* item = dynamic_cast( uiManager->get_widget("/MenuBar/MenuSettings/MoveRootNoteWithRegionMoved")); - item->set_tooltip_text(_("If checked, and when a region is moved by dragging it around on the virtual keyboard, the keybord position dependent pitch will move exactly with the amount of semi tones the region was moved around.")); + item->set_tooltip_text(_("If checked, and when a region is moved by dragging it around on the virtual keyboard, the keyboard position dependent pitch will move exactly with the amount of semi tones the region was moved around.")); } { Gtk::MenuItem* item = dynamic_cast( @@ -1334,7 +1350,10 @@ 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.")); + m_TreeView.set_has_tooltip(true); + m_TreeView.signal_query_tooltip().connect( + sigc::mem_fun(*this, &MainWindow::onQueryTreeViewTooltip) + ); instrument_name_connection = m_refTreeModel->signal_row_changed().connect( sigc::mem_fun(*this, &MainWindow::instrument_name_changed) ); @@ -1465,7 +1484,7 @@ sigc::mem_fun(*this, &MainWindow::file_changed)); instrumentProps.signal_changed().connect( sigc::mem_fun(*this, &MainWindow::file_changed)); - propDialog.signal_changed().connect( + fileProps.signal_changed().connect( sigc::mem_fun(*this, &MainWindow::file_changed)); midiRules.signal_changed().connect( sigc::mem_fun(*this, &MainWindow::file_changed)); @@ -1491,7 +1510,11 @@ sigc::hide( sigc::bind( file_structure_to_be_changed_signal.make_slot(), +#if SIGCXX_MAJOR_VERSION > 2 || (SIGCXX_MAJOR_VERSION == 2 && SIGCXX_MINOR_VERSION >= 8) + std::ref(this->file) +#else sigc::ref(this->file) +#endif ) ) ); @@ -1499,7 +1522,11 @@ sigc::hide( sigc::bind( file_structure_changed_signal.make_slot(), +#if SIGCXX_MAJOR_VERSION > 2 || (SIGCXX_MAJOR_VERSION == 2 && SIGCXX_MINOR_VERSION >= 8) + std::ref(this->file) +#else sigc::ref(this->file) +#endif ) ) ); @@ -1521,7 +1548,7 @@ sigc::mem_fun(*this, &MainWindow::update_dimregs)); m_searchText.signal_changed().connect( - sigc::mem_fun(m_refTreeModelFilter.operator->(), &Gtk::TreeModelFilter::refilter) + sigc::mem_fun(*m_refTreeModelFilter.operator->(), &Gtk::TreeModelFilter::refilter) ); file = 0; @@ -1575,6 +1602,13 @@ Gtk::AccelMap::add_entry("/macro_9", GDK_KEY_F10, noModifier); Gtk::AccelMap::add_entry("/macro_10", GDK_KEY_F11, noModifier); Gtk::AccelMap::add_entry("/macro_11", GDK_KEY_F12, noModifier); + Gtk::AccelMap::add_entry("/macro_12", GDK_KEY_F13, noModifier); + Gtk::AccelMap::add_entry("/macro_13", GDK_KEY_F14, noModifier); + Gtk::AccelMap::add_entry("/macro_14", GDK_KEY_F15, noModifier); + Gtk::AccelMap::add_entry("/macro_15", GDK_KEY_F16, noModifier); + Gtk::AccelMap::add_entry("/macro_16", GDK_KEY_F17, noModifier); + Gtk::AccelMap::add_entry("/macro_17", GDK_KEY_F18, noModifier); + Gtk::AccelMap::add_entry("/macro_18", GDK_KEY_F19, noModifier); Gtk::AccelMap::add_entry("/SetupMacros", 'm', primaryModifierKey); Glib::RefPtr accelGroup = this->get_accel_group(); @@ -1597,11 +1631,21 @@ 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); + Gtk::AccelMap::add_entry("/script_12", GDK_KEY_F13, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_13", GDK_KEY_F14, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_14", GDK_KEY_F15, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_15", GDK_KEY_F16, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_16", GDK_KEY_F17, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_17", GDK_KEY_F18, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/script_18", GDK_KEY_F19, Gdk::SHIFT_MASK); + Gtk::AccelMap::add_entry("/DropAllScriptSlots", GDK_KEY_BackSpace, Gdk::SHIFT_MASK); Glib::RefPtr accelGroup = this->get_accel_group(); assign_scripts_menu->set_accel_group(accelGroup); } + on_show_tooltips_changed(); + Glib::signal_idle().connect_once( sigc::mem_fun(*this, &MainWindow::bringToFront), 200 @@ -1842,16 +1886,30 @@ } } + +LoaderSaverBase::LoaderSaverBase(const Glib::ustring filename, gig::File* gig) : + filename(filename), gig(gig), +#ifdef GLIB_THREADS + thread(0), +#endif + progress(0.f) +{ +} + void loader_progress_callback(gig::progress_t* progress) { - Loader* loader = static_cast(progress->custom); + LoaderSaverBase* loader = static_cast(progress->custom); loader->progress_callback(progress->factor); } -void Loader::progress_callback(float fraction) +void LoaderSaverBase::progress_callback(float fraction) { { +#ifdef GLIB_THREADS Glib::Threads::Mutex::Lock lock(progressMutex); +#else + std::lock_guard lock(progressMutex); +#endif progress = fraction; } progress_dispatcher(); @@ -1861,19 +1919,21 @@ // make sure stack is 16-byte aligned for SSE instructions __attribute__((force_align_arg_pointer)) #endif -void Loader::thread_function() +void LoaderSaverBase::thread_function() { +#ifdef GLIB_THREADS printf("thread_function self=%p\n", static_cast(Glib::Threads::Thread::self())); +#else + std::cout << "thread_function self=" << std::this_thread::get_id() << "\n"; +#endif printf("Start %s\n", filename.c_str()); try { - RIFF::File* riff = new RIFF::File(filename); - gig = new gig::File(riff); gig::progress_t progress; progress.callback = loader_progress_callback; progress.custom = this; - gig->GetInstrument(0, &progress); + thread_function_sub(progress); printf("End\n"); finished_dispatcher(); } catch (RIFF::Exception e) { @@ -1885,159 +1945,111 @@ } } -Loader::Loader(const char* filename) - : filename(filename), gig(0), thread(0), progress(0.f) -{ -} - -void Loader::launch() +void LoaderSaverBase::launch() { +#ifdef GLIB_THREADS #ifdef OLD_THREADS - thread = Glib::Thread::create(sigc::mem_fun(*this, &Loader::thread_function), true); + thread = Glib::Thread::create(sigc::mem_fun(*this, &LoaderSaverBase::thread_function), true); #else - thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Loader::thread_function)); + thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &LoaderSaverBase::thread_function)); #endif printf("launch thread=%p\n", static_cast(thread)); +#else + thread = std::thread([this](){ thread_function(); }); + std::cout << "launch thread=" << thread.get_id() << "\n"; +#endif } -float Loader::get_progress() +float LoaderSaverBase::get_progress() { - float res; - { - Glib::Threads::Mutex::Lock lock(progressMutex); - res = progress; - } - return res; +#ifdef GLIB_THREADS + Glib::Threads::Mutex::Lock lock(progressMutex); +#else + std::lock_guard lock(progressMutex); +#endif + return progress; } -Glib::Dispatcher& Loader::signal_progress() +Glib::Dispatcher& LoaderSaverBase::signal_progress() { return progress_dispatcher; } -Glib::Dispatcher& Loader::signal_finished() +Glib::Dispatcher& LoaderSaverBase::signal_finished() { return finished_dispatcher; } -Glib::Dispatcher& Loader::signal_error() +Glib::Dispatcher& LoaderSaverBase::signal_error() { return error_dispatcher; } -void saver_progress_callback(gig::progress_t* progress) -{ - Saver* saver = static_cast(progress->custom); - saver->progress_callback(progress->factor); +void LoaderSaverBase::join() { +#ifdef GLIB_THREADS + thread->join(); +#else + thread.join(); +#endif } -void Saver::progress_callback(float fraction) + +Loader::Loader(const char* filename) : + LoaderSaverBase(filename, 0) { - { - Glib::Threads::Mutex::Lock lock(progressMutex); - progress = fraction; - } - progress_dispatcher.emit(); } -#if defined(WIN32) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)) -// make sure stack is 16-byte aligned for SSE instructions -__attribute__((force_align_arg_pointer)) -#endif -void Saver::thread_function() +void Loader::thread_function_sub(gig::progress_t& progress) { - printf("thread_function self=%p\n", - static_cast(Glib::Threads::Thread::self())); - printf("Start %s\n", filename.c_str()); - try { - gig::progress_t progress; - progress.callback = saver_progress_callback; - progress.custom = this; + RIFF::File* riff = new RIFF::File(filename); + gig = new gig::File(riff); - // if no filename was provided, that means "save", if filename was provided means "save as" - if (filename.empty()) { - if (!Settings::singleton()->saveWithTemporaryFile) { - // save directly over the existing .gig file - // (requires less disk space than solution below - // but may be slower) - gig->Save(&progress); - } else { - // save the file as separate temporary file first, - // then move the saved file over the old file - // (may result in performance speedup during save) - gig::String tmpname = filename + ".TMP"; - gig->Save(tmpname, &progress); - #if defined(WIN32) - if (!DeleteFile(filename.c_str())) { - throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file)."); - } - #else // POSIX ... - if (unlink(filename.c_str())) { - 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): " + gig::String(strerror(errno))); - #endif - } - } - } else { - gig->Save(filename, &progress); - } - - printf("End\n"); - finished_dispatcher.emit(); - } catch (RIFF::Exception e) { - error_message = e.Message; - error_dispatcher.emit(); - } catch (...) { - error_message = _("Unknown exception occurred"); - error_dispatcher.emit(); - } + gig->GetInstrument(0, &progress); } -Saver::Saver(gig::File* file, Glib::ustring filename) - : gig(file), filename(filename), thread(0), progress(0.f) + +Saver::Saver(gig::File* file, Glib::ustring filename) : + LoaderSaverBase(filename, file) { } -void Saver::launch() +void Saver::thread_function_sub(gig::progress_t& progress) { -#ifdef OLD_THREADS - thread = Glib::Thread::create(sigc::mem_fun(*this, &Saver::thread_function), true); + // if no filename was provided, that means "save", if filename was provided means "save as" + if (filename.empty()) { + if (!Settings::singleton()->saveWithTemporaryFile) { + // save directly over the existing .gig file + // (requires less disk space than solution below + // but may be slower) + gig->Save(&progress); + } else { + // save the file as separate temporary file first, + // then move the saved file over the old file + // (may result in performance speedup during save) + gig::String tmpname = filename + ".TMP"; + gig->Save(tmpname, &progress); +#if defined(WIN32) + if (!DeleteFile(filename.c_str())) { + throw RIFF::Exception("Could not replace original file with temporary file (unable to remove original file)."); + } +#else // POSIX ... + if (unlink(filename.c_str())) { + 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 - thread = Glib::Threads::Thread::create(sigc::mem_fun(*this, &Saver::thread_function)); + throw RIFF::Exception("Could not replace original file with temporary file (unable to rename temp file): " + gig::String(strerror(errno))); #endif - printf("launch thread=%p\n", static_cast(thread)); -} - -float Saver::get_progress() -{ - float res; - { - Glib::Threads::Mutex::Lock lock(progressMutex); - res = progress; + } + } + } else { + gig->Save(filename, &progress); } - return res; } -Glib::Dispatcher& Saver::signal_progress() -{ - return progress_dispatcher; -} - -Glib::Dispatcher& Saver::signal_finished() -{ - return finished_dispatcher; -} - -Glib::Dispatcher& Saver::signal_error() -{ - return error_dispatcher; -} ProgressDialog::ProgressDialog(const Glib::ustring& title, Gtk::Window& parent) : Gtk::Dialog(title, parent, true) @@ -2193,10 +2205,16 @@ dialog.set_current_folder(current_gig_dir); } if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); std::string filename = dialog.get_filename(); printf("filename=%s\n", filename.c_str()); +#ifdef GLIB_THREADS printf("on_action_file_open self=%p\n", static_cast(Glib::Threads::Thread::self())); +#else + std::cout << "on_action_file_open self=" << + std::this_thread::get_id() << "\n"; +#endif load_file(filename.c_str()); current_gig_dir = Glib::path_get_dirname(filename); } @@ -2269,15 +2287,22 @@ void MainWindow::on_loader_finished() { + loader->join(); printf("Loader finished!\n"); +#ifdef GLIB_THREADS printf("on_loader_finished self=%p\n", static_cast(Glib::Threads::Thread::self())); +#else + std::cout << "on_loader_finished self=" << + std::this_thread::get_id() << "\n"; +#endif load_gig(loader->gig, loader->filename.c_str()); progress_dialog->hide(); } void MainWindow::on_loader_error() { + loader->join(); Glib::ustring txt = _("Could not load file: ") + loader->error_message; Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); msg.run(); @@ -2348,6 +2373,7 @@ void MainWindow::on_saver_error() { + saver->join(); file_structure_changed_signal.emit(this->file); Glib::ustring txt = _("Could not save file: ") + saver->error_message; Gtk::MessageDialog msg(*this, txt, false, Gtk::MESSAGE_ERROR); @@ -2356,6 +2382,7 @@ void MainWindow::on_saver_finished() { + saver->join(); this->file = saver->gig; this->filename = saver->filename; current_gig_dir = Glib::path_get_dirname(filename); @@ -2447,6 +2474,7 @@ #endif if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); std::string filename = dialog.get_filename(); if (!Glib::str_has_suffix(filename, ".gig")) { filename += ".gig"; @@ -2575,8 +2603,8 @@ void MainWindow::on_action_file_properties() { - propDialog.show(); - propDialog.deiconify(); + fileProps.show(); + fileProps.deiconify(); } void MainWindow::on_action_warn_user_on_extensions() { @@ -2584,6 +2612,30 @@ !Settings::singleton()->warnUserOnExtensions; } +void MainWindow::on_action_show_tooltips() { + Settings::singleton()->showTooltips = + !Settings::singleton()->showTooltips; + + on_show_tooltips_changed(); +} + +void MainWindow::on_show_tooltips_changed() { + const bool b = Settings::singleton()->showTooltips; + + dimreg_label.set_has_tooltip(b); + dimreg_all_regions.set_has_tooltip(b); + dimreg_all_dimregs.set_has_tooltip(b); + dimreg_stereo.set_has_tooltip(b); + + // Not doing this here, we let onQueryTreeViewTooltip() handle this per cell + //m_TreeView.set_has_tooltip(b); + + m_TreeViewSamples.set_has_tooltip(b); + m_TreeViewScripts.set_has_tooltip(b); + + set_has_tooltip(b); +} + void MainWindow::on_action_sync_sampler_instrument_selection() { Settings::singleton()->syncSamplerInstrumentSelection = !Settings::singleton()->syncSamplerInstrumentSelection; @@ -2603,7 +2655,7 @@ dialog.set_name("Gigedit"); #endif dialog.set_version(VERSION); - dialog.set_copyright("Copyright (C) 2006-2017 Andreas Persson"); + dialog.set_copyright("Copyright (C) 2006-2019 Andreas Persson"); const std::string sComment = _("Built " __DATE__ "\nUsing ") + ::gig::libraryName() + " " + ::gig::libraryVersion() + "\n\n" + @@ -2614,16 +2666,16 @@ "backup your Gigasampler/GigaStudio files before editing them with " "this application.\n" "\n" - "Please report bugs to: http://bugs.linuxsampler.org" + "Please report bugs to: https://bugs.linuxsampler.org" ); dialog.set_comments(sComment.c_str()); - dialog.set_website("http://www.linuxsampler.org"); - dialog.set_website_label("http://www.linuxsampler.org"); + dialog.set_website("https://www.linuxsampler.org"); + dialog.set_website_label("https://www.linuxsampler.org"); dialog.set_position(Gtk::WIN_POS_CENTER); dialog.run(); } -PropDialog::PropDialog() +FilePropDialog::FilePropDialog() : eFileFormat(_("File Format")), eName(_("Name")), eCreationDate(_("Creation date")), @@ -2657,6 +2709,7 @@ set_title(_("File Properties")); eName.set_width_chars(50); + connect(eFileFormat, &FilePropDialog::set_FileFormat); connect(eName, &DLS::Info::Name); connect(eCreationDate, &DLS::Info::CreationDate); connect(eComments, &DLS::Info::Comments); @@ -2699,7 +2752,7 @@ #endif add(vbox); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) table.set_margin(5); #else table.set_border_width(5); @@ -2707,7 +2760,7 @@ vbox.add(table); vbox.pack_start(buttonBox, Gtk::PACK_SHRINK); buttonBox.set_layout(Gtk::BUTTONBOX_END); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) buttonBox.set_margin(5); #else buttonBox.set_border_width(5); @@ -2717,9 +2770,7 @@ quitButton.set_can_default(); quitButton.grab_focus(); quitButton.signal_clicked().connect( - sigc::mem_fun(*this, &PropDialog::hide)); - eFileFormat.signal_value_changed().connect( - sigc::mem_fun(*this, &PropDialog::onFileFormatChanged)); + sigc::mem_fun(*this, &FilePropDialog::hide)); quitButton.show(); vbox.show(); @@ -2728,9 +2779,10 @@ #endif } -void PropDialog::set_file(gig::File* file) +void FilePropDialog::set_file(gig::File* file) { m_file = file; + update(file->pInfo); // update file format version combo box const std::string sGiga = "Gigasampler/GigaStudio v"; @@ -2738,25 +2790,24 @@ std::vector txts; std::vector values; txts.push_back(sGiga + "2"); values.push_back(2); - txts.push_back(sGiga + "3/v4"); values.push_back(3); - if (major != 2 && major != 3) { + txts.push_back(sGiga + "3"); values.push_back(3); + txts.push_back(sGiga + "4"); values.push_back(4); + if (major < 2 || major > 4) { txts.push_back(sGiga + ToString(major)); values.push_back(major); } std::vector texts; for (int i = 0; i < txts.size(); ++i) texts.push_back(txts[i].c_str()); texts.push_back(NULL); values.push_back(0); + + update_model++; eFileFormat.set_choices(&texts[0], &values[0]); eFileFormat.set_value(major); + update_model--; } -void PropDialog::onFileFormatChanged() { - const int major = eFileFormat.get_value(); - if (m_file) m_file->pVersion->major = major; -} - -void PropDialog::set_info(DLS::Info* info) +void FilePropDialog::set_FileFormat(int value) { - update(info); + m_file->pVersion->major = value; } @@ -2805,7 +2856,24 @@ ePitchbendRange(_("Pitchbend range"), 0, 48), ePianoReleaseMode(_("Piano release mode")), eDimensionKeyRangeLow(_("Keyswitching range low")), - eDimensionKeyRangeHigh(_("Keyswitching range high")) + eDimensionKeyRangeHigh(_("Keyswitching range high")), + table2(2,1), + eName2(_("Name")), + eCreationDate(_("Creation date")), + eComments(_("Comments")), + eProduct(_("Product")), + eCopyright(_("Copyright")), + eArtists(_("Artists")), + eGenre(_("Genre")), + eKeywords(_("Keywords")), + eEngineer(_("Engineer")), + eTechnician(_("Technician")), + eSoftware(_("Software")), + eMedium(_("Medium")), + eSource(_("Source")), + eSourceForm(_("Source form")), + eCommissioned(_("Commissioned")), + eSubject(_("Subject")) { if (!Settings::singleton()->autoRestoreWindowDimension) { //set_default_size(470, 390); @@ -2814,6 +2882,9 @@ set_title(_("Instrument Properties")); + tabs.append_page(vbox[1], _("Settings")); + tabs.append_page(vbox[2], _("Info")); + eDimensionKeyRangeLow.set_tip( _("start of the keyboard area which should switch the " "\"keyswitching\" dimension") @@ -2838,12 +2909,59 @@ eName.signal_value_changed().connect(sig_name_changed.make_slot()); + connect(eName2, &InstrumentProps::set_Name); + connectLambda(eCreationDate, [this](gig::String s) { + m->pInfo->CreationDate = s; + }); + connectLambda(eComments, [this](gig::String s) { + m->pInfo->Comments = s; + }); + connectLambda(eProduct, [this](gig::String s) { + m->pInfo->Product = s; + }); + connectLambda(eCopyright, [this](gig::String s) { + m->pInfo->Copyright = s; + }); + connectLambda(eArtists, [this](gig::String s) { + m->pInfo->Artists = s; + }); + connectLambda(eGenre, [this](gig::String s) { + m->pInfo->Genre = s; + }); + connectLambda(eKeywords, [this](gig::String s) { + m->pInfo->Keywords = s; + }); + connectLambda(eEngineer, [this](gig::String s) { + m->pInfo->Engineer = s; + }); + connectLambda(eTechnician, [this](gig::String s) { + m->pInfo->Technician = s; + }); + connectLambda(eSoftware, [this](gig::String s) { + m->pInfo->Software = s; + }); + connectLambda(eMedium, [this](gig::String s) { + m->pInfo->Medium = s; + }); + connectLambda(eSource, [this](gig::String s) { + m->pInfo->Source = s; + }); + connectLambda(eSourceForm, [this](gig::String s) { + m->pInfo->SourceForm = s; + }); + connectLambda(eCommissioned, [this](gig::String s) { + m->pInfo->Commissioned = s; + }); + connectLambda(eSubject, [this](gig::String s) { + m->pInfo->Subject = s; + }); + + // tab 1 #if USE_GTKMM_GRID table.set_column_spacing(5); #else table.set_col_spacings(5); #endif - table.add(eName); table.add(eIsDrum); table.add(eMIDIBank); @@ -2857,17 +2975,43 @@ table.add(eDimensionKeyRangeLow); table.add(eDimensionKeyRangeHigh); - add(vbox); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) + // tab 2 +#if USE_GTKMM_GRID + table2.set_column_spacing(5); +#else + table2.set_col_spacings(5); +#endif + table2.add(eName2); + table2.add(eCreationDate); + table2.add(eComments); + table2.add(eProduct); + table2.add(eCopyright); + table2.add(eArtists); + table2.add(eGenre); + table2.add(eKeywords); + table2.add(eEngineer); + table2.add(eTechnician); + table2.add(eSoftware); + table2.add(eMedium); + table2.add(eSource); + table2.add(eSourceForm); + table2.add(eCommissioned); + table2.add(eSubject); + + add(vbox[0]); +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) table.set_margin(5); #else table.set_border_width(5); #endif - vbox.pack_start(table); + vbox[1].pack_start(table); + vbox[2].pack_start(table2); table.show(); - vbox.pack_start(buttonBox, Gtk::PACK_SHRINK); + table2.show(); + vbox[0].pack_start(tabs); + vbox[0].pack_start(buttonBox, Gtk::PACK_SHRINK); buttonBox.set_layout(Gtk::BUTTONBOX_END); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) buttonBox.set_margin(5); #else buttonBox.set_border_width(5); @@ -2881,7 +3025,7 @@ sigc::mem_fun(*this, &InstrumentProps::hide)); quitButton.show(); - vbox.show(); + vbox[0].show(); #if HAS_GTKMM_SHOW_ALL_CHILDREN show_all_children(); #endif @@ -2892,10 +3036,30 @@ update(instrument); update_model++; + + // tab 1 eName.set_value(instrument->pInfo->Name); eIsDrum.set_value(instrument->IsDrum); eMIDIBank.set_value(instrument->MIDIBank); eMIDIProgram.set_value(instrument->MIDIProgram); + // tab 2 + eName2.set_value(instrument->pInfo->Name); + eCreationDate.set_value(instrument->pInfo->CreationDate); + eComments.set_value(instrument->pInfo->Comments); + eProduct.set_value(instrument->pInfo->Product); + eCopyright.set_value(instrument->pInfo->Copyright); + eArtists.set_value(instrument->pInfo->Artists); + eGenre.set_value(instrument->pInfo->Genre); + eKeywords.set_value(instrument->pInfo->Keywords); + eEngineer.set_value(instrument->pInfo->Engineer); + eTechnician.set_value(instrument->pInfo->Technician); + eSoftware.set_value(instrument->pInfo->Software); + eMedium.set_value(instrument->pInfo->Medium); + eSource.set_value(instrument->pInfo->Source); + eSourceForm.set_value(instrument->pInfo->SourceForm); + eCommissioned.set_value(instrument->pInfo->Commissioned); + eSubject.set_value(instrument->pInfo->Subject); + update_model--; } @@ -2929,6 +3093,55 @@ } } +bool MainWindow::onQueryTreeViewTooltip(int x, int y, bool keyboardTip, const Glib::RefPtr& tooltip) { + Gtk::TreeModel::iterator iter; + if (!m_TreeView.get_tooltip_context_iter(x, y, keyboardTip, iter)) { + return false; + } + Gtk::TreeModel::Path path(iter); + Gtk::TreeModel::Row row = *iter; + Gtk::TreeViewColumn* pointedColumn = NULL; + // resolve the precise table column the mouse points to + { + Gtk::TreeModel::Path path; // unused + int cellX, cellY; // unused + m_TreeView.get_path_at_pos(x, y, path, pointedColumn, cellX, cellY); + } + Gtk::TreeViewColumn* scriptsColumn = m_TreeView.get_column(2); + if (pointedColumn == scriptsColumn) { // mouse hovers scripts column ... + // show the script(s) assigned to the hovered instrument as tooltip + tooltip->set_markup( row[m_Columns.m_col_tooltip] ); + m_TreeView.set_tooltip_cell(tooltip, &path, scriptsColumn, NULL); + } else { + // if beginners' tooltips is disabled then don't show the following one + if (!Settings::singleton()->showTooltips) + return false; + // yeah, a beginners tooltip + tooltip->set_text(_( + "Right click here for actions on instruments & MIDI Rules. " + "Drag & drop to change the order of instruments." + )); + m_TreeView.set_tooltip_cell(tooltip, &path, pointedColumn, NULL); + } + return true; +} + +static Glib::ustring scriptTooltipFor(gig::Instrument* instrument, int index) { + Glib::ustring name(gig_to_utf8(instrument->pInfo->Name)); + const int iScriptSlots = instrument->ScriptSlotCount(); + Glib::ustring tooltip = "(" + ToString(index) + ") “" + name + "”\n\n"; + if (!iScriptSlots) + tooltip += "No script assigned"; + else { + for (int i = 0; i < iScriptSlots; ++i) { + tooltip += "• " + ToString(i+1) + ". Script: “" + + instrument->GetScriptOfSlot(i)->Name + "”"; + if (i + 1 < iScriptSlots) tooltip += "\n\n"; + } + } + return tooltip; +} + void MainWindow::load_gig(gig::File* gig, const char* filename, bool isSharedInstrument) { file = 0; @@ -2942,8 +3155,7 @@ file_has_name = filename; file_is_changed = false; - propDialog.set_file(gig); - propDialog.set_info(gig->pInfo); + fileProps.set_file(gig); instrument_name_connection.block(); int index = 0; @@ -2958,6 +3170,7 @@ row[m_Columns.m_col_name] = name; row[m_Columns.m_col_instr] = instrument; row[m_Columns.m_col_scripts] = iScriptSlots ? ToString(iScriptSlots) : ""; + row[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); #if !USE_GTKMM_BUILDER add_instrument_to_menu(name); @@ -3075,7 +3288,10 @@ gig::Instrument* instrument = row[m_Columns.m_col_instr]; Glib::ustring gigname(gig_to_utf8(instrument->pInfo->Name)); if (gigname != name) { + Gtk::TreeModel::Path path(*it); + const int index = path[0]; row[m_Columns.m_col_name] = gigname; + row[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); } } @@ -3121,6 +3337,7 @@ 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) : ""; + row[m_Columns.m_col_tooltip] = scriptTooltipFor(pInstrument, i); break; } @@ -3146,6 +3363,20 @@ onScriptSlotsModified(pInstrument); } +void MainWindow::dropAllScriptSlots() { + gig::Instrument* pInstrument = get_instrument(); + if (!pInstrument) { + printf("!instrument\n"); + return; + } + + const int iScriptSlots = pInstrument->ScriptSlotCount(); + for (int i = iScriptSlots - 1; i >= 0; --i) + pInstrument->RemoveScriptSlot(i); + + onScriptSlotsModified(pInstrument); +} + void MainWindow::on_action_refresh_all() { __refreshEntireGUI(); } @@ -3320,7 +3551,7 @@ show_intruments_tab(); m_TreeView.get_selection()->unselect_all(); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) auto iterSel = model->children()[i].get_iter(); m_TreeView.get_selection()->select(iterSel); #else @@ -3350,7 +3581,7 @@ // select and show the respective instrument in the list view show_intruments_tab(); m_TreeView.get_selection()->unselect_all(); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) auto iterSel = model->children()[i].get_iter(); m_TreeView.get_selection()->select(iterSel); #else @@ -3386,7 +3617,7 @@ if (rowSample[m_SamplesModel.m_col_sample] == sample) { show_samples_tab(); m_TreeViewSamples.get_selection()->unselect_all(); -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) auto iterSel = rowGroup.children()[s].get_iter(); m_TreeViewSamples.get_selection()->select(iterSel); #else @@ -3586,6 +3817,18 @@ assign_scripts_menu->append(*item); } + // add separator line to menu + assign_scripts_menu->append(*new Gtk::SeparatorMenuItem); + + { + Gtk::MenuItem* item = new Gtk::MenuItem(_("Unassign All Scripts")); + item->signal_activate().connect( + sigc::mem_fun(*this, &MainWindow::dropAllScriptSlots) + ); + assign_scripts_menu->append(*item); + item->set_accel_path("/DropAllScriptSlots"); + } + #if HAS_GTKMM_SHOW_ALL_CHILDREN assign_scripts_menu->show_all_children(); #endif @@ -3634,10 +3877,12 @@ instrument_name_connection.block(); Gtk::TreeModel::iterator iterInstr = m_refTreeModel->append(); Gtk::TreeModel::Row rowInstr = *iterInstr; - rowInstr[m_Columns.m_col_nr] = m_refTreeModel->children().size() - 1; + const int index = m_refTreeModel->children().size() - 1; + rowInstr[m_Columns.m_col_nr] = index; rowInstr[m_Columns.m_col_name] = name; rowInstr[m_Columns.m_col_instr] = instrument; rowInstr[m_Columns.m_col_scripts] = ""; + rowInstr[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); instrument_name_connection.unblock(); #if !USE_GTKMM_BUILDER @@ -3724,7 +3969,9 @@ it != m_refTreeModel->children().end(); ++it, ++index) { Gtk::TreeModel::Row row = *it; + gig::Instrument* instrument = row[m_Columns.m_col_instr]; row[m_Columns.m_col_nr] = index; + row[m_Columns.m_col_tooltip] = scriptTooltipFor(instrument, index); } } @@ -3977,13 +4224,14 @@ dialog.set_current_folder(current_sample_dir); } if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); current_sample_dir = dialog.get_current_folder(); Glib::ustring error_files; std::vector filenames = dialog.get_filenames(); for (std::vector::iterator iter = filenames.begin(); iter != filenames.end(); ++iter) { printf("Adding sample %s\n",(*iter).c_str()); - // use libsndfile to retrieve file informations + // use libsndfile to retrieve file information SF_INFO info; info.format = 0; SNDFILE* hFile = sf_open((*iter).c_str(), SFM_READ, &info); @@ -4157,6 +4405,7 @@ } if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); current_sample_dir = dialog.get_current_folder(); Glib::ustring error_files; std::string folder = dialog.get_filename(); @@ -4645,7 +4894,7 @@ trim(pattern); if (pattern.empty()) return true; -#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 22) +#if GTKMM_MAJOR_VERSION > 3 || (GTKMM_MAJOR_VERSION == 3 && GTKMM_MINOR_VERSION > 24) //HACK: on GTKMM4 development branch const_iterator cannot be easily converted to iterator, probably going to be fixed before final GTKMM4 release though. Gtk::TreeModel::Row row = **(Gtk::TreeModel::iterator*)(&iter); #else @@ -4885,8 +5134,14 @@ #endif if (dialog.run() == Gtk::RESPONSE_OK) { + dialog.hide(); +#ifdef GLIB_THREADS printf("on_action_merge_files self=%p\n", static_cast(Glib::Threads::Thread::self())); +#else + std::cout << "on_action_merge_files self=" << + std::this_thread::get_id() << "\n"; +#endif std::vector filenames = dialog.get_filenames(); // merge the selected files to the currently open .gig file