diff --git a/rtdata/languages/default b/rtdata/languages/default index 2ff654ede..605f0d95d 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -235,6 +235,7 @@ GENERAL_NO;No GENERAL_NONE;None GENERAL_OK;OK GENERAL_OPEN;Open +GENERAL_OTHER;Other GENERAL_PORTRAIT;Portrait GENERAL_RESET;Reset GENERAL_SAVE;Save @@ -1513,7 +1514,7 @@ MAIN_BUTTON_PREFERENCES;Preferences MAIN_BUTTON_PUTTOQUEUE_TOOLTIP;Put current image to processing queue.\nShortcut: Ctrl+b MAIN_BUTTON_SAVE_TOOLTIP;Save current image.\nShortcut: Ctrl+s\nSave current profile (.pp3).\nShortcut: Ctrl+Shift+s MAIN_BUTTON_SENDTOEDITOR;Edit image in external editor -MAIN_BUTTON_SENDTOEDITOR_TOOLTIP;Edit current image in external editor.\nShortcut: Ctrl+e +MAIN_BUTTON_SENDTOEDITOR_TOOLTIP;Edit current image in external editor.\nShortcut: Ctrl+e\nCurrent editor: MAIN_BUTTON_SHOWHIDESIDEPANELS_TOOLTIP;Show/hide all side panels.\nShortcut: m MAIN_BUTTON_UNFULLSCREEN;Exit fullscreen MAIN_FRAME_EDITOR;Editor diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 7553a7353..e5543105c 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -666,12 +666,15 @@ EditorPanel::EditorPanel (FilePanel* filePanel) queueimg->set_tooltip_markup (M ("MAIN_BUTTON_PUTTOQUEUE_TOOLTIP")); setExpandAlignProperties (queueimg, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL); - Gtk::Image *sendToEditorButtonImage = Gtk::manage (new RTImage ("palette-brush.png")); - sendtogimp = Gtk::manage (new Gtk::Button ()); - sendtogimp->set_relief(Gtk::RELIEF_NONE); - sendtogimp->add (*sendToEditorButtonImage); - sendtogimp->set_tooltip_markup (M ("MAIN_BUTTON_SENDTOEDITOR_TOOLTIP")); - setExpandAlignProperties (sendtogimp, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL); + send_to_external = Gtk::make_managed("", false); + send_to_external->set_tooltip_text(M("MAIN_BUTTON_SENDTOEDITOR_TOOLTIP")); + setExpandAlignProperties(send_to_external->buttonGroup, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL); + send_to_external->addEntry("palette-brush.png", M("GENERAL_OTHER")); + updateExternalEditorWidget( + options.externalEditorIndex >= 0 ? options.externalEditorIndex : options.externalEditors.size(), + options.externalEditors + ); + send_to_external->show(); // Status box progressLabel = Gtk::manage (new MyProgressBar (300)); @@ -736,7 +739,7 @@ EditorPanel::EditorPanel (FilePanel* filePanel) iops->attach_next_to (*vsep1, Gtk::POS_LEFT, 1, 1); if (!gimpPlugin) { - iops->attach_next_to (*sendtogimp, Gtk::POS_LEFT, 1, 1); + iops->attach_next_to(*send_to_external->buttonGroup, Gtk::POS_LEFT, 1, 1); } if (!gimpPlugin && !simpleEditor) { @@ -840,7 +843,8 @@ EditorPanel::EditorPanel (FilePanel* filePanel) tbRightPanel_1->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::tbRightPanel_1_toggled) ); saveimgas->signal_pressed().connect ( sigc::mem_fun (*this, &EditorPanel::saveAsPressed) ); queueimg->signal_pressed().connect ( sigc::mem_fun (*this, &EditorPanel::queueImgPressed) ); - sendtogimp->signal_pressed().connect ( sigc::mem_fun (*this, &EditorPanel::sendToGimpPressed) ); + send_to_external->signal_changed().connect(sigc::mem_fun(*this, &EditorPanel::sendToExternalChanged)); + send_to_external->signal_pressed().connect(sigc::mem_fun(*this, &EditorPanel::sendToExternalPressed)); toggleHistogramProfile->signal_toggled().connect( sigc::mem_fun (*this, &EditorPanel::histogramProfile_toggled) ); if (navPrev) { @@ -1673,7 +1677,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event) case GDK_KEY_e: if (!gimpPlugin) { - sendToGimpPressed(); + sendToExternalPressed(); } return true; @@ -1791,7 +1795,7 @@ bool EditorPanel::idle_saveImage (ProgressConnector *pc, msgd.run (); saveimgas->set_sensitive (true); - sendtogimp->set_sensitive (true); + send_to_external->set_sensitive(true); isProcessing = false; } @@ -1819,7 +1823,7 @@ bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImagef } saveimgas->set_sensitive (true); - sendtogimp->set_sensitive (true); + send_to_external->set_sensitive(true); parent->setProgressStr (""); parent->setProgress (0.); @@ -1930,7 +1934,7 @@ void EditorPanel::saveAsPressed () ld->startFunc (sigc::bind (sigc::ptr_fun (&rtengine::processImage), job, err, parent->getProgressListener(), false ), sigc::bind (sigc::mem_fun ( *this, &EditorPanel::idle_saveImage ), ld, fnameOut, sf, pparams)); saveimgas->set_sensitive (false); - sendtogimp->set_sensitive (false); + send_to_external->set_sensitive(false); } } else { BatchQueueEntry* bqe = createBatchQueueEntry (); @@ -1961,7 +1965,7 @@ void EditorPanel::queueImgPressed () parent->addBatchQueueJob (createBatchQueueEntry ()); } -void EditorPanel::sendToGimpPressed () +void EditorPanel::sendToExternal() { if (!ipc || !openThm) { return; @@ -1975,7 +1979,29 @@ void EditorPanel::sendToGimpPressed () ld->startFunc (sigc::bind (sigc::ptr_fun (&rtengine::processImage), job, err, parent->getProgressListener(), false ), sigc::bind (sigc::mem_fun ( *this, &EditorPanel::idle_sendToGimp ), ld, openThm->getFileName() )); saveimgas->set_sensitive (false); - sendtogimp->set_sensitive (false); + send_to_external->set_sensitive(false); +} + +void EditorPanel::sendToExternalChanged(int) +{ + int index = send_to_external->getSelected(); + if (index >= 0 && static_cast(index) == options.externalEditors.size()) { + index = -1; + } + options.externalEditorIndex = index; +} + +void EditorPanel::sendToExternalPressed() +{ + if (options.externalEditorIndex == -1) { + // "Other" external editor. Show app chooser dialog to let user pick. + Gtk::AppChooserDialog *dialog = getAppChooserDialog(); + dialog->show(); + } else { + struct ExternalEditor editor = options.externalEditors.at(options.externalEditorIndex); + external_editor_info = Gio::AppInfo::create_from_commandline(editor.command, editor.name, Gio::APP_INFO_CREATE_NONE); + sendToExternal(); + } } @@ -2078,7 +2104,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector *p Gtk::MessageDialog msgd (*parent, msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); msgd.run (); saveimgas->set_sensitive (true); - sendtogimp->set_sensitive (true); + send_to_external->set_sensitive(true); } return false; @@ -2093,18 +2119,12 @@ bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImagef if (!errore) { saveimgas->set_sensitive (true); - sendtogimp->set_sensitive (true); + send_to_external->set_sensitive(true); parent->setProgressStr (""); parent->setProgress (0.); bool success = false; - if (options.editorToSendTo == 1) { - success = ExtProgStore::openInGimp (filename); - } else if (options.editorToSendTo == 2) { - success = ExtProgStore::openInPhotoshop (filename); - } else if (options.editorToSendTo == 3) { - success = ExtProgStore::openInCustomEditor (filename); - } + success = ExtProgStore::openInExternalEditor(filename, external_editor_info); if (!success) { Gtk::MessageDialog msgd (*parent, M ("MAIN_MSG_CANNOTSTARTEDITOR"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); @@ -2117,6 +2137,36 @@ bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImagef return false; } +Gtk::AppChooserDialog *EditorPanel::getAppChooserDialog() +{ + if (!app_chooser_dialog.get()) { + app_chooser_dialog.reset(new Gtk::AppChooserDialog("image/tiff")); + app_chooser_dialog->signal_response().connect( + sigc::mem_fun(*this, &EditorPanel::onAppChooserDialogResponse) + ); + app_chooser_dialog->set_modal(); + } + + return app_chooser_dialog.get(); +} + +void EditorPanel::onAppChooserDialogResponse(int responseId) +{ + switch (responseId) { + case Gtk::RESPONSE_OK: + getAppChooserDialog()->close(); + external_editor_info = getAppChooserDialog()->get_app_info(); + sendToExternal(); + break; + case Gtk::RESPONSE_CANCEL: + case Gtk::RESPONSE_CLOSE: + getAppChooserDialog()->close(); + break; + default: + break; + } +} + void EditorPanel::historyBeforeLineChanged (const rtengine::procparams::ProcParams& params) { @@ -2392,6 +2442,26 @@ void EditorPanel::tbShowHideSidePanels_managestate() ShowHideSidePanelsconn.block (false); } +void EditorPanel::updateExternalEditorWidget(int selectedIndex, const std::vector &editors) +{ + // Remove the editors and leave the "Other" entry. + while (send_to_external->getEntryCount() > 1) { + send_to_external->removeEntry(0); + } + // Add the editors. + for (unsigned i = 0; i < editors.size(); i++) { + const auto & name = editors[i].name.empty() ? Glib::ustring(" ") : editors[i].name; + if (!editors[i].icon_name.empty()) { + Glib::RefPtr gioIcon = Gio::Icon::create(editors[i].icon_name); + send_to_external->insertEntry(i, gioIcon, name); + } else { + send_to_external->insertEntry(i, "palette-brush.png", name); + } + } + send_to_external->setSelected(selectedIndex); + send_to_external->show(); +} + void EditorPanel::updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC) { } diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h index 7675face5..0008786fa 100644 --- a/rtgui/editorpanel.h +++ b/rtgui/editorpanel.h @@ -43,6 +43,7 @@ class EditorPanel; class FilePanel; class MyProgressBar; class Navigator; +class PopUpButton; class Thumbnail; class ToolPanelCoordinator; @@ -162,7 +163,9 @@ public: void tbBeforeLock_toggled(); void saveAsPressed (); void queueImgPressed (); - void sendToGimpPressed (); + void sendToExternal(); + void sendToExternalChanged(int); + void sendToExternalPressed(); void openNextEditorImage (); void openPreviousEditorImage (); void syncFileBrowser (); @@ -182,6 +185,7 @@ public: { return isProcessing; } + void updateExternalEditorWidget(int selectedIndex, const std::vector &editors); void updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC); void updateTPVScrollbar (bool hide); void updateHistogramPosition (int oldPosition, int newPosition); @@ -201,6 +205,8 @@ private: bool idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname); bool idle_sentToGimp (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring filename); void histogramProfile_toggled (); + Gtk::AppChooserDialog *getAppChooserDialog(); + void onAppChooserDialogResponse(int resposneId); Glib::ustring lastSaveAsFileName; @@ -230,10 +236,12 @@ private: Gtk::Button* queueimg; Gtk::Button* saveimgas; - Gtk::Button* sendtogimp; + PopUpButton* send_to_external; Gtk::Button* navSync; Gtk::Button* navNext; Gtk::Button* navPrev; + Glib::RefPtr external_editor_info; + std::unique_ptr app_chooser_dialog; class ColorManagementToolbar; std::unique_ptr colorMgmtToolBar; diff --git a/rtgui/popupcommon.cc b/rtgui/popupcommon.cc index fabc4d572..c33ac068e 100644 --- a/rtgui/popupcommon.cc +++ b/rtgui/popupcommon.cc @@ -251,7 +251,7 @@ void PopUpCommon::setButtonHint() auto item = dynamic_cast(widget); if (item) { - hint += item->getLabel ()->get_text (); + hint += escapeHtmlChars(item->getLabel()->get_text()); } } diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 9d9603297..d4f550dd3 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -17,6 +17,7 @@ * along with RawTherapee. If not, see . */ #include +#include "externaleditorpreferences.h" #include "preferences.h" #include "multilangmgr.h" #include "splash.h" @@ -33,6 +34,8 @@ #include #endif +//#define EXT_EDITORS_RADIOS // TODO: Remove the corresponding code after testing. + namespace { void placeSpinBox(Gtk::Container* where, Gtk::SpinButton* &spin, const std::string &labelText, int digits, int inc0, int inc1, int maxLength, int range0, int range1, const std::string &toolTip = "") { Gtk::Box* HB = Gtk::manage ( new Gtk::Box () ); @@ -1188,6 +1191,7 @@ Gtk::Widget* Preferences::getGeneralPanel() Gtk::Frame* fdg = Gtk::manage(new Gtk::Frame(M("PREFERENCES_EXTERNALEDITOR"))); setExpandAlignProperties(fdg, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL); +#ifdef EXT_EDITORS_RADIOS Gtk::Grid* externaleditorGrid = Gtk::manage(new Gtk::Grid()); externaleditorGrid->set_column_spacing(4); externaleditorGrid->set_row_spacing(4); @@ -1243,8 +1247,17 @@ Gtk::Widget* Preferences::getGeneralPanel() externaleditorGrid->attach_next_to(*edOther, *edGimp, Gtk::POS_BOTTOM, 1, 1); externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1); #endif +#endif + + externalEditors = Gtk::make_managed(); + externalEditors->set_size_request(-1, 200); +#ifdef EXT_EDITORS_RADIOS + externaleditorGrid->attach_next_to(*externalEditors, *edOther, Gtk::POS_BOTTOM, 2, 1); fdg->add(*externaleditorGrid); +#else + fdg->add(*externalEditors); +#endif vbGeneral->attach_next_to (*fdg, *fclip, Gtk::POS_BOTTOM, 2, 1); langAutoDetectConn = ckbLangAutoDetect->signal_toggled().connect(sigc::mem_fun(*this, &Preferences::langAutoDetectToggled)); tconn = themeCBT->signal_changed().connect ( sigc::mem_fun (*this, &Preferences::themeChanged) ); @@ -1700,6 +1713,7 @@ void Preferences::storePreferences() moptions.pseudoHiDPISupport = pseudoHiDPI->get_active(); +#ifdef EXT_EDITORS_RADIOS #ifdef WIN32 moptions.gimpDir = gimpDir->get_filename(); moptions.psDir = psDir->get_filename(); @@ -1726,6 +1740,20 @@ void Preferences::storePreferences() else if (edOther->get_active()) { moptions.editorToSendTo = 3; } +#endif + + const std::vector &editors = externalEditors->getEditors(); + moptions.externalEditors.resize(editors.size()); + moptions.externalEditorIndex = -1; + for (unsigned i = 0; i < editors.size(); i++) { + moptions.externalEditors[i] = (ExternalEditor( + editors[i].name, editors[i].command, editors[i].icon_name)); + if (editors[i].other_data) { + // The current editor was marked before the list was edited. We + // found the mark, so this is the editor that was active. + moptions.externalEditorIndex = i; + } + } moptions.CPBPath = txtCustProfBuilderPath->get_text(); moptions.CPBKeys = CPBKeyType(custProfBuilderLabelType->get_active_row_number()); @@ -1981,6 +2009,7 @@ void Preferences::fillPreferences() hlThresh->set_value(moptions.highlightThreshold); shThresh->set_value(moptions.shadowThreshold); +#ifdef EXT_EDITORS_RADIOS edGimp->set_active(moptions.editorToSendTo == 1); edOther->set_active(moptions.editorToSendTo == 3); #ifdef WIN32 @@ -2009,6 +2038,18 @@ void Preferences::fillPreferences() #endif editorToSendTo->set_text(moptions.customEditorProg); +#endif + + std::vector editorInfos; + for (const auto &editor : moptions.externalEditors) { + editorInfos.push_back(ExternalEditorPreferences::EditorInfo( + editor.name, editor.command, editor.icon_name)); + } + if (moptions.externalEditorIndex >= 0) { + // Mark the current editor so we can track it. + editorInfos[moptions.externalEditorIndex].other_data = (void *)1; + } + externalEditors->setEditors(editorInfos); txtCustProfBuilderPath->set_text(moptions.CPBPath); custProfBuilderLabelType->set_active(moptions.CPBKeys); @@ -2474,6 +2515,23 @@ void Preferences::workflowUpdate() parent->updateProfiles (moptions.rtSettings.printerProfile, rtengine::RenderingIntent(moptions.rtSettings.printerIntent), moptions.rtSettings.printerBPC); } + bool changed = moptions.externalEditorIndex != options.externalEditorIndex + || moptions.externalEditors.size() != options.externalEditors.size(); + if (!changed) { + auto &editors = options.externalEditors; + auto &meditors = moptions.externalEditors; + for (unsigned i = 0; i < editors.size(); i++) { + if (editors[i] != meditors[i]) { + changed = true; + break; + } + } + } + if (changed) { + // Update the send to external editor widget. + parent->updateExternalEditorWidget(moptions.externalEditorIndex, moptions.externalEditors); + } + } void Preferences::addExtPressed() diff --git a/rtgui/preferences.h b/rtgui/preferences.h index df4e3327a..9e0730c04 100644 --- a/rtgui/preferences.h +++ b/rtgui/preferences.h @@ -26,6 +26,7 @@ #include "options.h" #include "../rtengine/profilestore.h" +class ExternalEditorPreferences; class RTWindow; class Splash; @@ -101,6 +102,7 @@ class Preferences final : Gtk::RadioButton* edGimp; Gtk::RadioButton* edPS; Gtk::RadioButton* edOther; + ExternalEditorPreferences *externalEditors; MyFileChooserButton* darkFrameDir; MyFileChooserButton* flatFieldDir; MyFileChooserButton* clutsDir; diff --git a/rtgui/rtwindow.cc b/rtgui/rtwindow.cc index c0042f949..0822c0aad 100644 --- a/rtgui/rtwindow.cc +++ b/rtgui/rtwindow.cc @@ -1030,6 +1030,13 @@ void RTWindow::MoveFileBrowserToEditor() } } +void RTWindow::updateExternalEditorWidget(int selectedIndex, const std::vector & editors) +{ + if (epanel) { + epanel->updateExternalEditorWidget(selectedIndex, editors); + } +} + void RTWindow::updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC) { if (epanel) { diff --git a/rtgui/rtwindow.h b/rtgui/rtwindow.h index e5e180747..32d0756b5 100644 --- a/rtgui/rtwindow.h +++ b/rtgui/rtwindow.h @@ -33,6 +33,7 @@ class BatchQueueEntry; class BatchQueuePanel; class EditorPanel; +class ExternalEditor; class FilePanel; class PLDBridge; class RTWindow final : @@ -114,6 +115,7 @@ public: void MoveFileBrowserToEditor(); void MoveFileBrowserToMain(); + void updateExternalEditorWidget(int selectedIndex, const std::vector &editors); void updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC); void updateTPVScrollbar (bool hide); void updateHistogramPosition (int oldPosition, int newPosition);