diff --git a/rtdata/languages/default b/rtdata/languages/default index 8f3a59097..45471bb5c 100644 --- a/rtdata/languages/default +++ b/rtdata/languages/default @@ -1883,8 +1883,9 @@ PREFERENCES_EXTEDITOR_FLOAT32;32-bit float TIFF output PREFERENCES_EXTERNALEDITOR;External Editor PREFERENCES_EXTERNALEDITOR_CHANGE;Change Application PREFERENCES_EXTERNALEDITOR_CHANGE_FILE;Change Executable -PREFERENCES_EXTERNALEDITOR_COLUMN_NAME;Name PREFERENCES_EXTERNALEDITOR_COLUMN_COMMAND;Command +PREFERENCES_EXTERNALEDITOR_COLUMN_NAME;Name +PREFERENCES_EXTERNALEDITOR_COLUMN_NATIVE_COMMAND;Native command PREFERENCES_FBROWSEROPTS;File Browser / Thumbnail Options PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Compact toolbars in File Browser PREFERENCES_FLATFIELDFOUND;Found diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index ffe13fea3..420717b7e 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -2250,6 +2250,7 @@ void EditorPanel::sendToExternalPressed() } 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); + external_editor_native_command = editor.native_command; sendToExternal(); } } @@ -2415,7 +2416,7 @@ bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImagef setUserOnlyPermission(Gio::File::create_for_path(filename), false); - success = ExtProgStore::openInExternalEditor(filename, external_editor_info); + success = ExtProgStore::openInExternalEditor(filename, external_editor_info, external_editor_native_command); if (!success) { Gtk::MessageDialog msgd (*parent, M ("MAIN_MSG_CANNOTSTARTEDITOR"), false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); @@ -2447,6 +2448,7 @@ void EditorPanel::onAppChooserDialogResponse(int responseId) case Gtk::RESPONSE_OK: getAppChooserDialog()->close(); external_editor_info = getAppChooserDialog()->get_app_info(); + external_editor_native_command = false; sendToExternal(); break; case Gtk::RESPONSE_CANCEL: diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h index 98a475a7c..309f14e8c 100644 --- a/rtgui/editorpanel.h +++ b/rtgui/editorpanel.h @@ -253,6 +253,7 @@ private: Gtk::Button* navNext; Gtk::Button* navPrev; Glib::RefPtr external_editor_info; + bool external_editor_native_command; std::unique_ptr app_chooser_dialog; ExternalEditorChangedSignal *externalEditorChangedSignal; sigc::connection externalEditorChangedSignalConnection; diff --git a/rtgui/externaleditorpreferences.cc b/rtgui/externaleditorpreferences.cc index 79dac52d2..55c9f7956 100644 --- a/rtgui/externaleditorpreferences.cc +++ b/rtgui/externaleditorpreferences.cc @@ -37,6 +37,7 @@ ExternalEditorPreferences::ExternalEditorPreferences(): list_view = Gtk::manage(new Gtk::TreeView()); list_view->set_model(list_model); list_view->append_column(*Gtk::manage(makeAppColumn())); + list_view->append_column(*Gtk::manage(makeNativeCommandColumn())); list_view->append_column(*Gtk::manage(makeCommandColumn())); for (auto &&column : list_view->get_columns()) { @@ -97,12 +98,12 @@ ExternalEditorPreferences::getEditors() const for (auto rowIter = children.begin(); rowIter != children.end(); rowIter++) { const Gio::Icon *const icon = rowIter->get_value(model_columns.icon).get(); const auto &icon_serialized = icon == nullptr ? "" : icon->serialize().print(); - editors.push_back(ExternalEditorPreferences::EditorInfo( - rowIter->get_value(model_columns.name), - rowIter->get_value(model_columns.command), - icon_serialized, - rowIter->get_value(model_columns.other_data) - )); + editors.emplace_back( + rowIter->get_value(model_columns.name), + rowIter->get_value(model_columns.command), + icon_serialized, + rowIter->get_value(model_columns.native_command), + rowIter->get_value(model_columns.other_data)); } return editors; @@ -138,6 +139,7 @@ void ExternalEditorPreferences::setEditors( row[model_columns.name] = editor.name; row[model_columns.icon] = icon; row[model_columns.command] = editor.command; + row[model_columns.native_command] = editor.native_command; row[model_columns.other_data] = editor.other_data; } } @@ -194,6 +196,24 @@ Gtk::TreeViewColumn *ExternalEditorPreferences::makeCommandColumn() return col; } +Gtk::TreeViewColumn *ExternalEditorPreferences::makeNativeCommandColumn() +{ + auto toggle_renderer = Gtk::manage(new Gtk::CellRendererToggle()); + auto col = Gtk::manage(new Gtk::TreeViewColumn()); + + col->set_title(M("PREFERENCES_EXTERNALEDITOR_COLUMN_NATIVE_COMMAND")); + col->pack_start(*toggle_renderer); + col->add_attribute(toggle_renderer->property_active(), model_columns.native_command); + + toggle_renderer->signal_toggled().connect([this](const Glib::ustring &path) { + const auto row_iter = list_model->get_iter(path); + bool new_value = !row_iter->get_value(model_columns.native_command); + row_iter->set_value(model_columns.native_command, new_value); + }); + + return col; +} + void ExternalEditorPreferences::onAppChooserDialogResponse( int response_id, RTAppChooserDialog *dialog) { @@ -224,6 +244,7 @@ void ExternalEditorPreferences::onFileChooserDialogResponse( for (const auto &selected : selection) { auto row = *list_model->get_iter(selected); row[model_columns.icon] = Glib::RefPtr(nullptr); + row[model_columns.native_command] = false; row[model_columns.command] = #ifdef WIN32 '"' + dialog->get_filename() + '"'; @@ -313,6 +334,7 @@ void ExternalEditorPreferences::setApp(const Glib::RefPtr app_info row[model_columns.icon] = app_info->get_icon(); row[model_columns.name] = app_info->get_name(); row[model_columns.command] = app_info->get_commandline(); + row[model_columns.native_command] = false; } } @@ -344,8 +366,8 @@ void ExternalEditorPreferences::updateToolbarSensitivity() } ExternalEditorPreferences::EditorInfo::EditorInfo( - Glib::ustring name, Glib::ustring command, Glib::ustring icon_serialized, void *other_data -) : name(name), icon_serialized(icon_serialized), command(command), other_data(other_data) + Glib::ustring name, Glib::ustring command, Glib::ustring icon_serialized, bool native_command, void *other_data +) : name(name), icon_serialized(icon_serialized), command(command), native_command(native_command), other_data(other_data) { } @@ -354,5 +376,6 @@ ExternalEditorPreferences::ModelColumns::ModelColumns() add(name); add(icon); add(command); + add(native_command); add(other_data); } diff --git a/rtgui/externaleditorpreferences.h b/rtgui/externaleditorpreferences.h index 34658d942..26903371c 100644 --- a/rtgui/externaleditorpreferences.h +++ b/rtgui/externaleditorpreferences.h @@ -50,6 +50,7 @@ public: Glib::ustring name = Glib::ustring(), Glib::ustring command = Glib::ustring(), Glib::ustring icon_serialized = Glib::ustring(), + bool native_command = false, void *other_data = nullptr ); /** @@ -65,6 +66,10 @@ public: * Gio::AppInfo::get_commandline() */ Glib::ustring command; + /** + * Use the OS native launcher instead of Gio. + */ + bool native_command; /** * Holds any other data associated with the editor. For example, it can * be used as a tag to uniquely identify the editor. @@ -96,6 +101,7 @@ private: Gtk::TreeModelColumn name; Gtk::TreeModelColumn> icon; Gtk::TreeModelColumn command; + Gtk::TreeModelColumn native_command; Gtk::TreeModelColumn other_data; }; @@ -124,6 +130,10 @@ private: * Constructs the column for displaying an editable commandline. */ Gtk::TreeViewColumn *makeCommandColumn(); + /** + * Constructs the column for displaying the native command toggle. + */ + Gtk::TreeViewColumn *makeNativeCommandColumn(); /** * Called when the user is done interacting with the app chooser dialog. * Closes the dialog and updates the selected entry if an app was chosen. diff --git a/rtgui/extprog.cc b/rtgui/extprog.cc index 9ec87c548..ea1800638 100644 --- a/rtgui/extprog.cc +++ b/rtgui/extprog.cc @@ -344,8 +344,20 @@ bool ExtProgStore::openInCustomEditor (const Glib::ustring& fileName, const Glib } -bool ExtProgStore::openInExternalEditor(const Glib::ustring &fileName, const Glib::RefPtr &editorInfo) +bool ExtProgStore::openInExternalEditor(const Glib::ustring &fileName, const Glib::RefPtr &editorInfo, bool nativeCommand) { + if (nativeCommand) { + if (rtengine::settings->verbose) { + std::cout << "Launching external editor as native command." << std::endl; + } + const Glib::ustring command = editorInfo->get_commandline(); + return openInCustomEditor(fileName, &command); + } + + if (rtengine::settings->verbose) { + std::cout << "Launching external editor with Gio." << std::endl; + } + bool success = false; try { diff --git a/rtgui/extprog.h b/rtgui/extprog.h index 6547896ef..5336c4703 100644 --- a/rtgui/extprog.h +++ b/rtgui/extprog.h @@ -70,7 +70,7 @@ public: static bool openInGimp (const Glib::ustring& fileName); static bool openInPhotoshop (const Glib::ustring& fileName); static bool openInCustomEditor (const Glib::ustring& fileName, const Glib::ustring* command = nullptr); - static bool openInExternalEditor(const Glib::ustring &fileName, const Glib::RefPtr &editorInfo); + static bool openInExternalEditor(const Glib::ustring &fileName, const Glib::RefPtr &editorInfo, bool nativeCommand); }; #define extProgStore ExtProgStore::getInstance() diff --git a/rtgui/options.cc b/rtgui/options.cc index b59e7c203..d6c3f7d25 100644 --- a/rtgui/options.cc +++ b/rtgui/options.cc @@ -873,6 +873,7 @@ void Options::readFromFile(Glib::ustring fname) if (keyFile.has_group("External Editor")) { if (keyFile.has_key("External Editor", "Names") || keyFile.has_key("External Editor", "Commands") + || keyFile.has_key("External Editor", "NativeCommands") || keyFile.has_key("External Editor", "IconsSerialized")) { // Multiple external editors. @@ -886,6 +887,11 @@ void Options::readFromFile(Glib::ustring fname) std::vector() : static_cast>( keyFile.get_string_list("External Editor", "Commands")); + const auto & native_commands = + !keyFile.has_key("External Editor", "NativeCommands") ? + std::vector() : + static_cast>( + keyFile.get_boolean_list("External Editor", "NativeCommands")); const auto & icons_serialized = !keyFile.has_key("External Editor", "IconsSerialized") ? std::vector() : @@ -899,6 +905,9 @@ void Options::readFromFile(Glib::ustring fname) for (unsigned i = 0; i < commands.size(); i++) { externalEditors[i].command = commands[i]; } + for (unsigned i = 0; i < native_commands.size(); i++) { + externalEditors[i].native_command = native_commands[i]; + } for (unsigned i = 0; i < icons_serialized.size(); i++) { externalEditors[i].icon_serialized = icons_serialized[i]; } @@ -938,7 +947,7 @@ void Options::readFromFile(Glib::ustring fname) if (editorToSendTo == 1) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("GIMP", "\"" + executable + "\"", getIconSerialized(executable))); + externalEditors.emplace_back("GIMP", executable, true, getIconSerialized(executable)); } else { for (auto ver = 12; ver >= 0; --ver) { executable = Glib::build_filename(gimpDir, "bin", Glib::ustring::compose(Glib::ustring("gimp-2.%1.exe"), ver)); @@ -946,7 +955,7 @@ void Options::readFromFile(Glib::ustring fname) if (editorToSendTo == 1) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("GIMP", "\"" + executable + "\"", getIconSerialized(executable))); + externalEditors.emplace_back("GIMP", executable, true, getIconSerialized(executable)); break; } } @@ -961,7 +970,7 @@ void Options::readFromFile(Glib::ustring fname) if (editorToSendTo == 2) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("Photoshop", "\"" + executable + "\"", getIconSerialized(executable))); + externalEditors.emplace_back("Photoshop", executable, true, getIconSerialized(executable)); } if (keyFile.has_key("External Editor", "CustomEditor")) { @@ -970,20 +979,20 @@ void Options::readFromFile(Glib::ustring fname) if (editorToSendTo == 3) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("-", "\"" + executable + "\"", "")); + externalEditors.emplace_back("-", executable, true, ""); } } #elif defined __APPLE__ if (editorToSendTo == 1) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("GIMP", "open -a GIMP", "gimp")); - externalEditors.push_back(ExternalEditor("GIMP-dev", "open -a GIMP-dev", "gimp")); + externalEditors.emplace_back("GIMP", "open -a GIMP", true, ""); + externalEditors.emplace_back("GIMP-dev", "open -a GIMP-dev", true, ""); if (editorToSendTo == 2) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("Photoshop", "open -a Photoshop", "")); + externalEditors.emplace_back("Photoshop", "open -a Photoshop", true, ""); if (keyFile.has_key("External Editor", "CustomEditor")) { auto executable = keyFile.get_string("External Editor", "CustomEditor"); @@ -991,20 +1000,21 @@ void Options::readFromFile(Glib::ustring fname) if (editorToSendTo == 3) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("-", executable, "")); + externalEditors.emplace_back("-", executable, true, ""); } } #else + const Glib::ustring gimp_icon_serialized = "('themed', <['gimp', 'gimp-symbolic']>)"; if (Glib::find_program_in_path("gimp").compare("")) { if (editorToSendTo == 1) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("GIMP", "gimp", "gimp")); + externalEditors.emplace_back("GIMP", "gimp", true, gimp_icon_serialized); } else if (Glib::find_program_in_path("gimp-remote").compare("")) { if (editorToSendTo == 1) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("GIMP", "gimp-remote", "gimp")); + externalEditors.emplace_back("GIMP", "gimp-remote", true, gimp_icon_serialized); } if (keyFile.has_key("External Editor", "CustomEditor")) { @@ -1013,7 +1023,7 @@ void Options::readFromFile(Glib::ustring fname) if (editorToSendTo == 3) { externalEditorIndex = externalEditors.size(); } - externalEditors.push_back(ExternalEditor("-", executable, "")); + externalEditors.emplace_back("-", executable, true, ""); } } #endif @@ -2328,16 +2338,19 @@ void Options::saveToFile(Glib::ustring fname) { std::vector names; std::vector commands; + std::vector native_commands; std::vector icons_serialized; for (const auto & editor : externalEditors) { names.push_back(editor.name); commands.push_back(editor.command); + native_commands.push_back(editor.native_command); icons_serialized.push_back(editor.icon_serialized); } keyFile.set_string_list("External Editor", "Names", names); keyFile.set_string_list("External Editor", "Commands", commands); + keyFile.set_boolean_list("External Editor", "NativeCommands", native_commands); keyFile.set_string_list("External Editor", "IconsSerialized", icons_serialized); keyFile.set_integer("External Editor", "EditorIndex", externalEditorIndex); @@ -3009,15 +3022,15 @@ Glib::ustring Options::getICCProfileCopyright() return Glib::ustring::compose("Copyright RawTherapee %1, CC0", now.get_year()); } -ExternalEditor::ExternalEditor() {} +ExternalEditor::ExternalEditor() = default; ExternalEditor::ExternalEditor( - const Glib::ustring &name, const Glib::ustring &command, const Glib::ustring &icon_serialized -): name(name), command(command), icon_serialized(icon_serialized) {} + const Glib::ustring &name, const Glib::ustring &command, bool native_command, const Glib::ustring &icon_serialized +): name(name), command(command), native_command(native_command), icon_serialized(icon_serialized) {} bool ExternalEditor::operator==(const ExternalEditor &other) const { - return this->name == other.name && this->command == other.command && this->icon_serialized == other.icon_serialized; + return this->name == other.name && this->command == other.command && this->native_command == other.native_command && this->icon_serialized == other.icon_serialized; } bool ExternalEditor::operator!=(const ExternalEditor &other) const diff --git a/rtgui/options.h b/rtgui/options.h index 2c34260aa..78059357e 100644 --- a/rtgui/options.h +++ b/rtgui/options.h @@ -54,9 +54,10 @@ struct ExternalEditor { ExternalEditor(); - ExternalEditor(const Glib::ustring &name, const Glib::ustring &command, const Glib::ustring &icon_serialized); + ExternalEditor(const Glib::ustring &name, const Glib::ustring &command, bool native_command, const Glib::ustring &icon_serialized); Glib::ustring name; Glib::ustring command; + bool native_command; Glib::ustring icon_serialized; bool operator==(const ExternalEditor & other) const; diff --git a/rtgui/preferences.cc b/rtgui/preferences.cc index 7d95ef716..a97cb003d 100644 --- a/rtgui/preferences.cc +++ b/rtgui/preferences.cc @@ -1752,7 +1752,7 @@ void Preferences::storePreferences() moptions.externalEditorIndex = -1; for (unsigned i = 0; i < editors.size(); i++) { moptions.externalEditors[i] = (ExternalEditor( - editors[i].name, editors[i].command, editors[i].icon_serialized)); + editors[i].name, editors[i].command, editors[i].native_command, editors[i].icon_serialized)); 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. @@ -2034,8 +2034,7 @@ void Preferences::fillPreferences() std::vector editorInfos; for (const auto &editor : moptions.externalEditors) { - editorInfos.push_back(ExternalEditorPreferences::EditorInfo( - editor.name, editor.command, editor.icon_serialized)); + editorInfos.emplace_back(editor.name, editor.command, editor.icon_serialized, editor.native_command); } if (moptions.externalEditorIndex >= 0) { // Mark the current editor so we can track it. @@ -2536,7 +2535,10 @@ void Preferences::workflowUpdate() } if (changed) { // Update the send to external editor widget. - parent->updateExternalEditorWidget(moptions.externalEditorIndex, moptions.externalEditors); + int selected_index = moptions.externalEditorIndex >= 0 + ? moptions.externalEditorIndex + : static_cast(moptions.externalEditors.size()); + parent->updateExternalEditorWidget(selected_index, moptions.externalEditors); } if (moptions.cloneFavoriteTools != options.cloneFavoriteTools ||