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);