Merge pull request #6299 from Beep6581/multi-external-editor
Multiple External Editors
This commit is contained in:
commit
f449a726e8
@ -210,6 +210,7 @@ FILEBROWSER_ZOOMOUTHINT;Decrease thumbnail size.\n\nShortcuts:\n<b>-</b> - Multi
|
||||
FILECHOOSER_FILTER_ANY;All files
|
||||
FILECHOOSER_FILTER_COLPROF;Color profiles (*.icc)
|
||||
FILECHOOSER_FILTER_CURVE;Curve files
|
||||
FILECHOOSER_FILTER_EXECUTABLE;Executable files
|
||||
FILECHOOSER_FILTER_LCP;Lens correction profiles
|
||||
FILECHOOSER_FILTER_PP;Processing profiles
|
||||
FILECHOOSER_FILTER_SAME;Same format as current photo
|
||||
@ -237,6 +238,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
|
||||
@ -1621,7 +1623,7 @@ MAIN_BUTTON_PREFERENCES;Preferences
|
||||
MAIN_BUTTON_PUTTOQUEUE_TOOLTIP;Put current image to processing queue.\nShortcut: <b>Ctrl+b</b>
|
||||
MAIN_BUTTON_SAVE_TOOLTIP;Save current image.\nShortcut: <b>Ctrl+s</b>\nSave current profile (.pp3).\nShortcut: <b>Ctrl+Shift+s</b>
|
||||
MAIN_BUTTON_SENDTOEDITOR;Edit image in external editor
|
||||
MAIN_BUTTON_SENDTOEDITOR_TOOLTIP;Edit current image in external editor.\nShortcut: <b>Ctrl+e</b>
|
||||
MAIN_BUTTON_SENDTOEDITOR_TOOLTIP;Edit current image in external editor.\nShortcut: <b>Ctrl+e</b>\nCurrent editor:
|
||||
MAIN_BUTTON_SHOWHIDESIDEPANELS_TOOLTIP;Show/hide all side panels.\nShortcut: <b>m</b>
|
||||
MAIN_BUTTON_UNFULLSCREEN;Exit fullscreen
|
||||
MAIN_FRAME_EDITOR;Editor
|
||||
@ -1872,6 +1874,10 @@ PREFERENCES_EXTEDITOR_DIR_CUSTOM;Custom
|
||||
PREFERENCES_EXTEDITOR_DIR_TEMP;OS temp dir
|
||||
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_FBROWSEROPTS;File Browser / Thumbnail Options
|
||||
PREFERENCES_FILEBROWSERTOOLBARSINGLEROW;Compact toolbars in File Browser
|
||||
PREFERENCES_FLATFIELDFOUND;Found
|
||||
|
@ -62,6 +62,7 @@ set(NONCLISOURCEFILES
|
||||
exiffiltersettings.cc
|
||||
exifpanel.cc
|
||||
exportpanel.cc
|
||||
externaleditorpreferences.cc
|
||||
extprog.cc
|
||||
fattaltonemap.cc
|
||||
filebrowser.cc
|
||||
@ -134,6 +135,7 @@ set(NONCLISOURCEFILES
|
||||
retinex.cc
|
||||
rgbcurves.cc
|
||||
rotate.cc
|
||||
rtappchooserdialog.cc
|
||||
rtimage.cc
|
||||
rtscalable.cc
|
||||
rtsurface.cc
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "procparamchangers.h"
|
||||
#include "placesbrowser.h"
|
||||
#include "pathutils.h"
|
||||
#include "rtappchooserdialog.h"
|
||||
#include "thumbnail.h"
|
||||
#include "toolpanelcoord.h"
|
||||
|
||||
@ -701,7 +702,9 @@ public:
|
||||
EditorPanel::EditorPanel (FilePanel* filePanel)
|
||||
: catalogPane (nullptr), realized (false), tbBeforeLock (nullptr), iHistoryShow (nullptr), iHistoryHide (nullptr),
|
||||
iTopPanel_1_Show (nullptr), iTopPanel_1_Hide (nullptr), iRightPanel_1_Show (nullptr), iRightPanel_1_Hide (nullptr),
|
||||
iBeforeLockON (nullptr), iBeforeLockOFF (nullptr), previewHandler (nullptr), beforePreviewHandler (nullptr),
|
||||
iBeforeLockON (nullptr), iBeforeLockOFF (nullptr),
|
||||
externalEditorChangedSignal (nullptr),
|
||||
previewHandler (nullptr), beforePreviewHandler (nullptr),
|
||||
beforeIarea (nullptr), beforeBox (nullptr), afterBox (nullptr), beforeLabel (nullptr), afterLabel (nullptr),
|
||||
beforeHeaderBox (nullptr), afterHeaderBox (nullptr), parent (nullptr), parentWindow (nullptr), openThm (nullptr),
|
||||
selectedFrame(0), isrc (nullptr), ipc (nullptr), beforeIpc (nullptr), err (0), isProcessing (false),
|
||||
@ -899,12 +902,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::manage(new PopUpButton("", 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));
|
||||
@ -969,7 +975,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) {
|
||||
@ -1073,7 +1079,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) {
|
||||
@ -1906,7 +1913,7 @@ bool EditorPanel::handleShortcutKey (GdkEventKey* event)
|
||||
|
||||
case GDK_KEY_e:
|
||||
if (!gimpPlugin) {
|
||||
sendToGimpPressed();
|
||||
sendToExternalPressed();
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -2024,7 +2031,7 @@ bool EditorPanel::idle_saveImage (ProgressConnector<rtengine::IImagefloat*> *pc,
|
||||
msgd.run ();
|
||||
|
||||
saveimgas->set_sensitive (true);
|
||||
sendtogimp->set_sensitive (true);
|
||||
send_to_external->set_sensitive(true);
|
||||
isProcessing = false;
|
||||
|
||||
}
|
||||
@ -2052,7 +2059,7 @@ bool EditorPanel::idle_imageSaved (ProgressConnector<int> *pc, rtengine::IImagef
|
||||
}
|
||||
|
||||
saveimgas->set_sensitive (true);
|
||||
sendtogimp->set_sensitive (true);
|
||||
send_to_external->set_sensitive(true);
|
||||
|
||||
parent->setProgressStr ("");
|
||||
parent->setProgress (0.);
|
||||
@ -2163,7 +2170,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 ();
|
||||
@ -2194,7 +2201,7 @@ void EditorPanel::queueImgPressed ()
|
||||
parent->addBatchQueueJob (createBatchQueueEntry ());
|
||||
}
|
||||
|
||||
void EditorPanel::sendToGimpPressed ()
|
||||
void EditorPanel::sendToExternal()
|
||||
{
|
||||
if (!ipc || !openThm) {
|
||||
return;
|
||||
@ -2206,12 +2213,46 @@ void EditorPanel::sendToGimpPressed ()
|
||||
if (options.editor_bypass_output_profile) {
|
||||
pparams.icm.outputProfile = rtengine::procparams::ColorManagementParams::NoProfileString;
|
||||
}
|
||||
|
||||
if (!cached_exported_filename.empty() && cached_exported_image == ipc->getInitialImage() && pparams == cached_exported_pparams && Glib::file_test(cached_exported_filename, Glib::FILE_TEST_IS_REGULAR)) {
|
||||
idle_sentToGimp(nullptr, nullptr, cached_exported_filename);
|
||||
return;
|
||||
}
|
||||
|
||||
cached_exported_image = ipc->getInitialImage();
|
||||
cached_exported_pparams = pparams;
|
||||
cached_exported_filename.clear();
|
||||
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
|
||||
ProgressConnector<rtengine::IImagefloat*> *ld = new ProgressConnector<rtengine::IImagefloat*>();
|
||||
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<unsigned>(index) == options.externalEditors.size()) {
|
||||
index = -1;
|
||||
}
|
||||
options.externalEditorIndex = index;
|
||||
if (externalEditorChangedSignal) {
|
||||
externalEditorChangedSignal->emit();
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPanel::sendToExternalPressed()
|
||||
{
|
||||
if (options.externalEditorIndex == -1) {
|
||||
// "Other" external editor. Show app chooser dialog to let user pick.
|
||||
RTAppChooserDialog *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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2265,6 +2306,23 @@ void EditorPanel::syncFileBrowser() // synchronize filebrowser with image in E
|
||||
}
|
||||
}
|
||||
|
||||
ExternalEditorChangedSignal * EditorPanel::getExternalEditorChangedSignal()
|
||||
{
|
||||
return externalEditorChangedSignal;
|
||||
}
|
||||
|
||||
void EditorPanel::setExternalEditorChangedSignal(ExternalEditorChangedSignal *signal)
|
||||
{
|
||||
if (externalEditorChangedSignal) {
|
||||
externalEditorChangedSignalConnection.disconnect();
|
||||
}
|
||||
externalEditorChangedSignal = signal;
|
||||
if (signal) {
|
||||
externalEditorChangedSignalConnection = signal->connect(
|
||||
sigc::mem_fun(*this, &EditorPanel::updateExternalEditorSelection));
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPanel::histogramProfile_toggled()
|
||||
{
|
||||
options.rtSettings.HistogramWorking = toggleHistogramProfile->get_active();
|
||||
@ -2330,7 +2388,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *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;
|
||||
@ -2338,27 +2396,27 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *p
|
||||
|
||||
bool EditorPanel::idle_sentToGimp (ProgressConnector<int> *pc, rtengine::IImagefloat* img, Glib::ustring filename)
|
||||
{
|
||||
delete img;
|
||||
int errore = pc->returnValue();
|
||||
if (img) {
|
||||
delete img;
|
||||
cached_exported_filename = filename;
|
||||
}
|
||||
int errore = 0;
|
||||
setProgressState(false);
|
||||
delete pc;
|
||||
if (pc) {
|
||||
errore = pc->returnValue();
|
||||
delete pc;
|
||||
}
|
||||
|
||||
if (!errore) {
|
||||
if ((!img && Glib::file_test(filename, Glib::FILE_TEST_IS_REGULAR)) || (img && !errore)) {
|
||||
saveimgas->set_sensitive (true);
|
||||
sendtogimp->set_sensitive (true);
|
||||
send_to_external->set_sensitive(true);
|
||||
parent->setProgressStr ("");
|
||||
parent->setProgress (0.);
|
||||
bool success = false;
|
||||
|
||||
setUserOnlyPermission(Gio::File::create_for_path(filename), 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);
|
||||
@ -2371,6 +2429,48 @@ bool EditorPanel::idle_sentToGimp (ProgressConnector<int> *pc, rtengine::IImagef
|
||||
return false;
|
||||
}
|
||||
|
||||
RTAppChooserDialog *EditorPanel::getAppChooserDialog()
|
||||
{
|
||||
if (!app_chooser_dialog.get()) {
|
||||
app_chooser_dialog.reset(new RTAppChooserDialog("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::updateExternalEditorSelection()
|
||||
{
|
||||
int index = send_to_external->getSelected();
|
||||
if (index >= 0 && static_cast<unsigned>(index) == options.externalEditors.size()) {
|
||||
index = -1;
|
||||
}
|
||||
if (options.externalEditorIndex != index) {
|
||||
send_to_external->setSelected(
|
||||
options.externalEditorIndex >= 0 ? options.externalEditorIndex : options.externalEditors.size());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPanel::historyBeforeLineChanged (const rtengine::procparams::ProcParams& params)
|
||||
{
|
||||
|
||||
@ -2646,6 +2746,40 @@ void EditorPanel::tbShowHideSidePanels_managestate()
|
||||
ShowHideSidePanelsconn.block (false);
|
||||
}
|
||||
|
||||
void EditorPanel::updateExternalEditorWidget(int selectedIndex, const std::vector<ExternalEditor> &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_serialized.empty()) {
|
||||
Glib::RefPtr<Gio::Icon> gioIcon;
|
||||
GError *e = nullptr;
|
||||
GVariant *icon_variant = g_variant_parse(
|
||||
nullptr, editors[i].icon_serialized.c_str(), nullptr, nullptr, &e);
|
||||
|
||||
if (e) {
|
||||
std::cerr
|
||||
<< "Error loading external editor icon from \""
|
||||
<< editors[i].icon_serialized << "\": " << e->message
|
||||
<< std::endl;
|
||||
gioIcon = Glib::RefPtr<Gio::Icon>();
|
||||
} else {
|
||||
gioIcon = Gio::Icon::deserialize(Glib::VariantBase(icon_variant));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
@ -38,11 +38,15 @@ template<typename T>
|
||||
class array2D;
|
||||
}
|
||||
|
||||
using ExternalEditorChangedSignal = sigc::signal<void>;
|
||||
|
||||
class BatchQueueEntry;
|
||||
class EditorPanel;
|
||||
class FilePanel;
|
||||
class MyProgressBar;
|
||||
class Navigator;
|
||||
class PopUpButton;
|
||||
class RTAppChooserDialog;
|
||||
class Thumbnail;
|
||||
class ToolPanelCoordinator;
|
||||
|
||||
@ -65,6 +69,7 @@ class EditorPanel final :
|
||||
public rtengine::NonCopyable
|
||||
{
|
||||
public:
|
||||
|
||||
explicit EditorPanel (FilePanel* filePanel = nullptr);
|
||||
~EditorPanel () override;
|
||||
|
||||
@ -162,11 +167,17 @@ public:
|
||||
void tbBeforeLock_toggled();
|
||||
void saveAsPressed ();
|
||||
void queueImgPressed ();
|
||||
void sendToGimpPressed ();
|
||||
void sendToExternal();
|
||||
void sendToExternalChanged(int);
|
||||
void sendToExternalPressed();
|
||||
void openNextEditorImage ();
|
||||
void openPreviousEditorImage ();
|
||||
void syncFileBrowser ();
|
||||
|
||||
// Signals.
|
||||
ExternalEditorChangedSignal * getExternalEditorChangedSignal();
|
||||
void setExternalEditorChangedSignal(ExternalEditorChangedSignal *signal);
|
||||
|
||||
void tbTopPanel_1_visible (bool visible);
|
||||
bool CheckSidePanelsVisibility();
|
||||
void tbShowHideSidePanels_managestate();
|
||||
@ -182,6 +193,7 @@ public:
|
||||
{
|
||||
return isProcessing;
|
||||
}
|
||||
void updateExternalEditorWidget(int selectedIndex, const std::vector<ExternalEditor> &editors);
|
||||
void updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC);
|
||||
void updateTPVScrollbar (bool hide);
|
||||
void updateHistogramPosition (int oldPosition, int newPosition);
|
||||
@ -201,6 +213,9 @@ private:
|
||||
bool idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *pc, Glib::ustring fname);
|
||||
bool idle_sentToGimp (ProgressConnector<int> *pc, rtengine::IImagefloat* img, Glib::ustring filename);
|
||||
void histogramProfile_toggled ();
|
||||
RTAppChooserDialog *getAppChooserDialog();
|
||||
void onAppChooserDialogResponse(int resposneId);
|
||||
void updateExternalEditorSelection();
|
||||
|
||||
|
||||
Glib::ustring lastSaveAsFileName;
|
||||
@ -230,10 +245,18 @@ 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<Gio::AppInfo> external_editor_info;
|
||||
std::unique_ptr<RTAppChooserDialog> app_chooser_dialog;
|
||||
ExternalEditorChangedSignal *externalEditorChangedSignal;
|
||||
sigc::connection externalEditorChangedSignalConnection;
|
||||
|
||||
rtengine::InitialImage *cached_exported_image;
|
||||
rtengine::procparams::ProcParams cached_exported_pparams;
|
||||
Glib::ustring cached_exported_filename;
|
||||
|
||||
class ColorManagementToolbar;
|
||||
std::unique_ptr<ColorManagementToolbar> colorMgmtToolBar;
|
||||
|
@ -250,6 +250,7 @@ void EditWindow::addEditorPanel (EditorPanel* ep, const std::string &name)
|
||||
{
|
||||
ep->setParent (parent);
|
||||
ep->setParentWindow(this);
|
||||
ep->setExternalEditorChangedSignal(&externalEditorChangedSignal);
|
||||
|
||||
// construct closeable tab for the image
|
||||
Gtk::Box* hb = Gtk::manage (new Gtk::Box ());
|
||||
@ -288,6 +289,7 @@ void EditWindow::remEditorPanel (EditorPanel* ep)
|
||||
return; // Will crash if destroyed while loading
|
||||
}
|
||||
|
||||
ep->setExternalEditorChangedSignal(nullptr);
|
||||
epanels.erase (ep->getFileName());
|
||||
filesEdited.erase (ep->getFileName ());
|
||||
parent->fpanel->refreshEditedState (filesEdited);
|
||||
|
@ -39,6 +39,8 @@ private:
|
||||
std::set<Glib::ustring> filesEdited;
|
||||
std::map<Glib::ustring, EditorPanel*> epanels;
|
||||
|
||||
sigc::signal<void> externalEditorChangedSignal;
|
||||
|
||||
bool isFullscreen;
|
||||
bool isClosed;
|
||||
bool isMinimized;
|
||||
|
354
rtgui/externaleditorpreferences.cc
Normal file
354
rtgui/externaleditorpreferences.cc
Normal file
@ -0,0 +1,354 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2021 Lawrence Lee <billee@ucdavis.edu>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* RawTherapee is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <iostream>
|
||||
|
||||
#include <giomm/contenttype.h>
|
||||
#include <glibmm/shell.h>
|
||||
#include <gtkmm/filechooserdialog.h>
|
||||
#include <gtkmm/stock.h>
|
||||
|
||||
#include "externaleditorpreferences.h"
|
||||
#include "multilangmgr.h"
|
||||
#include "rtimage.h"
|
||||
|
||||
|
||||
ExternalEditorPreferences::ExternalEditorPreferences():
|
||||
Box(Gtk::Orientation::ORIENTATION_VERTICAL),
|
||||
list_model(Gtk::ListStore::create(model_columns)),
|
||||
toolbar(Gtk::Orientation::ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
// List view.
|
||||
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(makeCommandColumn()));
|
||||
|
||||
for (auto &&column : list_view->get_columns()) {
|
||||
column->set_sizing(Gtk::TreeViewColumnSizing::TREE_VIEW_COLUMN_FIXED);
|
||||
}
|
||||
|
||||
list_view->set_grid_lines(Gtk::TREE_VIEW_GRID_LINES_VERTICAL);
|
||||
list_view->set_reorderable();
|
||||
|
||||
// List scroll area.
|
||||
list_scroll_area.set_hexpand();
|
||||
list_scroll_area.set_vexpand();
|
||||
list_scroll_area.add(*list_view);
|
||||
|
||||
// Toolbar buttons.
|
||||
auto add_image = Gtk::manage(new RTImage("add-small.png"));
|
||||
auto remove_image = Gtk::manage(new RTImage("remove-small.png"));
|
||||
button_add = Gtk::manage(new Gtk::Button());
|
||||
button_remove = Gtk::manage(new Gtk::Button());
|
||||
button_add->set_image(*add_image);
|
||||
button_remove->set_image(*remove_image);
|
||||
button_app_chooser = Gtk::manage(new Gtk::Button(M("PREFERENCES_EXTERNALEDITOR_CHANGE")));
|
||||
button_file_chooser = Gtk::manage(new Gtk::Button(M("PREFERENCES_EXTERNALEDITOR_CHANGE_FILE")));
|
||||
|
||||
button_app_chooser->signal_pressed().connect(sigc::mem_fun(
|
||||
*this, &ExternalEditorPreferences::openAppChooserDialog));
|
||||
button_add->signal_pressed().connect(sigc::mem_fun(
|
||||
*this, &ExternalEditorPreferences::addEditor));
|
||||
button_file_chooser->signal_pressed().connect(sigc::mem_fun(
|
||||
*this, &ExternalEditorPreferences::openFileChooserDialog));
|
||||
button_remove->signal_pressed().connect(sigc::mem_fun(
|
||||
*this, &ExternalEditorPreferences::removeSelectedEditors));
|
||||
|
||||
list_view->get_selection()->signal_changed().connect(sigc::mem_fun(
|
||||
*this, &ExternalEditorPreferences::updateToolbarSensitivity));
|
||||
updateToolbarSensitivity();
|
||||
|
||||
// Toolbar.
|
||||
toolbar.set_halign(Gtk::Align::ALIGN_END);
|
||||
toolbar.add(*button_app_chooser);
|
||||
toolbar.add(*button_file_chooser);
|
||||
toolbar.add(*button_add);
|
||||
toolbar.add(*button_remove);
|
||||
|
||||
// This widget's children.
|
||||
add(list_scroll_area);
|
||||
add(toolbar);
|
||||
show_all();
|
||||
}
|
||||
|
||||
std::vector<ExternalEditorPreferences::EditorInfo>
|
||||
ExternalEditorPreferences::getEditors() const
|
||||
{
|
||||
std::vector<ExternalEditorPreferences::EditorInfo> editors;
|
||||
|
||||
auto children = list_model->children();
|
||||
|
||||
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)
|
||||
));
|
||||
}
|
||||
|
||||
return editors;
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::setEditors(
|
||||
const std::vector<ExternalEditorPreferences::EditorInfo> &editors)
|
||||
{
|
||||
list_model->clear();
|
||||
|
||||
for (const ExternalEditorPreferences::EditorInfo & editor : editors) {
|
||||
auto row = *list_model->append();
|
||||
Glib::RefPtr<Gio::Icon> icon;
|
||||
|
||||
// Get icon.
|
||||
if (editor.icon_serialized.empty()) {
|
||||
icon = Glib::RefPtr<Gio::Icon>();
|
||||
} else {
|
||||
GError *e = nullptr;
|
||||
GVariant *icon_variant = g_variant_parse(
|
||||
nullptr, editor.icon_serialized.c_str(), nullptr, nullptr, &e);
|
||||
if (e) {
|
||||
std::cerr
|
||||
<< "Error loading external editor icon from \""
|
||||
<< editor.icon_serialized << "\": " << e->message
|
||||
<< std::endl;
|
||||
icon = Glib::RefPtr<Gio::Icon>();
|
||||
} else {
|
||||
icon = Gio::Icon::deserialize(Glib::VariantBase(icon_variant));
|
||||
}
|
||||
}
|
||||
|
||||
row[model_columns.name] = editor.name;
|
||||
row[model_columns.icon] = icon;
|
||||
row[model_columns.command] = editor.command;
|
||||
row[model_columns.other_data] = editor.other_data;
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::addEditor()
|
||||
{
|
||||
Gtk::TreeModel::Row row;
|
||||
auto selected = list_view->get_selection()->get_selected_rows();
|
||||
|
||||
if (selected.size()) {
|
||||
row = *list_model->insert_after(list_model->get_iter(selected.back()));
|
||||
} else {
|
||||
row = *list_model->append();
|
||||
}
|
||||
|
||||
row[model_columns.name] = "-";
|
||||
list_view->get_selection()->select(row);
|
||||
}
|
||||
|
||||
Gtk::TreeViewColumn *ExternalEditorPreferences::makeAppColumn()
|
||||
{
|
||||
auto name_renderer = Gtk::manage(new Gtk::CellRendererText());
|
||||
auto icon_renderer = Gtk::manage(new Gtk::CellRendererPixbuf());
|
||||
auto col = Gtk::manage(new Gtk::TreeViewColumn());
|
||||
|
||||
col->set_title(M("PREFERENCES_EXTERNALEDITOR_COLUMN_NAME"));
|
||||
col->set_resizable();
|
||||
col->pack_start(*icon_renderer, false);
|
||||
col->pack_start(*name_renderer);
|
||||
col->add_attribute(*icon_renderer, "gicon", model_columns.icon);
|
||||
col->add_attribute(*name_renderer, "text", model_columns.name);
|
||||
col->set_min_width(20);
|
||||
|
||||
name_renderer->property_editable() = true;
|
||||
name_renderer->signal_edited().connect(
|
||||
sigc::mem_fun(*this, &ExternalEditorPreferences::setAppName));
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
Gtk::TreeViewColumn *ExternalEditorPreferences::makeCommandColumn()
|
||||
{
|
||||
auto command_renderer = Gtk::manage(new Gtk::CellRendererText());
|
||||
auto col = Gtk::manage(new Gtk::TreeViewColumn());
|
||||
|
||||
col->set_title(M("PREFERENCES_EXTERNALEDITOR_COLUMN_COMMAND"));
|
||||
col->pack_start(*command_renderer);
|
||||
col->add_attribute(*command_renderer, "text", model_columns.command);
|
||||
|
||||
command_renderer->property_editable() = true;
|
||||
command_renderer->signal_edited().connect(
|
||||
sigc::mem_fun(*this, &ExternalEditorPreferences::setAppCommand));
|
||||
|
||||
return col;
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::onAppChooserDialogResponse(
|
||||
int response_id, RTAppChooserDialog *dialog)
|
||||
{
|
||||
switch (response_id) {
|
||||
case Gtk::RESPONSE_OK:
|
||||
dialog->close();
|
||||
setApp(dialog->get_app_info());
|
||||
break;
|
||||
|
||||
case Gtk::RESPONSE_CANCEL:
|
||||
case Gtk::RESPONSE_CLOSE:
|
||||
dialog->close();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::onFileChooserDialogResponse(
|
||||
int response_id, Gtk::FileChooserDialog *dialog)
|
||||
{
|
||||
switch (response_id) {
|
||||
case Gtk::RESPONSE_OK: {
|
||||
dialog->close();
|
||||
|
||||
auto selection = list_view->get_selection()->get_selected_rows();
|
||||
for (const auto &selected : selection) {
|
||||
auto row = *list_model->get_iter(selected);
|
||||
row[model_columns.icon] = Glib::RefPtr<Gio::Icon>(nullptr);
|
||||
row[model_columns.command] =
|
||||
#ifdef WIN32
|
||||
'"' + dialog->get_filename() + '"';
|
||||
#else
|
||||
Glib::shell_quote(dialog->get_filename());
|
||||
#endif
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case Gtk::RESPONSE_CANCEL:
|
||||
case Gtk::RESPONSE_CLOSE:
|
||||
dialog->close();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::openAppChooserDialog()
|
||||
{
|
||||
if (app_chooser_dialog.get()) {
|
||||
app_chooser_dialog->refresh();
|
||||
app_chooser_dialog->show();
|
||||
return;
|
||||
}
|
||||
|
||||
app_chooser_dialog.reset(new RTAppChooserDialog("image/tiff"));
|
||||
app_chooser_dialog->signal_response().connect(sigc::bind(
|
||||
sigc::mem_fun(*this, &ExternalEditorPreferences::onAppChooserDialogResponse),
|
||||
app_chooser_dialog.get()
|
||||
));
|
||||
app_chooser_dialog->set_modal();
|
||||
app_chooser_dialog->show();
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::openFileChooserDialog()
|
||||
{
|
||||
if (file_chooser_dialog.get()) {
|
||||
file_chooser_dialog->show();
|
||||
return;
|
||||
}
|
||||
|
||||
file_chooser_dialog.reset(new Gtk::FileChooserDialog(M("PREFERENCES_EXTERNALEDITOR_CHANGE_FILE")));
|
||||
|
||||
const auto exe_filter = Gtk::FileFilter::create();
|
||||
exe_filter->set_name(M("FILECHOOSER_FILTER_EXECUTABLE"));
|
||||
exe_filter->add_custom(Gtk::FILE_FILTER_MIME_TYPE, [](const Gtk::FileFilter::Info &info) {
|
||||
return Gio::content_type_can_be_executable(info.mime_type);
|
||||
});
|
||||
const auto all_filter = Gtk::FileFilter::create();
|
||||
all_filter->set_name(M("FILECHOOSER_FILTER_ANY"));
|
||||
all_filter->add_pattern("*");
|
||||
file_chooser_dialog->add_filter(exe_filter);
|
||||
file_chooser_dialog->add_filter(all_filter);
|
||||
|
||||
file_chooser_dialog->signal_response().connect(sigc::bind(
|
||||
sigc::mem_fun(*this, &ExternalEditorPreferences::onFileChooserDialogResponse),
|
||||
file_chooser_dialog.get()));
|
||||
file_chooser_dialog->set_modal();
|
||||
file_chooser_dialog->add_button(M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL);
|
||||
file_chooser_dialog->add_button(M("GENERAL_OPEN"), Gtk::RESPONSE_OK);
|
||||
file_chooser_dialog->show();
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::removeSelectedEditors()
|
||||
{
|
||||
auto selection = list_view->get_selection()->get_selected_rows();
|
||||
|
||||
for (const auto &selected : selection) {
|
||||
list_model->erase(list_model->get_iter(selected));
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::setApp(const Glib::RefPtr<Gio::AppInfo> app_info)
|
||||
{
|
||||
auto selection = list_view->get_selection()->get_selected_rows();
|
||||
|
||||
for (const auto &selected : selection) {
|
||||
auto row = *list_model->get_iter(selected);
|
||||
row[model_columns.icon] = app_info->get_icon();
|
||||
row[model_columns.name] = app_info->get_name();
|
||||
row[model_columns.command] = app_info->get_commandline();
|
||||
}
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::setAppCommand(
|
||||
const Glib::ustring & path, const Glib::ustring & new_text)
|
||||
{
|
||||
auto row_iter = list_model->get_iter(path);
|
||||
|
||||
if (!row_iter->get_value(model_columns.command).compare(new_text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
row_iter->set_value(model_columns.command, new_text);
|
||||
row_iter->set_value(model_columns.icon, Glib::RefPtr<Gio::Icon>(nullptr));
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::setAppName(
|
||||
const Glib::ustring & path, const Glib::ustring & new_text)
|
||||
{
|
||||
list_model->get_iter(path)->set_value(model_columns.name, new_text);
|
||||
}
|
||||
|
||||
void ExternalEditorPreferences::updateToolbarSensitivity()
|
||||
{
|
||||
bool selected = list_view->get_selection()->count_selected_rows();
|
||||
button_app_chooser->set_sensitive(selected);
|
||||
button_file_chooser->set_sensitive(selected);
|
||||
button_remove->set_sensitive(selected);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalEditorPreferences::ModelColumns::ModelColumns()
|
||||
{
|
||||
add(name);
|
||||
add(icon);
|
||||
add(command);
|
||||
add(other_data);
|
||||
}
|
167
rtgui/externaleditorpreferences.h
Normal file
167
rtgui/externaleditorpreferences.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2021 Lawrence Lee <billee@ucdavis.edu>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* RawTherapee is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <gtkmm/liststore.h>
|
||||
#include <gtkmm/scrolledwindow.h>
|
||||
#include <gtkmm/treemodelcolumn.h>
|
||||
#include <gtkmm/treeview.h>
|
||||
|
||||
#include "rtappchooserdialog.h"
|
||||
|
||||
|
||||
namespace Gtk
|
||||
{
|
||||
|
||||
class FileChooserDialog;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Widget for editing the external editors options.
|
||||
*/
|
||||
class ExternalEditorPreferences : public Gtk::Box
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Data struct containing information about an external editor.
|
||||
*/
|
||||
struct EditorInfo {
|
||||
explicit EditorInfo(
|
||||
Glib::ustring name = Glib::ustring(),
|
||||
Glib::ustring command = Glib::ustring(),
|
||||
Glib::ustring icon_serialized = Glib::ustring(),
|
||||
void *other_data = nullptr
|
||||
);
|
||||
/**
|
||||
* Name of the external editor.
|
||||
*/
|
||||
Glib::ustring name;
|
||||
/**
|
||||
* The string representation of the icon. See Gio::Icon::serialize().
|
||||
*/
|
||||
Glib::ustring icon_serialized;
|
||||
/**
|
||||
* The commandline for running the program. See
|
||||
* Gio::AppInfo::get_commandline()
|
||||
*/
|
||||
Glib::ustring command;
|
||||
/**
|
||||
* Holds any other data associated with the editor. For example, it can
|
||||
* be used as a tag to uniquely identify the editor.
|
||||
*/
|
||||
void *other_data;
|
||||
};
|
||||
|
||||
ExternalEditorPreferences();
|
||||
|
||||
/**
|
||||
* Creates and returns a vector representing the external editors shown in
|
||||
* this widget.
|
||||
*/
|
||||
std::vector<EditorInfo> getEditors() const;
|
||||
/**
|
||||
* Populates this widget with the external editors described in the
|
||||
* argument.
|
||||
*/
|
||||
void setEditors(const std::vector<EditorInfo> &editors);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Model representing the data fields each external editor entry has.
|
||||
*/
|
||||
class ModelColumns : public Gtk::TreeModelColumnRecord
|
||||
{
|
||||
public:
|
||||
ModelColumns();
|
||||
Gtk::TreeModelColumn<Glib::ustring> name;
|
||||
Gtk::TreeModelColumn<Glib::RefPtr<Gio::Icon>> icon;
|
||||
Gtk::TreeModelColumn<Glib::ustring> command;
|
||||
Gtk::TreeModelColumn<void *> other_data;
|
||||
};
|
||||
|
||||
ModelColumns model_columns;
|
||||
Glib::RefPtr<Gtk::ListStore> list_model; // The list of editors.
|
||||
Gtk::ScrolledWindow list_scroll_area; // Allows the list to be scrolled.
|
||||
Gtk::TreeView *list_view; // Widget for displaying the list.
|
||||
Gtk::Box toolbar; // Contains buttons for editing the list.
|
||||
Gtk::Button *button_app_chooser;
|
||||
Gtk::Button *button_add;
|
||||
Gtk::Button *button_file_chooser;
|
||||
Gtk::Button *button_remove;
|
||||
std::unique_ptr<RTAppChooserDialog> app_chooser_dialog;
|
||||
std::unique_ptr<Gtk::FileChooserDialog> file_chooser_dialog;
|
||||
|
||||
/**
|
||||
* Inserts a new editor entry after the current selection, or at the end if
|
||||
* no editor is selected.
|
||||
*/
|
||||
void addEditor();
|
||||
/**
|
||||
* Constructs the column for displaying the external editor name (and icon).
|
||||
*/
|
||||
Gtk::TreeViewColumn *makeAppColumn();
|
||||
/**
|
||||
* Constructs the column for displaying an editable commandline.
|
||||
*/
|
||||
Gtk::TreeViewColumn *makeCommandColumn();
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
void onAppChooserDialogResponse(int responseId, RTAppChooserDialog *dialog);
|
||||
/**
|
||||
* Called when the user is done interacting with the file chooser dialog.
|
||||
* Closes the dialog and updates the selected entry if a file was chosen.
|
||||
*/
|
||||
void onFileChooserDialogResponse(int responseId, Gtk::FileChooserDialog *dialog);
|
||||
/**
|
||||
* Shows the app chooser dialog.
|
||||
*/
|
||||
void openAppChooserDialog();
|
||||
/**
|
||||
* Shows the file chooser dialog for picking an executable.
|
||||
*/
|
||||
void openFileChooserDialog();
|
||||
/**
|
||||
* Removes all selected editors.
|
||||
*/
|
||||
void removeSelectedEditors();
|
||||
/**
|
||||
* Sets the selected entries with the provided information.
|
||||
*/
|
||||
void setApp(const Glib::RefPtr<Gio::AppInfo> app_info);
|
||||
/**
|
||||
* Updates the application command and removes the icon for the given row.
|
||||
*/
|
||||
void setAppCommand(const Glib::ustring & path, const Glib::ustring & new_text);
|
||||
/**
|
||||
* Updates the application name for the given row.
|
||||
*/
|
||||
void setAppName(const Glib::ustring & path, const Glib::ustring & new_text);
|
||||
/**
|
||||
* Sets the sensitivity of the widgets in the toolbar to reflect the current
|
||||
* state of the list. For example, makes the remove button insensitive if no
|
||||
* entries are selected.
|
||||
*/
|
||||
void updateToolbarSensitivity();
|
||||
};
|
@ -339,3 +339,16 @@ bool ExtProgStore::openInCustomEditor (const Glib::ustring& fileName)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool ExtProgStore::openInExternalEditor(const Glib::ustring &fileName, const Glib::RefPtr<Gio::AppInfo> &editorInfo)
|
||||
{
|
||||
try {
|
||||
return editorInfo->launch(Gio::File::create_for_path(fileName));
|
||||
} catch (const Glib::Error &e) {
|
||||
std::cerr
|
||||
<< "Error launching external editor.\n"
|
||||
<< "Error code #" << e.code() << ": " << e.what()
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -20,10 +20,16 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <glibmm/refptr.h>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include "threadutils.h"
|
||||
|
||||
namespace Gio
|
||||
{
|
||||
class AppInfo;
|
||||
}
|
||||
|
||||
struct ExtProgAction
|
||||
{
|
||||
Glib::ustring filePathEXE;
|
||||
@ -64,6 +70,7 @@ public:
|
||||
static bool openInGimp (const Glib::ustring& fileName);
|
||||
static bool openInPhotoshop (const Glib::ustring& fileName);
|
||||
static bool openInCustomEditor (const Glib::ustring& fileName);
|
||||
static bool openInExternalEditor(const Glib::ustring &fileName, const Glib::RefPtr<Gio::AppInfo> &editorInfo);
|
||||
};
|
||||
|
||||
#define extProgStore ExtProgStore::getInstance()
|
||||
|
@ -1507,13 +1507,28 @@ TextOrIcon::TextOrIcon (const Glib::ustring &fname, const Glib::ustring &labelTx
|
||||
}
|
||||
|
||||
MyImageMenuItem::MyImageMenuItem(Glib::ustring label, Glib::ustring imageFileName)
|
||||
{
|
||||
RTImage* itemImage = nullptr;
|
||||
|
||||
if (!imageFileName.empty()) {
|
||||
itemImage = Gtk::manage(new RTImage(imageFileName));
|
||||
}
|
||||
|
||||
construct(label, itemImage);
|
||||
}
|
||||
|
||||
MyImageMenuItem::MyImageMenuItem(Glib::ustring label, RTImage* itemImage) {
|
||||
construct(label, itemImage);
|
||||
}
|
||||
|
||||
void MyImageMenuItem::construct(Glib::ustring label, RTImage* itemImage)
|
||||
{
|
||||
box = Gtk::manage (new Gtk::Grid());
|
||||
this->label = Gtk::manage( new Gtk::Label(label));
|
||||
box->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
|
||||
|
||||
if (!imageFileName.empty()) {
|
||||
image = Gtk::manage( new RTImage(imageFileName) );
|
||||
if (itemImage) {
|
||||
image = itemImage;
|
||||
box->attach_next_to(*image, Gtk::POS_LEFT, 1, 1);
|
||||
} else {
|
||||
image = nullptr;
|
||||
|
@ -489,8 +489,11 @@ private:
|
||||
RTImage *image;
|
||||
Gtk::Label *label;
|
||||
|
||||
void construct(Glib::ustring label, RTImage* image);
|
||||
|
||||
public:
|
||||
MyImageMenuItem (Glib::ustring label, Glib::ustring imageFileName);
|
||||
MyImageMenuItem (Glib::ustring label, RTImage* image);
|
||||
const RTImage *getImage () const;
|
||||
const Gtk::Label* getLabel () const;
|
||||
};
|
||||
|
188
rtgui/options.cc
188
rtgui/options.cc
@ -412,6 +412,8 @@ void Options::setDefaults()
|
||||
gimpDir = "";
|
||||
psDir = "";
|
||||
customEditorProg = "";
|
||||
externalEditors.clear();
|
||||
externalEditorIndex = -1;
|
||||
CPBKeys = CPBKT_TID;
|
||||
editorToSendTo = 1;
|
||||
editor_out_dir = EDITOR_OUT_DIR_TEMP;
|
||||
@ -821,6 +823,7 @@ void Options::readFromFile(Glib::ustring fname)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Remove.
|
||||
if (keyFile.has_group("External Editor")) {
|
||||
if (keyFile.has_key("External Editor", "EditorKind")) {
|
||||
editorToSendTo = keyFile.get_integer("External Editor", "EditorKind");
|
||||
@ -861,6 +864,156 @@ 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", "IconsSerialized")) {
|
||||
// Multiple external editors.
|
||||
|
||||
const auto & names =
|
||||
!keyFile.has_key("External Editor", "Names") ?
|
||||
std::vector<Glib::ustring>() :
|
||||
static_cast<std::vector<Glib::ustring>>(
|
||||
keyFile.get_string_list("External Editor", "Names"));
|
||||
const auto & commands =
|
||||
!keyFile.has_key("External Editor", "Commands") ?
|
||||
std::vector<Glib::ustring>() :
|
||||
static_cast<std::vector<Glib::ustring>>(
|
||||
keyFile.get_string_list("External Editor", "Commands"));
|
||||
const auto & icons_serialized =
|
||||
!keyFile.has_key("External Editor", "IconsSerialized") ?
|
||||
std::vector<Glib::ustring>() :
|
||||
static_cast<std::vector<Glib::ustring>>(
|
||||
keyFile.get_string_list("External Editor", "IconsSerialized"));
|
||||
externalEditors = std::vector<ExternalEditor>(std::max(std::max(
|
||||
names.size(), commands.size()), icons_serialized.size()));
|
||||
for (unsigned i = 0; i < names.size(); i++) {
|
||||
externalEditors[i].name = names[i];
|
||||
}
|
||||
for (unsigned i = 0; i < commands.size(); i++) {
|
||||
externalEditors[i].command = commands[i];
|
||||
}
|
||||
for (unsigned i = 0; i < icons_serialized.size(); i++) {
|
||||
externalEditors[i].icon_serialized = icons_serialized[i];
|
||||
}
|
||||
|
||||
if (keyFile.has_key("External Editor", "EditorIndex")) {
|
||||
int index = keyFile.get_integer("External Editor", "EditorIndex");
|
||||
externalEditorIndex = std::min(
|
||||
std::max(-1, index),
|
||||
static_cast<int>(externalEditors.size())
|
||||
);
|
||||
}
|
||||
} else if (keyFile.has_key("External Editor", "EditorKind")) {
|
||||
// Legacy fixed external editors. Convert to flexible.
|
||||
|
||||
// GIMP == 1, Photoshop == 2, Custom == 3.
|
||||
editorToSendTo = keyFile.get_integer("External Editor", "EditorKind");
|
||||
|
||||
#ifdef WIN32
|
||||
auto getIconSerialized = [](const Glib::ustring &executable) {
|
||||
// Backslashes and quotes must be escaped in the text representation of GVariant strings.
|
||||
// See https://www.freedesktop.org/software/gstreamer-sdk/data/docs/2012.5/glib/gvariant-text.html#gvariant-text-strings
|
||||
Glib::ustring exec_escaped = "";
|
||||
for (const auto character : executable) {
|
||||
if (character == '\\' || character == '\'') {
|
||||
exec_escaped += '\\';
|
||||
}
|
||||
exec_escaped += character;
|
||||
}
|
||||
return Glib::ustring::compose("('themed', <['%1,0', '%1,0-symbolic']>)", exec_escaped);
|
||||
};
|
||||
Glib::ustring gimpDir = "";
|
||||
if (keyFile.has_key("External Editor", "GimpDir")) {
|
||||
gimpDir = keyFile.get_string("External Editor", "GimpDir");
|
||||
}
|
||||
auto executable = Glib::build_filename(options.gimpDir, "bin", "gimp-win-remote");
|
||||
if (Glib::file_test(executable, Glib::FILE_TEST_IS_EXECUTABLE)) {
|
||||
if (editorToSendTo == 1) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("GIMP", "\"" + executable + "\"", 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));
|
||||
if (Glib::file_test(executable, Glib::FILE_TEST_IS_EXECUTABLE)) {
|
||||
if (editorToSendTo == 1) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("GIMP", "\"" + executable + "\"", getIconSerialized(executable)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Glib::ustring psDir = "";
|
||||
if (keyFile.has_key("External Editor", "PhotoshopDir")) {
|
||||
psDir = keyFile.get_string("External Editor", "PhotoshopDir");
|
||||
}
|
||||
executable = Glib::build_filename(psDir, "Photoshop.exe");
|
||||
if (Glib::file_test(executable, Glib::FILE_TEST_IS_EXECUTABLE)) {
|
||||
if (editorToSendTo == 2) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("Photoshop", "\"" + executable + "\"", getIconSerialized(executable)));
|
||||
}
|
||||
|
||||
if (keyFile.has_key("External Editor", "CustomEditor")) {
|
||||
executable = keyFile.get_string("External Editor", "CustomEditor");
|
||||
if (!executable.empty()) {
|
||||
if (editorToSendTo == 3) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("-", "\"" + executable + "\"", ""));
|
||||
}
|
||||
}
|
||||
#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"));
|
||||
|
||||
if (editorToSendTo == 2) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("Photoshop", "open -a Photoshop", ""));
|
||||
|
||||
if (keyFile.has_key("External Editor", "CustomEditor")) {
|
||||
auto executable = keyFile.get_string("External Editor", "CustomEditor");
|
||||
if (!executable.empty()) {
|
||||
if (editorToSendTo == 3) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("-", executable, ""));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (Glib::find_program_in_path("gimp").compare("")) {
|
||||
if (editorToSendTo == 1) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("GIMP", "gimp", "gimp"));
|
||||
} else if (Glib::find_program_in_path("gimp-remote").compare("")) {
|
||||
if (editorToSendTo == 1) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("GIMP", "gimp-remote", "gimp"));
|
||||
}
|
||||
|
||||
if (keyFile.has_key("External Editor", "CustomEditor")) {
|
||||
auto executable = keyFile.get_string("External Editor", "CustomEditor");
|
||||
if (!executable.empty()) {
|
||||
if (editorToSendTo == 3) {
|
||||
externalEditorIndex = externalEditors.size();
|
||||
}
|
||||
externalEditors.push_back(ExternalEditor("-", executable, ""));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (keyFile.has_group("Output")) {
|
||||
if (keyFile.has_key("Output", "Format")) {
|
||||
saveFormat.format = keyFile.get_string("Output", "Format");
|
||||
@ -2175,6 +2328,7 @@ void Options::saveToFile(Glib::ustring fname)
|
||||
keyFile.set_boolean("General", "Detectshape", rtSettings.detectshape);
|
||||
keyFile.set_boolean("General", "Fftwsigma", rtSettings.fftwsigma);
|
||||
|
||||
// TODO: Remove.
|
||||
keyFile.set_integer("External Editor", "EditorKind", editorToSendTo);
|
||||
keyFile.set_string("External Editor", "GimpDir", gimpDir);
|
||||
keyFile.set_string("External Editor", "PhotoshopDir", psDir);
|
||||
@ -2184,6 +2338,24 @@ void Options::saveToFile(Glib::ustring fname)
|
||||
keyFile.set_boolean("External Editor", "Float32", editor_float32);
|
||||
keyFile.set_boolean("External Editor", "BypassOutputProfile", editor_bypass_output_profile);
|
||||
|
||||
{
|
||||
std::vector<Glib::ustring> names;
|
||||
std::vector<Glib::ustring> commands;
|
||||
std::vector<Glib::ustring> icons_serialized;
|
||||
|
||||
for (const auto & editor : externalEditors) {
|
||||
names.push_back(editor.name);
|
||||
commands.push_back(editor.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_string_list("External Editor", "IconsSerialized", icons_serialized);
|
||||
|
||||
keyFile.set_integer("External Editor", "EditorIndex", externalEditorIndex);
|
||||
}
|
||||
|
||||
keyFile.set_boolean("File Browser", "BrowseOnlyRaw", fbOnlyRaw);
|
||||
keyFile.set_boolean("File Browser", "BrowserShowsDate", fbShowDateTime);
|
||||
keyFile.set_boolean("File Browser", "BrowserShowsExif", fbShowBasicExif);
|
||||
@ -2853,3 +3025,19 @@ Glib::ustring Options::getICCProfileCopyright()
|
||||
now.set_time_current();
|
||||
return Glib::ustring::compose("Copyright RawTherapee %1, CC0", now.get_year());
|
||||
}
|
||||
|
||||
ExternalEditor::ExternalEditor() {}
|
||||
|
||||
ExternalEditor::ExternalEditor(
|
||||
const Glib::ustring &name, const Glib::ustring &command, const Glib::ustring &icon_serialized
|
||||
): name(name), command(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;
|
||||
}
|
||||
|
||||
bool ExternalEditor::operator!=(const ExternalEditor &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
@ -52,6 +52,17 @@
|
||||
// Special name for the Dynamic profile
|
||||
#define DEFPROFILE_DYNAMIC "Dynamic"
|
||||
|
||||
struct ExternalEditor {
|
||||
ExternalEditor();
|
||||
ExternalEditor(const Glib::ustring &name, const Glib::ustring &command, const Glib::ustring &icon_serialized);
|
||||
Glib::ustring name;
|
||||
Glib::ustring command;
|
||||
Glib::ustring icon_serialized;
|
||||
|
||||
bool operator==(const ExternalEditor & other) const;
|
||||
bool operator!=(const ExternalEditor & other) const;
|
||||
};
|
||||
|
||||
struct SaveFormat {
|
||||
SaveFormat(
|
||||
const Glib::ustring& _format,
|
||||
@ -276,6 +287,8 @@ public:
|
||||
Glib::ustring gimpDir;
|
||||
Glib::ustring psDir;
|
||||
Glib::ustring customEditorProg;
|
||||
std::vector<ExternalEditor> externalEditors;
|
||||
int externalEditorIndex;
|
||||
Glib::ustring CPBPath; // Custom Profile Builder's path
|
||||
CPBKeyType CPBKeys; // Custom Profile Builder's key type
|
||||
int editorToSendTo;
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
PopUpCommon::PopUpCommon (Gtk::Button* thisButton, const Glib::ustring& label)
|
||||
: buttonImage (nullptr)
|
||||
, menu (nullptr)
|
||||
, menu(new Gtk::Menu())
|
||||
, selected (-1) // -1 means that the button is invalid
|
||||
{
|
||||
button = thisButton;
|
||||
@ -48,59 +48,148 @@ PopUpCommon::PopUpCommon (Gtk::Button* thisButton, const Glib::ustring& label)
|
||||
setExpandAlignProperties(buttonGroup, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
|
||||
buttonGroup->attach(*button, 0, 0, 1, 1);
|
||||
buttonGroup->get_style_context()->add_class("image-combo");
|
||||
|
||||
// Create the image for the button
|
||||
buttonImage = Gtk::manage(new RTImage());
|
||||
setExpandAlignProperties(buttonImage, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
|
||||
imageContainer->attach_next_to(*buttonImage, Gtk::POS_RIGHT, 1, 1);
|
||||
buttonImage->set_no_show_all();
|
||||
|
||||
// Create the button for showing the pop-up.
|
||||
arrowButton = Gtk::manage(new Gtk::Button());
|
||||
Gtk::Image *arrowImage = Gtk::manage(new Gtk::Image());
|
||||
arrowImage->set_from_icon_name("pan-down-symbolic", Gtk::ICON_SIZE_BUTTON);
|
||||
setExpandAlignProperties(arrowButton, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
||||
arrowButton->add(*arrowImage); //menuSymbol);
|
||||
arrowImage->show();
|
||||
buttonGroup->attach_next_to(*arrowButton, *button, Gtk::POS_RIGHT, 1, 1);
|
||||
arrowButton->signal_button_release_event().connect_notify(sigc::mem_fun(*this, &PopUpCommon::showMenu));
|
||||
arrowButton->get_style_context()->add_class("Right");
|
||||
arrowButton->get_style_context()->add_class("popupbutton-arrow");
|
||||
arrowButton->set_no_show_all();
|
||||
}
|
||||
|
||||
PopUpCommon::~PopUpCommon ()
|
||||
{
|
||||
delete menu;
|
||||
delete buttonImage;
|
||||
}
|
||||
|
||||
bool PopUpCommon::addEntry (const Glib::ustring& fileName, const Glib::ustring& label)
|
||||
{
|
||||
if (label.empty ())
|
||||
return false;
|
||||
return insertEntry(getEntryCount(), fileName, label);
|
||||
}
|
||||
|
||||
bool PopUpCommon::insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label)
|
||||
{
|
||||
RTImage* image = nullptr;
|
||||
if (!fileName.empty()) {
|
||||
image = Gtk::manage(new RTImage(fileName));
|
||||
}
|
||||
bool success = insertEntryImpl(position, fileName, Glib::RefPtr<const Gio::Icon>(), image, label);
|
||||
if (!success && image) {
|
||||
delete image;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool PopUpCommon::insertEntry(int position, const Glib::RefPtr<const Gio::Icon>& gIcon, const Glib::ustring& label)
|
||||
{
|
||||
RTImage* image = Gtk::manage(new RTImage(gIcon, Gtk::ICON_SIZE_BUTTON));
|
||||
bool success = insertEntryImpl(position, "", gIcon, image, label);
|
||||
if (!success) {
|
||||
delete image;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool PopUpCommon::insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon, RTImage* image, const Glib::ustring& label)
|
||||
{
|
||||
if (label.empty() || position < 0 || position > getEntryCount())
|
||||
return false;
|
||||
|
||||
// Create the menu item and image
|
||||
MyImageMenuItem* newItem = Gtk::manage (new MyImageMenuItem (label, fileName));
|
||||
imageFilenames.push_back (fileName);
|
||||
images.push_back (newItem->getImage ());
|
||||
|
||||
if (selected == -1) {
|
||||
// Create the menu on the first item
|
||||
menu = new Gtk::Menu ();
|
||||
// Create the image for the button
|
||||
buttonImage = new RTImage(fileName);
|
||||
setExpandAlignProperties(buttonImage, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
|
||||
// Use the first image by default
|
||||
imageContainer->attach_next_to(*buttonImage, Gtk::POS_RIGHT, 1, 1);
|
||||
selected = 0;
|
||||
}
|
||||
MyImageMenuItem *newItem = Gtk::manage(new MyImageMenuItem(label, image));
|
||||
imageIcons.insert(imageIcons.begin() + position, gIcon);
|
||||
imageFilenames.insert(imageFilenames.begin() + position, fileName);
|
||||
images.insert(images.begin() + position, newItem->getImage());
|
||||
|
||||
// When there is at least 1 choice, we add the arrow button
|
||||
if (images.size() == 1) {
|
||||
Gtk::Button* arrowButton = Gtk::manage( new Gtk::Button() );
|
||||
Gtk::Image *arrowImage = Gtk::manage(new Gtk::Image());
|
||||
arrowImage->set_from_icon_name("pan-down-symbolic", Gtk::ICON_SIZE_BUTTON);
|
||||
setExpandAlignProperties(arrowButton, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
||||
arrowButton->add(*arrowImage); //menuSymbol);
|
||||
buttonGroup->attach_next_to(*arrowButton, *button, Gtk::POS_RIGHT, 1, 1);
|
||||
arrowButton->signal_button_release_event().connect_notify( sigc::mem_fun(*this, &PopUpCommon::showMenu) );
|
||||
changeImage(fileName, gIcon);
|
||||
buttonImage->show();
|
||||
selected = 0;
|
||||
button->get_style_context()->add_class("Left");
|
||||
arrowButton->get_style_context()->add_class("Right");
|
||||
arrowButton->get_style_context()->add_class("popupbutton-arrow");
|
||||
arrowButton->show();
|
||||
hasMenu = true;
|
||||
} else if (position <= selected) {
|
||||
selected++;
|
||||
}
|
||||
|
||||
newItem->signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, &PopUpCommon::entrySelected), images.size () - 1));
|
||||
menu->append (*newItem);
|
||||
|
||||
void (PopUpCommon::*entrySelectedFunc)(Gtk::Widget *) = &PopUpCommon::entrySelected;
|
||||
newItem->signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, entrySelectedFunc), newItem));
|
||||
menu->insert(*newItem, position);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: 'PopUpCommon::removeEntry' method to be created...
|
||||
void PopUpCommon::removeEntry(int position)
|
||||
{
|
||||
if (position < 0 || position >= getEntryCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
void PopUpCommon::entrySelected (int i)
|
||||
if (getEntryCount() == 1) { // Last of the entries.
|
||||
// Hide the arrow button.
|
||||
button->get_style_context()->remove_class("Left");
|
||||
arrowButton->hide();
|
||||
hasMenu = false;
|
||||
// Remove the button image.
|
||||
buttonImage->hide();
|
||||
selected = -1;
|
||||
}
|
||||
else if (position < selected) {
|
||||
selected--;
|
||||
}
|
||||
else if (position == selected) { // Select a different entry before removing.
|
||||
int nextSelection = position + (position == getEntryCount() - 1 ? -1 : 1);
|
||||
changeImage(nextSelection);
|
||||
setButtonHint();
|
||||
}
|
||||
|
||||
MyImageMenuItem *menuItem = dynamic_cast<MyImageMenuItem *>(menu->get_children()[position]);
|
||||
menu->remove(*menuItem);
|
||||
delete menuItem;
|
||||
imageIcons.erase(imageIcons.begin() + position);
|
||||
imageFilenames.erase(imageFilenames.begin() + position);
|
||||
images.erase(images.begin() + position);
|
||||
}
|
||||
|
||||
void PopUpCommon::changeImage(int position)
|
||||
{
|
||||
changeImage(imageFilenames.at(position), imageIcons.at(position));
|
||||
}
|
||||
|
||||
void PopUpCommon::changeImage(const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon)
|
||||
{
|
||||
if (!fileName.empty()) {
|
||||
buttonImage->changeImage(fileName);
|
||||
} else {
|
||||
buttonImage->changeImage(gIcon, static_cast<Gtk::IconSize>(Gtk::ICON_SIZE_BUTTON));
|
||||
}
|
||||
}
|
||||
|
||||
void PopUpCommon::entrySelected(Gtk::Widget* widget)
|
||||
{
|
||||
int i = 0;
|
||||
for (const auto & child : menu->get_children()) {
|
||||
if (widget == child) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
entrySelected(i);
|
||||
}
|
||||
|
||||
void PopUpCommon::entrySelected(int i)
|
||||
{
|
||||
// Emit a signal if the selected item has changed
|
||||
if (setSelected (posToIndex(i)))
|
||||
@ -130,7 +219,7 @@ bool PopUpCommon::setSelected (int entryNum)
|
||||
return false;
|
||||
} else {
|
||||
// Maybe we could do something better than loading the image file each time the selection is changed !?
|
||||
buttonImage->changeImage(imageFilenames.at(entryNum));
|
||||
changeImage(entryNum);
|
||||
selected = entryNum;
|
||||
setButtonHint();
|
||||
return true;
|
||||
@ -162,7 +251,7 @@ void PopUpCommon::setButtonHint()
|
||||
auto item = dynamic_cast<MyImageMenuItem*>(widget);
|
||||
|
||||
if (item) {
|
||||
hint += item->getLabel ()->get_text ();
|
||||
hint += escapeHtmlChars(item->getLabel()->get_text());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,19 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "glibmm/refptr.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
#include <sigc++/signal.h>
|
||||
|
||||
namespace Gio
|
||||
{
|
||||
class Icon;
|
||||
}
|
||||
|
||||
namespace Gtk
|
||||
{
|
||||
|
||||
@ -33,6 +40,7 @@ class Grid;
|
||||
class Menu;
|
||||
class Button;
|
||||
class ImageMenuItem;
|
||||
class Widget;
|
||||
|
||||
}
|
||||
|
||||
@ -53,9 +61,12 @@ public:
|
||||
explicit PopUpCommon (Gtk::Button* button, const Glib::ustring& label = "");
|
||||
virtual ~PopUpCommon ();
|
||||
bool addEntry (const Glib::ustring& fileName, const Glib::ustring& label);
|
||||
bool insertEntry(int position, const Glib::ustring& fileName, const Glib::ustring& label);
|
||||
bool insertEntry(int position, const Glib::RefPtr<const Gio::Icon>& gIcon, const Glib::ustring& label);
|
||||
int getEntryCount () const;
|
||||
bool setSelected (int entryNum);
|
||||
int getSelected () const;
|
||||
void removeEntry(int position);
|
||||
void setButtonHint();
|
||||
void show ();
|
||||
void set_tooltip_text (const Glib::ustring &text);
|
||||
@ -65,16 +76,22 @@ private:
|
||||
type_signal_changed messageChanged;
|
||||
type_signal_item_selected messageItemSelected;
|
||||
|
||||
std::vector<Glib::RefPtr<const Gio::Icon>> imageIcons;
|
||||
std::vector<Glib::ustring> imageFilenames;
|
||||
std::vector<const RTImage*> images;
|
||||
Glib::ustring buttonHint;
|
||||
RTImage* buttonImage;
|
||||
Gtk::Grid* imageContainer;
|
||||
Gtk::Menu* menu;
|
||||
std::unique_ptr<Gtk::Menu> menu;
|
||||
Gtk::Button* button;
|
||||
Gtk::Button* arrowButton;
|
||||
int selected;
|
||||
bool hasMenu;
|
||||
|
||||
void changeImage(int position);
|
||||
void changeImage(const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon);
|
||||
void entrySelected(Gtk::Widget* menuItem);
|
||||
bool insertEntryImpl(int position, const Glib::ustring& fileName, const Glib::RefPtr<const Gio::Icon>& gIcon, RTImage* image, const Glib::ustring& label);
|
||||
void showMenu(GdkEventButton* event);
|
||||
|
||||
protected:
|
||||
|
@ -17,6 +17,7 @@
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <sigc++/slot.h>
|
||||
#include "externaleditorpreferences.h"
|
||||
#include "preferences.h"
|
||||
#include "multilangmgr.h"
|
||||
#include "splash.h"
|
||||
@ -1202,68 +1203,16 @@ 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);
|
||||
Gtk::Grid* externaleditorGrid = Gtk::manage(new Gtk::Grid());
|
||||
externaleditorGrid->set_column_spacing(4);
|
||||
externaleditorGrid->set_row_spacing(4);
|
||||
setExpandAlignProperties(externaleditorGrid, false, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
|
||||
|
||||
edOther = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_EDITORCMDLINE") + ":"));
|
||||
setExpandAlignProperties(edOther, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
|
||||
editorToSendTo = Gtk::manage(new Gtk::Entry());
|
||||
setExpandAlignProperties(editorToSendTo, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_BASELINE);
|
||||
Gtk::RadioButton::Group ge = edOther->get_group();
|
||||
|
||||
#ifdef __APPLE__
|
||||
edGimp = Gtk::manage(new Gtk::RadioButton("GIMP"));
|
||||
setExpandAlignProperties(edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
|
||||
edGimp->set_group(ge);
|
||||
externaleditorGrid->attach_next_to(*edGimp, Gtk::POS_TOP, 2, 1);
|
||||
|
||||
edPS = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_PSPATH") + ":"));
|
||||
setExpandAlignProperties(edPS, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
|
||||
psDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_PSPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER));
|
||||
setExpandAlignProperties(psDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
|
||||
externaleditorGrid->attach_next_to(*edPS, *edGimp, Gtk::POS_BOTTOM, 1, 1);
|
||||
externaleditorGrid->attach_next_to(*psDir, *edPS, Gtk::POS_RIGHT, 1, 1);
|
||||
edPS->set_group(ge);
|
||||
|
||||
externaleditorGrid->attach_next_to(*edOther, *edPS, Gtk::POS_BOTTOM, 1, 1);
|
||||
externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1);
|
||||
#elif defined WIN32
|
||||
edGimp = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_GIMPPATH") + ":"));
|
||||
setExpandAlignProperties(edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
|
||||
gimpDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_GIMPPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER));
|
||||
setExpandAlignProperties(gimpDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
|
||||
externaleditorGrid->attach_next_to(*edGimp, Gtk::POS_TOP, 1, 1);
|
||||
externaleditorGrid->attach_next_to(*gimpDir, *edGimp, Gtk::POS_RIGHT, 1, 1);
|
||||
edGimp->set_group(ge);
|
||||
|
||||
edPS = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_PSPATH") + ":"));
|
||||
setExpandAlignProperties(edPS, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
|
||||
psDir = Gtk::manage(new MyFileChooserButton(M("PREFERENCES_PSPATH"), Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER));
|
||||
setExpandAlignProperties(psDir, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
|
||||
externaleditorGrid->attach_next_to(*edPS, *edGimp, Gtk::POS_BOTTOM, 1, 1);
|
||||
externaleditorGrid->attach_next_to(*psDir, *edPS, Gtk::POS_RIGHT, 1, 1);
|
||||
edPS->set_group(ge);
|
||||
|
||||
externaleditorGrid->attach_next_to(*edOther, *edPS, Gtk::POS_BOTTOM, 1, 1);
|
||||
externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1);
|
||||
#else
|
||||
edGimp = Gtk::manage(new Gtk::RadioButton("GIMP"));
|
||||
setExpandAlignProperties(edGimp, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
|
||||
externaleditorGrid->attach_next_to(*edGimp, Gtk::POS_TOP, 2, 1);
|
||||
edGimp->set_group(ge);
|
||||
|
||||
externaleditorGrid->attach_next_to(*edOther, *edGimp, Gtk::POS_BOTTOM, 1, 1);
|
||||
externaleditorGrid->attach_next_to(*editorToSendTo, *edOther, Gtk::POS_RIGHT, 1, 1);
|
||||
#endif
|
||||
|
||||
externalEditors = Gtk::manage(new ExternalEditorPreferences());
|
||||
externalEditors->set_size_request(-1, 200);
|
||||
|
||||
// fdg->add(*externaleditorGrid);
|
||||
editor_dir_temp = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_EXTEDITOR_DIR_TEMP")));
|
||||
editor_dir_current = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_EXTEDITOR_DIR_CURRENT")));
|
||||
editor_dir_custom = Gtk::manage(new Gtk::RadioButton(M("PREFERENCES_EXTEDITOR_DIR_CUSTOM") + ": "));
|
||||
editor_dir_custom_path = Gtk::manage(new MyFileChooserButton("", Gtk::FILE_CHOOSER_ACTION_SELECT_FOLDER));
|
||||
Gtk::RadioButton::Group ge;
|
||||
ge = editor_dir_temp->get_group();
|
||||
editor_dir_current->set_group(ge);
|
||||
editor_dir_custom->set_group(ge);
|
||||
@ -1272,7 +1221,7 @@ Gtk::Widget* Preferences::getGeneralPanel()
|
||||
editor_bypass_output_profile = Gtk::manage(new Gtk::CheckButton(M("PREFERENCES_EXTEDITOR_BYPASS_OUTPUT_PROFILE")));
|
||||
{
|
||||
Gtk::Frame *f = Gtk::manage(new Gtk::Frame(M("PREFERENCES_EXTEDITOR_DIR")));
|
||||
setExpandAlignProperties(f, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
|
||||
setExpandAlignProperties(f, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START);
|
||||
Gtk::Box *vb = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
|
||||
vb->pack_start(*editor_dir_temp);
|
||||
vb->pack_start(*editor_dir_current);
|
||||
@ -1283,7 +1232,7 @@ Gtk::Widget* Preferences::getGeneralPanel()
|
||||
f->add(*vb);
|
||||
|
||||
hb = Gtk::manage(new Gtk::Box());
|
||||
hb->pack_start(*externaleditorGrid);
|
||||
hb->pack_start(*externalEditors);
|
||||
hb->pack_start(*f, Gtk::PACK_EXPAND_WIDGET, 4);
|
||||
|
||||
vb = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
|
||||
@ -1755,31 +1704,17 @@ void Preferences::storePreferences()
|
||||
|
||||
moptions.pseudoHiDPISupport = pseudoHiDPI->get_active();
|
||||
|
||||
#ifdef WIN32
|
||||
moptions.gimpDir = gimpDir->get_filename();
|
||||
moptions.psDir = psDir->get_filename();
|
||||
#elif defined __APPLE__
|
||||
moptions.psDir = psDir->get_filename();
|
||||
#endif
|
||||
moptions.customEditorProg = editorToSendTo->get_text();
|
||||
|
||||
if (edGimp->get_active()) {
|
||||
moptions.editorToSendTo = 1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
else if (edPS->get_active()) {
|
||||
moptions.editorToSendTo = 2;
|
||||
}
|
||||
|
||||
#elif defined __APPLE__
|
||||
else if (edPS->get_active()) {
|
||||
moptions.editorToSendTo = 2;
|
||||
}
|
||||
|
||||
#endif
|
||||
else if (edOther->get_active()) {
|
||||
moptions.editorToSendTo = 3;
|
||||
const std::vector<ExternalEditorPreferences::EditorInfo> &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_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.
|
||||
moptions.externalEditorIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (editor_dir_temp->get_active()) {
|
||||
@ -2050,34 +1985,16 @@ void Preferences::fillPreferences()
|
||||
hlThresh->set_value(moptions.highlightThreshold);
|
||||
shThresh->set_value(moptions.shadowThreshold);
|
||||
|
||||
edGimp->set_active(moptions.editorToSendTo == 1);
|
||||
edOther->set_active(moptions.editorToSendTo == 3);
|
||||
#ifdef WIN32
|
||||
edPS->set_active(moptions.editorToSendTo == 2);
|
||||
|
||||
if (Glib::file_test(moptions.gimpDir, Glib::FILE_TEST_IS_DIR)) {
|
||||
gimpDir->set_current_folder(moptions.gimpDir);
|
||||
} else {
|
||||
gimpDir->set_current_folder(Glib::get_home_dir());
|
||||
std::vector<ExternalEditorPreferences::EditorInfo> editorInfos;
|
||||
for (const auto &editor : moptions.externalEditors) {
|
||||
editorInfos.push_back(ExternalEditorPreferences::EditorInfo(
|
||||
editor.name, editor.command, editor.icon_serialized));
|
||||
}
|
||||
|
||||
if (Glib::file_test(moptions.psDir, Glib::FILE_TEST_IS_DIR)) {
|
||||
psDir->set_current_folder(moptions.psDir);
|
||||
} else {
|
||||
psDir->set_current_folder(Glib::get_home_dir());
|
||||
if (moptions.externalEditorIndex >= 0) {
|
||||
// Mark the current editor so we can track it.
|
||||
editorInfos[moptions.externalEditorIndex].other_data = (void *)1;
|
||||
}
|
||||
|
||||
#elif defined __APPLE__
|
||||
edPS->set_active(moptions.editorToSendTo == 2);
|
||||
|
||||
if (Glib::file_test(moptions.psDir, Glib::FILE_TEST_IS_DIR)) {
|
||||
psDir->set_current_folder(moptions.psDir);
|
||||
} else {
|
||||
psDir->set_current_folder(Glib::get_home_dir());
|
||||
}
|
||||
|
||||
#endif
|
||||
editorToSendTo->set_text(moptions.customEditorProg);
|
||||
externalEditors->setEditors(editorInfos);
|
||||
|
||||
editor_dir_temp->set_active(moptions.editor_out_dir == Options::EDITOR_OUT_DIR_TEMP);
|
||||
editor_dir_current->set_active(moptions.editor_out_dir == Options::EDITOR_OUT_DIR_CURRENT);
|
||||
@ -2554,6 +2471,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()
|
||||
|
@ -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;
|
||||
|
||||
Gtk::RadioButton *editor_dir_temp;
|
||||
Gtk::RadioButton *editor_dir_current;
|
||||
|
77
rtgui/rtappchooserdialog.cc
Normal file
77
rtgui/rtappchooserdialog.cc
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2021 Lawrence Lee <billee@ucdavis.edu>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* RawTherapee is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "rtappchooserdialog.h"
|
||||
|
||||
#if !(defined WIN32 || defined __APPLE__)
|
||||
#define GTKMM_APPCHOOSERDIALOG
|
||||
#endif
|
||||
|
||||
RTAppChooserDialog::~RTAppChooserDialog() {}
|
||||
|
||||
#ifdef GTKMM_APPCHOOSERDIALOG // Use Gtk::AppChooserDialog directly.
|
||||
|
||||
RTAppChooserDialog::RTAppChooserDialog(const Glib::ustring &content_type) :
|
||||
Gtk::AppChooserDialog(content_type)
|
||||
{
|
||||
}
|
||||
|
||||
Glib::RefPtr<Gio::AppInfo> RTAppChooserDialog::get_app_info()
|
||||
{
|
||||
return Gtk::AppChooserDialog::get_app_info();
|
||||
}
|
||||
|
||||
Glib::RefPtr<const Gio::AppInfo> RTAppChooserDialog::get_app_info() const
|
||||
{
|
||||
return Gtk::AppChooserDialog::get_app_info();
|
||||
}
|
||||
|
||||
#else // Work around bugs with GLib and glibmm.
|
||||
|
||||
RTAppChooserDialog::RTAppChooserDialog(const Glib::ustring &content_type) :
|
||||
Gtk::AppChooserDialog(content_type)
|
||||
{
|
||||
// GTK calls a faulty GLib function to update the most recently selected
|
||||
// application after an application is selected. This removes all signal
|
||||
// handlers to prevent the function call.
|
||||
auto signal_id = g_signal_lookup("response", GTK_TYPE_APP_CHOOSER_DIALOG);
|
||||
while (true) {
|
||||
auto handler_id = g_signal_handler_find(gobj(), G_SIGNAL_MATCH_ID, signal_id, GQuark(), nullptr, nullptr, nullptr);
|
||||
if (!handler_id) {
|
||||
break;
|
||||
}
|
||||
g_signal_handler_disconnect(gobj(), handler_id);
|
||||
}
|
||||
}
|
||||
|
||||
Glib::RefPtr<Gio::AppInfo> RTAppChooserDialog::get_app_info()
|
||||
{
|
||||
// glibmm wrapping of GAppInfo does not work on some platforms. Manually
|
||||
// wrap it here.
|
||||
GAppInfo *gAppInfo = gtk_app_chooser_get_app_info(GTK_APP_CHOOSER(gobj()));
|
||||
return Glib::wrap(gAppInfo, true);
|
||||
}
|
||||
|
||||
Glib::RefPtr<const Gio::AppInfo> RTAppChooserDialog::get_app_info() const
|
||||
{
|
||||
GAppInfo *gAppInfo = gtk_app_chooser_get_app_info(GTK_APP_CHOOSER(
|
||||
const_cast<GtkAppChooserDialog *>(gobj())));
|
||||
return Glib::wrap(gAppInfo, true);
|
||||
}
|
||||
|
||||
#endif
|
39
rtgui/rtappchooserdialog.h
Normal file
39
rtgui/rtappchooserdialog.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of RawTherapee.
|
||||
*
|
||||
* Copyright (c) 2021 Lawrence Lee <billee@ucdavis.edu>
|
||||
*
|
||||
* RawTherapee is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* RawTherapee is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with RawTherapee. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <giomm/appinfo.h>
|
||||
#include <glibmm/refptr.h>
|
||||
#include <glibmm/ustring.h>
|
||||
#include <gtkmm/appchooserdialog.h>
|
||||
|
||||
/**
|
||||
* Custom version of gtkmm's Gtk::AppChooserDialog to work around crashes
|
||||
* (https://gitlab.gnome.org/GNOME/glib/-/issues/1104 and
|
||||
* https://gitlab.gnome.org/GNOME/glibmm/-/issues/94).
|
||||
*/
|
||||
class RTAppChooserDialog : public Gtk::AppChooserDialog
|
||||
{
|
||||
public:
|
||||
RTAppChooserDialog(const Glib::ustring &content_type);
|
||||
~RTAppChooserDialog();
|
||||
|
||||
Glib::RefPtr<Gio::AppInfo> get_app_info();
|
||||
Glib::RefPtr<const Gio::AppInfo> get_app_info() const;
|
||||
};
|
107
rtgui/rtimage.cc
107
rtgui/rtimage.cc
@ -22,12 +22,40 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <gtkmm/icontheme.h>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../rtengine/settings.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct GIconKey {
|
||||
Glib::RefPtr<const Gio::Icon> icon;
|
||||
/**
|
||||
* Icon size in pixels.
|
||||
*/
|
||||
int icon_size;
|
||||
|
||||
GIconKey() {}
|
||||
GIconKey(const Glib::RefPtr<const Gio::Icon> &icon, int icon_size): icon(icon), icon_size(icon_size) {}
|
||||
|
||||
bool operator==(const GIconKey &other) const
|
||||
{
|
||||
bool icons_match = (icon.get() == nullptr && other.icon.get() == nullptr) || (icon.get() != nullptr && icon->equal(Glib::RefPtr<Gio::Icon>::cast_const(other.icon)));
|
||||
return icons_match && icon_size == other.icon_size;
|
||||
}
|
||||
};
|
||||
|
||||
struct GIconKeyHash {
|
||||
size_t operator()(const GIconKey &key) const
|
||||
{
|
||||
const size_t icon_hash = key.icon ? key.icon->hash() : 0;
|
||||
return icon_hash ^ std::hash<int>()(key.icon_size);
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_map<GIconKey, Glib::RefPtr<Gdk::Pixbuf>, GIconKeyHash> gIconPixbufCache;
|
||||
std::map<std::string, Glib::RefPtr<Gdk::Pixbuf> > pixbufCache;
|
||||
std::map<std::string, Cairo::RefPtr<Cairo::ImageSurface> > surfaceCache;
|
||||
|
||||
@ -44,6 +72,8 @@ RTImage::RTImage (RTImage &other) : surface(other.surface), pixbuf(other.pixbuf)
|
||||
set(pixbuf);
|
||||
} else if (surface) {
|
||||
set(surface);
|
||||
} else if (other.gIcon) {
|
||||
changeImage(other.gIcon, other.gIconSize);
|
||||
}
|
||||
}
|
||||
|
||||
@ -80,13 +110,27 @@ RTImage::RTImage (Glib::RefPtr<RTImage> &other)
|
||||
if (other->get_surface()) {
|
||||
surface = other->get_surface();
|
||||
set(surface);
|
||||
} else {
|
||||
} else if (other->pixbuf) {
|
||||
pixbuf = other->get_pixbuf();
|
||||
set(pixbuf);
|
||||
} else if (other->gIcon) {
|
||||
changeImage(other->gIcon, other->gIconSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RTImage::RTImage(const Glib::RefPtr<const Gio::Icon> &gIcon, Gtk::IconSize size)
|
||||
{
|
||||
changeImage(gIcon, size);
|
||||
}
|
||||
|
||||
int RTImage::iconSizeToPixels(Gtk::IconSize size) const
|
||||
{
|
||||
int width, height;
|
||||
Gtk::IconSize::lookup(size, width, height);
|
||||
return std::round(getTweakedDPI() / baseDPI * std::max(width, height));
|
||||
}
|
||||
|
||||
void RTImage::setImage (const Glib::ustring& fileName, const Glib::ustring& rtlFileName)
|
||||
{
|
||||
Glib::ustring imageName;
|
||||
@ -113,10 +157,41 @@ void RTImage::setDPInScale (const double newDPI, const int newScale)
|
||||
}
|
||||
}
|
||||
|
||||
void RTImage::changeImage(const Glib::RefPtr<const Gio::Icon> &gIcon, int size)
|
||||
{
|
||||
clear();
|
||||
|
||||
pixbuf.reset();
|
||||
surface.clear();
|
||||
this->gIcon = gIcon;
|
||||
|
||||
if (!gIcon) {
|
||||
return;
|
||||
}
|
||||
|
||||
gIconSize = size;
|
||||
GIconKey key(gIcon, gIconSize);
|
||||
auto iterator = gIconPixbufCache.find(key);
|
||||
|
||||
if (iterator == gIconPixbufCache.end()) {
|
||||
auto icon_pixbuf = createPixbufFromGIcon(gIcon, gIconSize);
|
||||
iterator = gIconPixbufCache.emplace(key, icon_pixbuf).first;
|
||||
}
|
||||
|
||||
set(iterator->second);
|
||||
}
|
||||
|
||||
void RTImage::changeImage(const Glib::RefPtr<const Gio::Icon> &gIcon, Gtk::IconSize size)
|
||||
{
|
||||
changeImage(gIcon, iconSizeToPixels(size));
|
||||
}
|
||||
|
||||
void RTImage::changeImage (const Glib::ustring& imageName)
|
||||
{
|
||||
clear ();
|
||||
|
||||
gIcon.reset();
|
||||
|
||||
if (imageName.empty()) {
|
||||
return;
|
||||
}
|
||||
@ -150,6 +225,11 @@ int RTImage::get_width()
|
||||
if (pixbuf) {
|
||||
return pixbuf->get_width();
|
||||
}
|
||||
|
||||
if (gIcon) {
|
||||
return this->get_pixbuf()->get_width();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -161,6 +241,11 @@ int RTImage::get_height()
|
||||
if (pixbuf) {
|
||||
return pixbuf->get_height();
|
||||
}
|
||||
|
||||
if (gIcon) {
|
||||
return this->get_pixbuf()->get_height();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -178,6 +263,11 @@ void RTImage::cleanup(bool all)
|
||||
for (auto& entry : surfaceCache) {
|
||||
entry.second.clear();
|
||||
}
|
||||
|
||||
for (auto& entry : gIconPixbufCache) {
|
||||
entry.second.reset();
|
||||
}
|
||||
|
||||
RTScalable::cleanup(all);
|
||||
}
|
||||
|
||||
@ -189,6 +279,10 @@ void RTImage::updateImages()
|
||||
for (auto& entry : surfaceCache) {
|
||||
entry.second = createImgSurfFromFile(entry.first);
|
||||
}
|
||||
|
||||
for (auto& entry : gIconPixbufCache) {
|
||||
entry.second = createPixbufFromGIcon(entry.first.icon, entry.first.icon_size);
|
||||
}
|
||||
}
|
||||
|
||||
Glib::RefPtr<Gdk::Pixbuf> RTImage::createPixbufFromFile (const Glib::ustring& fileName)
|
||||
@ -197,6 +291,17 @@ Glib::RefPtr<Gdk::Pixbuf> RTImage::createPixbufFromFile (const Glib::ustring& fi
|
||||
return Gdk::Pixbuf::create(imgSurf, 0, 0, imgSurf->get_width(), imgSurf->get_height());
|
||||
}
|
||||
|
||||
Glib::RefPtr<Gdk::Pixbuf> RTImage::createPixbufFromGIcon(const Glib::RefPtr<const Gio::Icon> &icon, int size)
|
||||
{
|
||||
// TODO: Listen for theme changes and update icon, remove from cache.
|
||||
Gtk::IconInfo iconInfo = Gtk::IconTheme::get_default()->lookup_icon(icon, size, Gtk::ICON_LOOKUP_FORCE_SIZE);
|
||||
try {
|
||||
return iconInfo.load_icon();
|
||||
} catch (Glib::Exception &e) {
|
||||
return Glib::RefPtr<Gdk::Pixbuf>();
|
||||
}
|
||||
}
|
||||
|
||||
Cairo::RefPtr<Cairo::ImageSurface> RTImage::createImgSurfFromFile (const Glib::ustring& fileName)
|
||||
{
|
||||
Cairo::RefPtr<Cairo::ImageSurface> surf;
|
||||
|
@ -34,6 +34,11 @@ class RTImage final : public Gtk::Image, public RTScalable
|
||||
protected:
|
||||
Cairo::RefPtr<Cairo::ImageSurface> surface;
|
||||
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
|
||||
Glib::RefPtr<const Gio::Icon> gIcon;
|
||||
int gIconSize;
|
||||
|
||||
void changeImage(const Glib::RefPtr<const Gio::Icon> &gIcon, int size);
|
||||
int iconSizeToPixels(Gtk::IconSize size) const;
|
||||
|
||||
public:
|
||||
RTImage ();
|
||||
@ -42,9 +47,11 @@ public:
|
||||
explicit RTImage (Cairo::RefPtr<Cairo::ImageSurface> &surf);
|
||||
explicit RTImage(Cairo::RefPtr<Cairo::ImageSurface> other);
|
||||
explicit RTImage (Glib::RefPtr<RTImage> &other);
|
||||
explicit RTImage(const Glib::RefPtr<const Gio::Icon> &gIcon, Gtk::IconSize size);
|
||||
explicit RTImage (const Glib::ustring& fileName, const Glib::ustring& rtlFileName = Glib::ustring());
|
||||
|
||||
void setImage (const Glib::ustring& fileName, const Glib::ustring& rtlFileName = Glib::ustring());
|
||||
void changeImage(const Glib::RefPtr<const Gio::Icon> &gIcon, Gtk::IconSize size);
|
||||
void changeImage (const Glib::ustring& imageName);
|
||||
Cairo::RefPtr<Cairo::ImageSurface> get_surface();
|
||||
int get_width();
|
||||
@ -58,6 +65,7 @@ public:
|
||||
static void setScale (const int newScale);
|
||||
|
||||
static Glib::RefPtr<Gdk::Pixbuf> createPixbufFromFile (const Glib::ustring& fileName);
|
||||
static Glib::RefPtr<Gdk::Pixbuf> createPixbufFromGIcon(const Glib::RefPtr<const Gio::Icon> &icon, int size);
|
||||
static Cairo::RefPtr<Cairo::ImageSurface> createImgSurfFromFile (const Glib::ustring& fileName);
|
||||
|
||||
};
|
||||
|
@ -1054,6 +1054,13 @@ void RTWindow::MoveFileBrowserToEditor()
|
||||
}
|
||||
}
|
||||
|
||||
void RTWindow::updateExternalEditorWidget(int selectedIndex, const std::vector<ExternalEditor> & editors)
|
||||
{
|
||||
if (epanel) {
|
||||
epanel->updateExternalEditorWidget(selectedIndex, editors);
|
||||
}
|
||||
}
|
||||
|
||||
void RTWindow::updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC)
|
||||
{
|
||||
if (epanel) {
|
||||
|
@ -33,6 +33,7 @@
|
||||
class BatchQueueEntry;
|
||||
class BatchQueuePanel;
|
||||
class EditorPanel;
|
||||
class ExternalEditor;
|
||||
class FilePanel;
|
||||
class PLDBridge;
|
||||
class RTWindow final :
|
||||
@ -118,6 +119,7 @@ public:
|
||||
void MoveFileBrowserToEditor();
|
||||
void MoveFileBrowserToMain();
|
||||
|
||||
void updateExternalEditorWidget(int selectedIndex, const std::vector<ExternalEditor> &editors);
|
||||
void updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC);
|
||||
void updateTPVScrollbar (bool hide);
|
||||
void updateHistogramPosition (int oldPosition, int newPosition);
|
||||
|
Loading…
x
Reference in New Issue
Block a user