File browser thumbnails for raw images start with a minimally-processed images. These images are cached and image adjustments are applied on top. The black level is "baked-into" the cached image. Therefore, to reflect the black level adjustments in the thumbnail, one of two options are required: 1. Cache an image before the black level is applied and process the black level on top of this image. 2. Recreate the base image with the new black level and cache it. The first option yields better performance when the user changes the black level. However, it requires other base adjustments to be applied every time, such as the camera multipliers. The second option requires the base image to be recreated every time the black level is changed. This commit implements the second option. It minimizes code changes, and therefore possible bugs. It does add a performance penalty when the black level changes, but the black level adjustment is rarely used.
2875 lines
96 KiB
C++
2875 lines
96 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
* Copyright (c) 2010 Oliver Duis <www.oliverduis.de>
|
|
*
|
|
* 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 "editorpanel.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include "../rtengine/array2D.h"
|
|
#include "../rtengine/imagesource.h"
|
|
#include "../rtengine/iccstore.h"
|
|
#include "batchqueue.h"
|
|
#include "batchqueueentry.h"
|
|
#include "soundman.h"
|
|
#include "rtimage.h"
|
|
#include "rtwindow.h"
|
|
#include "filepanel.h"
|
|
#include "guiutils.h"
|
|
#include "popupbutton.h"
|
|
#include "options.h"
|
|
#include "navigator.h"
|
|
#include "previewwindow.h"
|
|
#include "progressconnector.h"
|
|
#include "procparamchangers.h"
|
|
#include "placesbrowser.h"
|
|
#include "pathutils.h"
|
|
#include "rtappchooserdialog.h"
|
|
#include "thumbnail.h"
|
|
#include "toolpanelcoord.h"
|
|
|
|
#ifdef WIN32
|
|
#include "windows.h"
|
|
|
|
#include "../rtengine/winutils.h"
|
|
#endif
|
|
|
|
using namespace rtengine::procparams;
|
|
|
|
using ScopeType = Options::ScopeType;
|
|
|
|
namespace
|
|
{
|
|
|
|
void setprogressStrUI(double val, const Glib::ustring str, MyProgressBar* pProgress)
|
|
{
|
|
if (!str.empty()) {
|
|
pProgress->set_text(M(str));
|
|
}
|
|
|
|
if (val >= 0.0) {
|
|
pProgress->set_fraction(val);
|
|
}
|
|
}
|
|
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
bool find_default_monitor_profile (GdkWindow *rootwin, Glib::ustring &defprof, Glib::ustring &defprofname)
|
|
{
|
|
#ifdef WIN32
|
|
HDC hDC = GetDC (nullptr);
|
|
|
|
if (hDC != nullptr) {
|
|
if (SetICMMode (hDC, ICM_ON)) {
|
|
char profileName[MAX_PATH + 1];
|
|
DWORD profileLength = MAX_PATH;
|
|
|
|
if (GetICMProfileA (hDC, &profileLength, profileName)) {
|
|
defprof = Glib::ustring (profileName);
|
|
defprofname = Glib::path_get_basename (defprof);
|
|
size_t pos = defprofname.rfind (".");
|
|
|
|
if (pos != Glib::ustring::npos) {
|
|
defprofname = defprofname.substr (0, pos);
|
|
}
|
|
|
|
defprof = Glib::ustring ("file:") + defprof;
|
|
return true;
|
|
}
|
|
|
|
// might fail if e.g. the monitor has no profile
|
|
}
|
|
|
|
ReleaseDC (NULL, hDC);
|
|
}
|
|
|
|
#else
|
|
// taken from geeqie (image.c) and adapted
|
|
// Originally licensed as GPL v2+, with the following copyright:
|
|
// * Copyright (C) 2006 John Ellis
|
|
// * Copyright (C) 2008 - 2016 The Geeqie Team
|
|
//
|
|
guchar *prof = nullptr;
|
|
gint proflen;
|
|
GdkAtom type = GDK_NONE;
|
|
gint format = 0;
|
|
|
|
if (gdk_property_get (rootwin, gdk_atom_intern ("_ICC_PROFILE", FALSE), GDK_NONE, 0, 64 * 1024 * 1024, FALSE, &type, &format, &proflen, &prof) && proflen > 0) {
|
|
cmsHPROFILE p = cmsOpenProfileFromMem (prof, proflen);
|
|
|
|
if (p) {
|
|
defprofname = "from GDK";
|
|
defprof = Glib::build_filename (Options::rtdir, "GDK_ICC_PROFILE.icc");
|
|
|
|
if (cmsSaveProfileToFile (p, defprof.c_str())) {
|
|
cmsCloseProfile (p);
|
|
|
|
if (prof) {
|
|
g_free (prof);
|
|
}
|
|
|
|
defprof = Glib::ustring ("file:") + defprof;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (prof) {
|
|
g_free (prof);
|
|
}
|
|
|
|
#endif
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
bool hasUserOnlyPermission(const Glib::ustring &dirname)
|
|
{
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
const Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dirname);
|
|
const Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("owner::user,unix::mode");
|
|
|
|
if (!file_info) {
|
|
return false;
|
|
}
|
|
|
|
const Glib::ustring owner = file_info->get_attribute_string("owner::user");
|
|
const guint32 mode = file_info->get_attribute_uint32("unix::mode");
|
|
|
|
return (mode & 0777) == 0700 && owner == Glib::get_user_name();
|
|
#elif defined(WIN32)
|
|
const Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dirname);
|
|
const Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("owner::user");
|
|
if (!file_info) {
|
|
return false;
|
|
}
|
|
|
|
// Current user must be the owner.
|
|
const Glib::ustring user_name = Glib::get_user_name();
|
|
const Glib::ustring owner = file_info->get_attribute_string("owner::user");
|
|
if (user_name != owner) {
|
|
return false;
|
|
}
|
|
|
|
// Get security descriptor and discretionary access control list.
|
|
PACL dacl = nullptr;
|
|
PSECURITY_DESCRIPTOR sec_desc_raw_ptr = nullptr;
|
|
auto win_error = GetNamedSecurityInfo(
|
|
dirname.c_str(),
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
nullptr,
|
|
nullptr,
|
|
&dacl,
|
|
nullptr,
|
|
&sec_desc_raw_ptr
|
|
);
|
|
const WinLocalPtr<PSECURITY_DESCRIPTOR> sec_desc_ptr(sec_desc_raw_ptr);
|
|
if (win_error != ERROR_SUCCESS) {
|
|
return false;
|
|
}
|
|
|
|
// Must not inherit permissions.
|
|
SECURITY_DESCRIPTOR_CONTROL sec_desc_control;
|
|
DWORD revision;
|
|
if (!(
|
|
GetSecurityDescriptorControl(sec_desc_ptr, &sec_desc_control, &revision)
|
|
&& sec_desc_control & SE_DACL_PROTECTED
|
|
)) {
|
|
return false;
|
|
}
|
|
|
|
// Check that there is one entry allowing full access.
|
|
ULONG acl_entry_count;
|
|
PEXPLICIT_ACCESS acl_entry_list_raw = nullptr;
|
|
win_error = GetExplicitEntriesFromAcl(dacl, &acl_entry_count, &acl_entry_list_raw);
|
|
const WinLocalPtr<PEXPLICIT_ACCESS> acl_entry_list(acl_entry_list_raw);
|
|
if (win_error != ERROR_SUCCESS || acl_entry_count != 1) {
|
|
return false;
|
|
}
|
|
const EXPLICIT_ACCESS &ace = acl_entry_list[0];
|
|
if (
|
|
ace.grfAccessMode != GRANT_ACCESS
|
|
|| (ace.grfAccessPermissions & FILE_ALL_ACCESS) != FILE_ALL_ACCESS
|
|
|| ace.Trustee.TrusteeForm != TRUSTEE_IS_SID // Should already be SID, but double check.
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
// ACE must be for the current user.
|
|
HANDLE process_token_raw;
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &process_token_raw)) {
|
|
return false;
|
|
}
|
|
const WinHandle process_token(process_token_raw);
|
|
DWORD actual_token_info_size = 0;
|
|
GetTokenInformation(process_token, TokenUser, nullptr, 0, &actual_token_info_size);
|
|
if (!actual_token_info_size) {
|
|
return false;
|
|
}
|
|
const WinHeapPtr<PTOKEN_USER> user_token_ptr(actual_token_info_size);
|
|
if (!user_token_ptr || !GetTokenInformation(
|
|
process_token,
|
|
TokenUser,
|
|
user_token_ptr,
|
|
actual_token_info_size,
|
|
&actual_token_info_size
|
|
)) {
|
|
return false;
|
|
}
|
|
return EqualSid(ace.Trustee.ptstrName, user_token_ptr->User.Sid);
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Sets read and write permissions, and optionally the execute permission, for
|
|
* the user and no permissions for others.
|
|
*/
|
|
void setUserOnlyPermission(const Glib::RefPtr<Gio::File> file, bool execute)
|
|
{
|
|
#if defined(__linux__) || defined(__APPLE__)
|
|
const Glib::RefPtr<Gio::FileInfo> file_info = file->query_info("unix::mode");
|
|
if (!file_info) {
|
|
return;
|
|
}
|
|
|
|
guint32 mode = file_info->get_attribute_uint32("unix::mode");
|
|
mode = (mode & ~0777) | (execute ? 0700 : 0600);
|
|
try {
|
|
file->set_attribute_uint32("unix::mode", mode, Gio::FILE_QUERY_INFO_NONE);
|
|
} catch (Gio::Error &) {
|
|
}
|
|
#elif defined(WIN32)
|
|
// Get the current user's SID.
|
|
HANDLE process_token_raw;
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &process_token_raw)) {
|
|
return;
|
|
}
|
|
const WinHandle process_token(process_token_raw);
|
|
DWORD actual_token_info_size = 0;
|
|
GetTokenInformation(process_token, TokenUser, nullptr, 0, &actual_token_info_size);
|
|
if (!actual_token_info_size) {
|
|
return;
|
|
}
|
|
const WinHeapPtr<PTOKEN_USER> user_token_ptr(actual_token_info_size);
|
|
if (!user_token_ptr || !GetTokenInformation(
|
|
process_token,
|
|
TokenUser,
|
|
user_token_ptr,
|
|
actual_token_info_size,
|
|
&actual_token_info_size
|
|
)) {
|
|
return;
|
|
}
|
|
const PSID user_sid = user_token_ptr->User.Sid;
|
|
|
|
// Get a handle to the file.
|
|
const Glib::ustring filename = file->get_path();
|
|
const HANDLE file_handle_raw = CreateFile(
|
|
filename.c_str(),
|
|
READ_CONTROL | WRITE_DAC,
|
|
0,
|
|
nullptr,
|
|
OPEN_EXISTING,
|
|
execute ? FILE_FLAG_BACKUP_SEMANTICS : FILE_ATTRIBUTE_NORMAL,
|
|
nullptr
|
|
);
|
|
if (file_handle_raw == INVALID_HANDLE_VALUE) {
|
|
return;
|
|
}
|
|
const WinHandle file_handle(file_handle_raw);
|
|
|
|
// Create the user-only permission and set it.
|
|
EXPLICIT_ACCESS ea = {
|
|
.grfAccessPermissions = FILE_ALL_ACCESS,
|
|
.grfAccessMode = GRANT_ACCESS,
|
|
.grfInheritance = NO_INHERITANCE,
|
|
};
|
|
BuildTrusteeWithSid(&(ea.Trustee), user_sid);
|
|
PACL new_dacl_raw = nullptr;
|
|
auto win_error = SetEntriesInAcl(1, &ea, nullptr, &new_dacl_raw);
|
|
if (win_error != ERROR_SUCCESS) {
|
|
return;
|
|
}
|
|
const WinLocalPtr<PACL> new_dacl(new_dacl_raw);
|
|
SetSecurityInfo(
|
|
file_handle,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
|
|
nullptr,
|
|
nullptr,
|
|
new_dacl,
|
|
nullptr
|
|
);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Gets the path to the temp directory, creating it if necessary.
|
|
*/
|
|
Glib::ustring getTmpDirectory()
|
|
{
|
|
#if defined(__linux__) || defined(__APPLE__) || defined(WIN32)
|
|
static Glib::ustring recent_dir = "";
|
|
const Glib::ustring tmp_dir_root = Glib::get_tmp_dir();
|
|
const Glib::ustring subdir_base =
|
|
Glib::ustring::compose("rawtherapee-%1", Glib::get_user_name());
|
|
Glib::ustring dir = Glib::build_filename(tmp_dir_root, subdir_base);
|
|
|
|
// Returns true if the directory doesn't exist or has the right permissions.
|
|
auto is_usable_dir = [](const Glib::ustring &dir_path) {
|
|
return !Glib::file_test(dir_path, Glib::FILE_TEST_EXISTS) || (Glib::file_test(dir_path, Glib::FILE_TEST_IS_DIR) && hasUserOnlyPermission(dir_path));
|
|
};
|
|
|
|
if (!(is_usable_dir(dir) || recent_dir.empty())) {
|
|
// Try to reuse the random suffix directory.
|
|
dir = recent_dir;
|
|
}
|
|
|
|
if (!is_usable_dir(dir)) {
|
|
// Create new directory with random suffix.
|
|
gchar *const rand_dir = g_dir_make_tmp((subdir_base + "-XXXXXX").c_str(), nullptr);
|
|
if (!rand_dir) {
|
|
return tmp_dir_root;
|
|
}
|
|
dir = recent_dir = rand_dir;
|
|
g_free(rand_dir);
|
|
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dir);
|
|
setUserOnlyPermission(file, true);
|
|
} else if (!Glib::file_test(dir, Glib::FILE_TEST_EXISTS)) {
|
|
// Create the directory.
|
|
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path(dir);
|
|
bool dir_created = file->make_directory();
|
|
if (!dir_created) {
|
|
return tmp_dir_root;
|
|
}
|
|
setUserOnlyPermission(file, true);
|
|
}
|
|
|
|
return dir;
|
|
#else
|
|
return Glib::get_tmp_dir();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
class EditorPanel::ColorManagementToolbar
|
|
{
|
|
private:
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
MyComboBoxText profileBox;
|
|
#endif
|
|
PopUpButton intentBox;
|
|
Gtk::ToggleButton softProof;
|
|
Gtk::ToggleButton spGamutCheck;
|
|
sigc::connection profileConn, intentConn, softproofConn;
|
|
Glib::ustring defprof;
|
|
|
|
rtengine::StagedImageProcessor* const& processor;
|
|
|
|
private:
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
void prepareProfileBox ()
|
|
{
|
|
profileBox.setPreferredWidth (70, 200);
|
|
setExpandAlignProperties (&profileBox, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
profileBox.append (M ("PREFERENCES_PROFILE_NONE"));
|
|
Glib::ustring defprofname;
|
|
|
|
if (find_default_monitor_profile (profileBox.get_root_window()->gobj(), defprof, defprofname)) {
|
|
profileBox.append (M ("MONITOR_PROFILE_SYSTEM") + " (" + defprofname + ")");
|
|
|
|
if (options.rtSettings.autoMonitorProfile) {
|
|
rtengine::ICCStore::getInstance()->setDefaultMonitorProfileName (defprof);
|
|
profileBox.set_active (1);
|
|
} else {
|
|
profileBox.set_active (0);
|
|
}
|
|
} else {
|
|
profileBox.set_active (0);
|
|
}
|
|
|
|
const std::vector<Glib::ustring> profiles = rtengine::ICCStore::getInstance()->getProfiles (rtengine::ICCStore::ProfileType::MONITOR);
|
|
|
|
for (const auto& profile : profiles) {
|
|
profileBox.append (profile);
|
|
}
|
|
|
|
profileBox.set_tooltip_text (profileBox.get_active_text ());
|
|
}
|
|
#endif
|
|
|
|
void prepareIntentBox ()
|
|
{
|
|
// same order as the enum
|
|
intentBox.addEntry ("intent-perceptual.png", M ("PREFERENCES_INTENT_PERCEPTUAL"));
|
|
intentBox.addEntry ("intent-relative.png", M ("PREFERENCES_INTENT_RELATIVE"));
|
|
intentBox.addEntry ("intent-absolute.png", M ("PREFERENCES_INTENT_ABSOLUTE"));
|
|
setExpandAlignProperties (intentBox.buttonGroup, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
intentBox.setSelected (1);
|
|
intentBox.show ();
|
|
}
|
|
|
|
void prepareSoftProofingBox ()
|
|
{
|
|
Gtk::Image *softProofImage = Gtk::manage (new RTImage ("gamut-softproof.png"));
|
|
softProofImage->set_padding (0, 0);
|
|
softProof.add (*softProofImage);
|
|
softProof.set_relief (Gtk::RELIEF_NONE);
|
|
softProof.set_tooltip_markup (M ("SOFTPROOF_TOOLTIP"));
|
|
|
|
softProof.set_active (false);
|
|
softProof.show ();
|
|
|
|
Gtk::Image *spGamutCheckImage = Gtk::manage (new RTImage ("gamut-warning.png"));
|
|
spGamutCheckImage->set_padding (0, 0);
|
|
spGamutCheck.add (*spGamutCheckImage);
|
|
spGamutCheck.set_relief (Gtk::RELIEF_NONE);
|
|
spGamutCheck.set_tooltip_markup (M ("SOFTPROOF_GAMUTCHECK_TOOLTIP"));
|
|
|
|
spGamutCheck.set_active (false);
|
|
spGamutCheck.set_sensitive (true);
|
|
spGamutCheck.show ();
|
|
}
|
|
|
|
#if !defined(__APPLE__)
|
|
void profileBoxChanged ()
|
|
{
|
|
updateParameters ();
|
|
}
|
|
#endif
|
|
|
|
void intentBoxChanged (int)
|
|
{
|
|
updateParameters ();
|
|
}
|
|
|
|
void softProofToggled ()
|
|
{
|
|
updateSoftProofParameters ();
|
|
}
|
|
|
|
void spGamutCheckToggled ()
|
|
{
|
|
updateSoftProofParameters ();
|
|
}
|
|
|
|
void updateParameters (bool noEvent = false)
|
|
{
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
ConnectionBlocker profileBlocker (profileConn);
|
|
#endif
|
|
ConnectionBlocker intentBlocker (intentConn);
|
|
|
|
Glib::ustring profile;
|
|
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
|
|
if (!defprof.empty() && profileBox.get_active_row_number () == 1) {
|
|
profile = defprof;
|
|
|
|
if (profile.empty ()) {
|
|
profile = options.rtSettings.monitorProfile;
|
|
}
|
|
|
|
if (profile.empty ()) {
|
|
profile = "sRGB IEC61966-2.1";
|
|
}
|
|
} else if (profileBox.get_active_row_number () > 0) {
|
|
profile = profileBox.get_active_text ();
|
|
}
|
|
|
|
#else
|
|
profile = options.rtSettings.srgb;
|
|
#endif
|
|
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
|
|
if (profileBox.get_active_row_number () == 0) {
|
|
|
|
profile.clear ();
|
|
|
|
intentBox.set_sensitive (false);
|
|
intentBox.setSelected (1);
|
|
softProof.set_sensitive (false);
|
|
spGamutCheck.set_sensitive (false);
|
|
|
|
profileBox.set_tooltip_text ("");
|
|
|
|
} else {
|
|
const uint8_t supportedIntents = rtengine::ICCStore::getInstance()->getProofIntents (profile);
|
|
const bool supportsRelativeColorimetric = supportedIntents & 1 << INTENT_RELATIVE_COLORIMETRIC;
|
|
const bool supportsPerceptual = supportedIntents & 1 << INTENT_PERCEPTUAL;
|
|
const bool supportsAbsoluteColorimetric = supportedIntents & 1 << INTENT_ABSOLUTE_COLORIMETRIC;
|
|
|
|
if (supportsPerceptual || supportsRelativeColorimetric || supportsAbsoluteColorimetric) {
|
|
intentBox.set_sensitive (true);
|
|
intentBox.setItemSensitivity (0, supportsPerceptual);
|
|
intentBox.setItemSensitivity (1, supportsRelativeColorimetric);
|
|
intentBox.setItemSensitivity (2, supportsAbsoluteColorimetric);
|
|
softProof.set_sensitive (true);
|
|
spGamutCheck.set_sensitive (true);
|
|
} else {
|
|
intentBox.setItemSensitivity (0, true);
|
|
intentBox.setItemSensitivity (1, true);
|
|
intentBox.setItemSensitivity (2, true);
|
|
intentBox.set_sensitive (false);
|
|
intentBox.setSelected (1);
|
|
softProof.set_sensitive (false);
|
|
spGamutCheck.set_sensitive (true);
|
|
}
|
|
|
|
profileBox.set_tooltip_text (profileBox.get_active_text ());
|
|
}
|
|
|
|
#endif
|
|
rtengine::RenderingIntent intent;
|
|
|
|
switch (intentBox.getSelected ()) {
|
|
default:
|
|
case 0:
|
|
intent = rtengine::RI_PERCEPTUAL;
|
|
break;
|
|
|
|
case 1:
|
|
intent = rtengine::RI_RELATIVE;
|
|
break;
|
|
|
|
case 2:
|
|
intent = rtengine::RI_ABSOLUTE;
|
|
break;
|
|
}
|
|
|
|
if (!processor) {
|
|
return;
|
|
}
|
|
|
|
if (!noEvent) {
|
|
processor->beginUpdateParams ();
|
|
}
|
|
|
|
processor->setMonitorProfile (profile, intent);
|
|
processor->setSoftProofing (softProof.get_sensitive() && softProof.get_active(), spGamutCheck.get_sensitive() && spGamutCheck.get_active());
|
|
|
|
if (!noEvent) {
|
|
processor->endUpdateParams (rtengine::EvMonitorTransform);
|
|
}
|
|
}
|
|
|
|
void updateSoftProofParameters (bool noEvent = false)
|
|
{
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
softProof.set_sensitive (profileBox.get_active_row_number () > 0);
|
|
spGamutCheck.set_sensitive(profileBox.get_active_row_number () > 0);
|
|
#endif
|
|
|
|
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
|
|
if (profileBox.get_active_row_number () > 0) {
|
|
#endif
|
|
|
|
if (processor) {
|
|
if (!noEvent) {
|
|
processor->beginUpdateParams ();
|
|
}
|
|
|
|
processor->setSoftProofing (softProof.get_sensitive() && softProof.get_active(), spGamutCheck.get_active());
|
|
|
|
if (!noEvent) {
|
|
processor->endUpdateParams (rtengine::EvMonitorTransform);
|
|
}
|
|
}
|
|
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
}
|
|
|
|
#endif
|
|
}
|
|
|
|
public:
|
|
explicit ColorManagementToolbar (rtengine::StagedImageProcessor* const& ipc) :
|
|
intentBox (Glib::ustring (), true),
|
|
processor (ipc)
|
|
{
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
prepareProfileBox ();
|
|
#endif
|
|
prepareIntentBox ();
|
|
prepareSoftProofingBox ();
|
|
|
|
reset ();
|
|
|
|
softproofConn = softProof.signal_toggled().connect (sigc::mem_fun (this, &ColorManagementToolbar::softProofToggled));
|
|
spGamutCheck.signal_toggled().connect (sigc::mem_fun (this, &ColorManagementToolbar::spGamutCheckToggled));
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
profileConn = profileBox.signal_changed ().connect (sigc::mem_fun (this, &ColorManagementToolbar::profileBoxChanged));
|
|
#endif
|
|
intentConn = intentBox.signal_changed ().connect (sigc::mem_fun (this, &ColorManagementToolbar::intentBoxChanged));
|
|
}
|
|
|
|
void pack_right_in (Gtk::Grid* grid)
|
|
{
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
grid->attach_next_to (profileBox, Gtk::POS_RIGHT, 1, 1);
|
|
#endif
|
|
grid->attach_next_to (*intentBox.buttonGroup, Gtk::POS_RIGHT, 1, 1);
|
|
grid->attach_next_to (softProof, Gtk::POS_RIGHT, 1, 1);
|
|
grid->attach_next_to (spGamutCheck, Gtk::POS_RIGHT, 1, 1);
|
|
}
|
|
|
|
void updateProcessor()
|
|
{
|
|
if (processor) {
|
|
updateParameters (true);
|
|
}
|
|
}
|
|
|
|
void reset ()
|
|
{
|
|
ConnectionBlocker intentBlocker (intentConn);
|
|
#if !defined(__APPLE__) // monitor profile not supported on apple
|
|
ConnectionBlocker profileBlocker (profileConn);
|
|
|
|
if (!defprof.empty() && options.rtSettings.autoMonitorProfile) {
|
|
profileBox.set_active (1);
|
|
} else {
|
|
setActiveTextOrIndex (profileBox, options.rtSettings.monitorProfile, 0);
|
|
}
|
|
|
|
#endif
|
|
|
|
switch (options.rtSettings.monitorIntent) {
|
|
default:
|
|
case rtengine::RI_PERCEPTUAL:
|
|
intentBox.setSelected (0);
|
|
break;
|
|
|
|
case rtengine::RI_RELATIVE:
|
|
intentBox.setSelected (1);
|
|
break;
|
|
|
|
case rtengine::RI_ABSOLUTE:
|
|
intentBox.setSelected (2);
|
|
break;
|
|
}
|
|
|
|
updateParameters ();
|
|
}
|
|
|
|
void updateHistogram()
|
|
{
|
|
updateParameters();
|
|
}
|
|
|
|
|
|
void defaultMonitorProfileChanged (const Glib::ustring &profile_name, bool auto_monitor_profile)
|
|
{
|
|
ConnectionBlocker profileBlocker (profileConn);
|
|
|
|
if (auto_monitor_profile && !defprof.empty()) {
|
|
rtengine::ICCStore::getInstance()->setDefaultMonitorProfileName (defprof);
|
|
#ifndef __APPLE__
|
|
profileBox.set_active (1);
|
|
#endif
|
|
} else {
|
|
rtengine::ICCStore::getInstance()->setDefaultMonitorProfileName (profile_name);
|
|
#ifndef __APPLE__
|
|
setActiveTextOrIndex (profileBox, profile_name, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
};
|
|
|
|
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),
|
|
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),
|
|
histogram_observable(nullptr), histogram_scope_type(ScopeType::NONE)
|
|
{
|
|
|
|
set_orientation(Gtk::ORIENTATION_VERTICAL);
|
|
epih = new EditorPanelIdleHelper;
|
|
epih->epanel = this;
|
|
epih->destroyed = false;
|
|
epih->pending = 0;
|
|
//rtengine::befaf=true;
|
|
processingStartedTime = 0;
|
|
firstProcessingDone = false;
|
|
|
|
// construct toolpanelcoordinator
|
|
tpc = new ToolPanelCoordinator ();
|
|
|
|
// build GUI
|
|
|
|
// build left side panel
|
|
leftbox = new Gtk::Paned (Gtk::ORIENTATION_VERTICAL);
|
|
|
|
// make a subpaned to allow resizing of the histogram (if it's on the left)
|
|
leftsubpaned = new Gtk::Paned(Gtk::ORIENTATION_VERTICAL);
|
|
leftsubpaned->set_size_request(230, 250);
|
|
|
|
histogramPanel = nullptr;
|
|
|
|
profilep = Gtk::manage(new ProfilePanel());
|
|
ppframe = Gtk::manage(new Gtk::Frame());
|
|
ppframe->set_label_align(0.025, 0.5);
|
|
ppframe->set_name ("ProfilePanel");
|
|
ppframe->add (*profilep);
|
|
ppframe->set_label(M("PROFILEPANEL_LABEL"));
|
|
//leftsubpaned->pack_start (*ppframe, Gtk::PACK_SHRINK, 4);
|
|
|
|
navigator = Gtk::manage(new Navigator());
|
|
navigator->previewWindow->set_size_request(-1, 150 * RTScalable::getScale());
|
|
leftsubpaned->pack1(*navigator, false, false);
|
|
|
|
history = Gtk::manage(new History());
|
|
leftsubpaned->pack2(*history, true, false);
|
|
|
|
leftsubpaned->set_position(0);
|
|
leftsubpaned->show_all();
|
|
|
|
leftbox->pack2(*leftsubpaned, true, true);
|
|
leftbox->show_all();
|
|
|
|
// build the middle of the screen
|
|
Gtk::Box* editbox = Gtk::manage (new Gtk::Box (Gtk::ORIENTATION_VERTICAL));
|
|
|
|
info = Gtk::manage (new Gtk::ToggleButton ());
|
|
Gtk::Image* infoimg = Gtk::manage (new RTImage ("info.png"));
|
|
info->add (*infoimg);
|
|
info->set_relief (Gtk::RELIEF_NONE);
|
|
info->set_tooltip_markup (M ("MAIN_TOOLTIP_QINFO"));
|
|
|
|
beforeAfter = Gtk::manage (new Gtk::ToggleButton ());
|
|
Gtk::Image* beforeAfterIcon = Gtk::manage (new RTImage ("beforeafter.png"));
|
|
beforeAfter->add (*beforeAfterIcon);
|
|
beforeAfter->set_relief (Gtk::RELIEF_NONE);
|
|
beforeAfter->set_tooltip_markup (M ("MAIN_TOOLTIP_TOGGLE"));
|
|
|
|
iBeforeLockON = new RTImage ("padlock-locked-small.png");
|
|
iBeforeLockOFF = new RTImage ("padlock-unlocked-small.png");
|
|
|
|
Gtk::Separator* vsept = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vsepz = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vsepi = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vseph = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
|
|
hidehp = Gtk::manage (new Gtk::ToggleButton ());
|
|
|
|
iHistoryShow = new RTImage ("panel-to-right.png");
|
|
iHistoryHide = new RTImage ("panel-to-left.png");
|
|
|
|
hidehp->set_relief (Gtk::RELIEF_NONE);
|
|
hidehp->set_active (options.showHistory);
|
|
hidehp->set_tooltip_markup (M ("MAIN_TOOLTIP_HIDEHP"));
|
|
|
|
if (options.showHistory) {
|
|
hidehp->set_image (*iHistoryHide);
|
|
} else {
|
|
hidehp->set_image (*iHistoryShow);
|
|
}
|
|
|
|
tbTopPanel_1 = nullptr;
|
|
|
|
if (!simpleEditor && filePanel) {
|
|
tbTopPanel_1 = new Gtk::ToggleButton ();
|
|
iTopPanel_1_Show = new RTImage ("panel-to-bottom.png");
|
|
iTopPanel_1_Hide = new RTImage ("panel-to-top.png");
|
|
tbTopPanel_1->set_relief (Gtk::RELIEF_NONE);
|
|
tbTopPanel_1->set_active (true);
|
|
tbTopPanel_1->set_tooltip_markup (M ("MAIN_TOOLTIP_SHOWHIDETP1"));
|
|
tbTopPanel_1->set_image (*iTopPanel_1_Hide);
|
|
}
|
|
|
|
Gtk::Separator* vsepcl = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vsepz2 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vsepz3 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vsepz4 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
|
|
Gtk::Separator* vsep1 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
Gtk::Separator* vsep2 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
|
|
// Histogram profile toggle controls
|
|
toggleHistogramProfile = Gtk::manage (new Gtk::ToggleButton ());
|
|
Gtk::Image* histProfImg = Gtk::manage (new RTImage ("gamut-hist.png"));
|
|
toggleHistogramProfile->add (*histProfImg);
|
|
toggleHistogramProfile->set_relief (Gtk::RELIEF_NONE);
|
|
toggleHistogramProfile->set_active (options.rtSettings.HistogramWorking);
|
|
toggleHistogramProfile->set_tooltip_markup ( (M ("PREFERENCES_HISTOGRAM_TOOLTIP")));
|
|
|
|
Gtk::Separator* vsep3 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
|
|
iareapanel = new ImageAreaPanel ();
|
|
tpc->setEditProvider (iareapanel->imageArea);
|
|
tpc->getToolBar()->setLockablePickerToolListener (iareapanel->imageArea);
|
|
|
|
Gtk::Box* toolBarPanel = Gtk::manage (new Gtk::Box (Gtk::ORIENTATION_HORIZONTAL));
|
|
toolBarPanel->set_name ("EditorTopPanel");
|
|
toolBarPanel->pack_start (*hidehp, Gtk::PACK_SHRINK, 1);
|
|
toolBarPanel->pack_start (*vseph, Gtk::PACK_SHRINK, 2);
|
|
toolBarPanel->pack_start (*info, Gtk::PACK_SHRINK, 1);
|
|
toolBarPanel->pack_start (*beforeAfter, Gtk::PACK_SHRINK, 1);
|
|
toolBarPanel->pack_start (*vsepi, Gtk::PACK_SHRINK, 2);
|
|
toolBarPanel->pack_start (*tpc->getToolBar(), Gtk::PACK_SHRINK, 1);
|
|
toolBarPanel->pack_start (*vsept, Gtk::PACK_SHRINK, 2);
|
|
|
|
if (tbTopPanel_1) {
|
|
Gtk::Separator* vsep = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
toolBarPanel->pack_end (*tbTopPanel_1, Gtk::PACK_SHRINK, 1);
|
|
toolBarPanel->pack_end (*vsep, Gtk::PACK_SHRINK, 2);
|
|
}
|
|
|
|
toolBarPanel->pack_end (*tpc->coarse, Gtk::PACK_SHRINK, 2);
|
|
toolBarPanel->pack_end (*vsepcl, Gtk::PACK_SHRINK, 2);
|
|
// Histogram profile toggle
|
|
toolBarPanel->pack_end (*toggleHistogramProfile, Gtk::PACK_SHRINK, 1);
|
|
toolBarPanel->pack_end (*vsep3, Gtk::PACK_SHRINK, 2);
|
|
|
|
toolBarPanel->pack_end (*iareapanel->imageArea->indClippedPanel, Gtk::PACK_SHRINK, 0);
|
|
toolBarPanel->pack_end (*vsepz, Gtk::PACK_SHRINK, 2);
|
|
toolBarPanel->pack_end (*iareapanel->imageArea->previewModePanel, Gtk::PACK_SHRINK, 0);
|
|
toolBarPanel->pack_end (*vsepz4, Gtk::PACK_SHRINK, 2);
|
|
|
|
afterBox = Gtk::manage (new Gtk::Box (Gtk::ORIENTATION_VERTICAL));
|
|
afterBox->pack_start (*iareapanel);
|
|
|
|
beforeAfterBox = Gtk::manage (new Gtk::Box (Gtk::ORIENTATION_HORIZONTAL));
|
|
beforeAfterBox->set_name ("BeforeAfterContainer");
|
|
beforeAfterBox->pack_start (*afterBox);
|
|
|
|
MyScrolledToolbar *stb1 = Gtk::manage(new MyScrolledToolbar());
|
|
stb1->set_name("EditorToolbarTop");
|
|
stb1->add(*toolBarPanel);
|
|
editbox->pack_start (*stb1, Gtk::PACK_SHRINK, 2);
|
|
editbox->pack_start (*beforeAfterBox);
|
|
|
|
// build right side panel
|
|
vboxright = new Gtk::Paned (Gtk::ORIENTATION_VERTICAL);
|
|
|
|
vsubboxright = new Gtk::Box (Gtk::ORIENTATION_VERTICAL, 0);
|
|
// int rightsize = options.fontSize * 44;
|
|
// vsubboxright->set_size_request (rightsize, rightsize - 50);
|
|
vsubboxright->set_size_request (300, 250);
|
|
|
|
vsubboxright->pack_start (*ppframe, Gtk::PACK_SHRINK, 2);
|
|
// main notebook
|
|
vsubboxright->pack_start (*tpc->toolPanelNotebook);
|
|
|
|
vboxright->pack2 (*vsubboxright, true, true);
|
|
|
|
// Save buttons
|
|
Gtk::Grid *iops = Gtk::manage(new Gtk::Grid());
|
|
iops->set_name ("IopsPanel");
|
|
iops->set_orientation (Gtk::ORIENTATION_HORIZONTAL);
|
|
iops->set_row_spacing (2);
|
|
iops->set_column_spacing (2);
|
|
|
|
Gtk::Image *saveButtonImage = Gtk::manage (new RTImage ("save.png"));
|
|
saveimgas = Gtk::manage (new Gtk::Button ());
|
|
saveimgas->set_relief(Gtk::RELIEF_NONE);
|
|
saveimgas->add (*saveButtonImage);
|
|
saveimgas->set_tooltip_markup (M ("MAIN_BUTTON_SAVE_TOOLTIP"));
|
|
setExpandAlignProperties (saveimgas, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
Gtk::Image *queueButtonImage = Gtk::manage (new RTImage ("gears.png"));
|
|
queueimg = Gtk::manage (new Gtk::Button ());
|
|
queueimg->set_relief(Gtk::RELIEF_NONE);
|
|
queueimg->add (*queueButtonImage);
|
|
queueimg->set_tooltip_markup (M ("MAIN_BUTTON_PUTTOQUEUE_TOOLTIP"));
|
|
setExpandAlignProperties (queueimg, 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);
|
|
updateExternalEditorWidget(
|
|
options.externalEditorIndex >= 0 ? options.externalEditorIndex : options.externalEditors.size(),
|
|
options.externalEditors
|
|
);
|
|
send_to_external->show();
|
|
|
|
// Status box
|
|
progressLabel = Gtk::manage (new MyProgressBar (300));
|
|
progressLabel->set_show_text (true);
|
|
setExpandAlignProperties (progressLabel, true, false, Gtk::ALIGN_START, Gtk::ALIGN_FILL);
|
|
progressLabel->set_fraction (0.0);
|
|
|
|
// tbRightPanel_1
|
|
tbRightPanel_1 = Gtk::manage(new Gtk::ToggleButton());
|
|
iRightPanel_1_Show = new RTImage ("panel-to-left.png");
|
|
iRightPanel_1_Hide = new RTImage ("panel-to-right.png");
|
|
tbRightPanel_1->set_relief (Gtk::RELIEF_NONE);
|
|
tbRightPanel_1->set_active (true);
|
|
tbRightPanel_1->set_tooltip_markup (M ("MAIN_TOOLTIP_SHOWHIDERP1"));
|
|
tbRightPanel_1->set_image (*iRightPanel_1_Hide);
|
|
setExpandAlignProperties (tbRightPanel_1, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
// ShowHideSidePanels
|
|
tbShowHideSidePanels = Gtk::manage(new Gtk::ToggleButton());
|
|
iShowHideSidePanels = new RTImage ("crossed-arrows-out.png");
|
|
iShowHideSidePanels_exit = new RTImage ("crossed-arrows-in.png");
|
|
tbShowHideSidePanels->set_relief (Gtk::RELIEF_NONE);
|
|
tbShowHideSidePanels->set_active (false);
|
|
tbShowHideSidePanels->set_tooltip_markup (M ("MAIN_BUTTON_SHOWHIDESIDEPANELS_TOOLTIP"));
|
|
tbShowHideSidePanels->set_image (*iShowHideSidePanels);
|
|
setExpandAlignProperties (tbShowHideSidePanels, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
navPrev = navNext = navSync = nullptr;
|
|
|
|
if (!simpleEditor && !options.tabbedUI) {
|
|
// Navigation buttons
|
|
Gtk::Image *navPrevImage = Gtk::manage (new RTImage ("arrow2-left.png"));
|
|
navPrevImage->set_padding (0, 0);
|
|
navPrev = Gtk::manage (new Gtk::Button ());
|
|
navPrev->add (*navPrevImage);
|
|
navPrev->set_relief (Gtk::RELIEF_NONE);
|
|
navPrev->set_tooltip_markup (M ("MAIN_BUTTON_NAVPREV_TOOLTIP"));
|
|
setExpandAlignProperties (navPrev, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
Gtk::Image *navNextImage = Gtk::manage (new RTImage ("arrow2-right.png"));
|
|
navNextImage->set_padding (0, 0);
|
|
navNext = Gtk::manage (new Gtk::Button ());
|
|
navNext->add (*navNextImage);
|
|
navNext->set_relief (Gtk::RELIEF_NONE);
|
|
navNext->set_tooltip_markup (M ("MAIN_BUTTON_NAVNEXT_TOOLTIP"));
|
|
setExpandAlignProperties (navNext, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
|
|
Gtk::Image *navSyncImage = Gtk::manage (new RTImage ("arrow-updown.png"));
|
|
navSyncImage->set_padding (0, 0);
|
|
navSync = Gtk::manage (new Gtk::Button ());
|
|
navSync->add (*navSyncImage);
|
|
navSync->set_relief (Gtk::RELIEF_NONE);
|
|
navSync->set_tooltip_markup (M ("MAIN_BUTTON_NAVSYNC_TOOLTIP"));
|
|
setExpandAlignProperties (navSync, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_FILL);
|
|
}
|
|
|
|
// ================== PACKING THE BOTTOM WIDGETS =================
|
|
|
|
// Adding widgets from center to the left, on the left side (using Gtk::POS_LEFT)
|
|
iops->attach_next_to (*vsep2, Gtk::POS_LEFT, 1, 1);
|
|
iops->attach_next_to (*progressLabel, Gtk::POS_LEFT, 1, 1);
|
|
iops->attach_next_to (*vsep1, Gtk::POS_LEFT, 1, 1);
|
|
|
|
if (!gimpPlugin) {
|
|
iops->attach_next_to(*send_to_external->buttonGroup, Gtk::POS_LEFT, 1, 1);
|
|
}
|
|
|
|
if (!gimpPlugin && !simpleEditor) {
|
|
iops->attach_next_to (*queueimg, Gtk::POS_LEFT, 1, 1);
|
|
}
|
|
|
|
if (!gimpPlugin) {
|
|
iops->attach_next_to (*saveimgas, Gtk::POS_LEFT, 1, 1);
|
|
}
|
|
|
|
|
|
// Color management toolbar
|
|
colorMgmtToolBar.reset (new ColorManagementToolbar (ipc));
|
|
colorMgmtToolBar->pack_right_in (iops);
|
|
|
|
if (!simpleEditor && !options.tabbedUI) {
|
|
Gtk::Separator* vsep3 = Gtk::manage (new Gtk::Separator(Gtk::ORIENTATION_VERTICAL));
|
|
iops->attach_next_to (*vsep3, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*navPrev, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*navSync, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*navNext, Gtk::POS_RIGHT, 1, 1);
|
|
}
|
|
|
|
iops->attach_next_to (*vsepz2, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*iareapanel->imageArea->zoomPanel, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*vsepz3, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*tbShowHideSidePanels, Gtk::POS_RIGHT, 1, 1);
|
|
iops->attach_next_to (*tbRightPanel_1, Gtk::POS_RIGHT, 1, 1);
|
|
|
|
MyScrolledToolbar *stb2 = Gtk::manage(new MyScrolledToolbar());
|
|
stb2->set_name("EditorToolbarBottom");
|
|
stb2->add(*iops);
|
|
|
|
editbox->pack_start (*stb2, Gtk::PACK_SHRINK, 0);
|
|
editbox->show_all ();
|
|
|
|
// build screen
|
|
hpanedl = Gtk::manage (new Gtk::Paned (Gtk::ORIENTATION_HORIZONTAL));
|
|
hpanedl->set_name ("EditorLeftPaned");
|
|
hpanedr = Gtk::manage (new Gtk::Paned (Gtk::ORIENTATION_HORIZONTAL));
|
|
hpanedr->set_name ("EditorRightPaned");
|
|
leftbox->reference ();
|
|
vboxright->reference ();
|
|
vboxright->set_name ("EditorModules");
|
|
|
|
if (options.showHistory) {
|
|
hpanedl->pack1 (*leftbox, false, false);
|
|
hpanedl->set_position (options.historyPanelWidth);
|
|
}
|
|
|
|
Gtk::Paned *viewpaned = Gtk::manage (new Gtk::Paned (Gtk::ORIENTATION_VERTICAL));
|
|
fPanel = filePanel;
|
|
|
|
if (filePanel) {
|
|
catalogPane = new Gtk::Paned();
|
|
viewpaned->pack1 (*catalogPane, false, true);
|
|
}
|
|
|
|
viewpaned->pack2 (*editbox, true, true);
|
|
|
|
hpanedl->pack2 (*viewpaned, true, true);
|
|
|
|
hpanedr->pack1 (*hpanedl, true, false);
|
|
hpanedr->pack2 (*vboxright, false, false);
|
|
hpanedl->signal_button_release_event().connect_notify ( sigc::mem_fun (*this, &EditorPanel::leftPaneButtonReleased) );
|
|
hpanedr->signal_button_release_event().connect_notify ( sigc::mem_fun (*this, &EditorPanel::rightPaneButtonReleased) );
|
|
|
|
pack_start (*hpanedr);
|
|
|
|
updateHistogramPosition (0, options.histogramPosition);
|
|
|
|
show_all ();
|
|
/*
|
|
// save as dialog
|
|
if (Glib::file_test (options.lastSaveAsPath, Glib::FILE_TEST_IS_DIR))
|
|
saveAsDialog = new SaveAsDialog (options.lastSaveAsPath);
|
|
else
|
|
saveAsDialog = new SaveAsDialog (safe_get_user_picture_dir());
|
|
|
|
saveAsDialog->set_default_size (options.saveAsDialogWidth, options.saveAsDialogHeight);
|
|
*/
|
|
// connect listeners
|
|
profilep->setProfileChangeListener (tpc);
|
|
history->setProfileChangeListener (tpc);
|
|
history->setHistoryBeforeLineListener (this);
|
|
tpc->addPParamsChangeListener (profilep);
|
|
tpc->addPParamsChangeListener (history);
|
|
tpc->addPParamsChangeListener (this);
|
|
iareapanel->imageArea->setCropGUIListener (tpc->getCropGUIListener());
|
|
iareapanel->imageArea->setPointerMotionListener (navigator);
|
|
iareapanel->imageArea->setImageAreaToolListener (tpc);
|
|
|
|
// initialize components
|
|
info->set_active (options.showInfo);
|
|
tpc->readOptions ();
|
|
|
|
// connect event handlers
|
|
info->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::info_toggled) );
|
|
beforeAfter->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::beforeAfterToggled) );
|
|
hidehp->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::hideHistoryActivated) );
|
|
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) );
|
|
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) {
|
|
navPrev->signal_pressed().connect ( sigc::mem_fun (*this, &EditorPanel::openPreviousEditorImage) );
|
|
}
|
|
|
|
if (navNext) {
|
|
navNext->signal_pressed().connect ( sigc::mem_fun (*this, &EditorPanel::openNextEditorImage) );
|
|
}
|
|
|
|
if (navSync) {
|
|
navSync->signal_pressed().connect ( sigc::mem_fun (*this, &EditorPanel::syncFileBrowser) );
|
|
}
|
|
|
|
ShowHideSidePanelsconn = tbShowHideSidePanels->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::toggleSidePanels), true);
|
|
|
|
if (tbTopPanel_1) {
|
|
tbTopPanel_1->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::tbTopPanel_1_toggled) );
|
|
}
|
|
|
|
}
|
|
|
|
EditorPanel::~EditorPanel ()
|
|
{
|
|
idle_register.destroy();
|
|
|
|
history->setHistoryBeforeLineListener (nullptr);
|
|
// the order is important!
|
|
iareapanel->setBeforeAfterViews (nullptr, iareapanel);
|
|
delete iareapanel;
|
|
iareapanel = nullptr;
|
|
|
|
if (beforeIpc) {
|
|
beforeIpc->stopProcessing ();
|
|
}
|
|
|
|
delete beforeIarea;
|
|
beforeIarea = nullptr;
|
|
|
|
if (beforeIpc) {
|
|
beforeIpc->setPreviewImageListener (nullptr);
|
|
}
|
|
|
|
delete beforePreviewHandler;
|
|
beforePreviewHandler = nullptr;
|
|
|
|
if (beforeIpc) {
|
|
rtengine::StagedImageProcessor::destroy (beforeIpc);
|
|
}
|
|
|
|
beforeIpc = nullptr;
|
|
|
|
close ();
|
|
|
|
if (epih->pending) {
|
|
epih->destroyed = true;
|
|
} else {
|
|
delete epih;
|
|
}
|
|
|
|
delete tpc;
|
|
|
|
delete leftsubpaned;
|
|
delete leftbox;
|
|
delete vsubboxright;
|
|
delete vboxright;
|
|
|
|
//delete saveAsDialog;
|
|
delete catalogPane;
|
|
delete iTopPanel_1_Show;
|
|
delete iTopPanel_1_Hide;
|
|
delete iHistoryShow;
|
|
delete iHistoryHide;
|
|
delete iBeforeLockON;
|
|
delete iBeforeLockOFF;
|
|
delete iRightPanel_1_Show;
|
|
delete iRightPanel_1_Hide;
|
|
delete iShowHideSidePanels_exit;
|
|
delete iShowHideSidePanels;
|
|
}
|
|
|
|
void EditorPanel::leftPaneButtonReleased (GdkEventButton *event)
|
|
{
|
|
if (event->button == 1) {
|
|
// Button 1 released : it's a resize
|
|
options.historyPanelWidth = hpanedl->get_position();
|
|
}
|
|
|
|
/*else if (event->button == 3) {
|
|
}*/
|
|
}
|
|
|
|
void EditorPanel::rightPaneButtonReleased (GdkEventButton *event)
|
|
{
|
|
if (event->button == 1) {
|
|
int winW, winH;
|
|
parentWindow->get_size (winW, winH);
|
|
// Button 1 released : it's a resize
|
|
options.toolPanelWidth = winW - hpanedr->get_position();
|
|
}
|
|
|
|
/*else if (event->button == 3) {
|
|
}*/
|
|
}
|
|
|
|
void EditorPanel::writeOptions()
|
|
{
|
|
if (profilep) {
|
|
profilep->writeOptions();
|
|
}
|
|
|
|
if (tpc) {
|
|
tpc->writeOptions();
|
|
}
|
|
}
|
|
|
|
|
|
void EditorPanel::writeToolExpandedStatus (std::vector<int> &tpOpen)
|
|
{
|
|
if (tpc) {
|
|
tpc->writeToolExpandedStatus (tpOpen);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::updateShowtooltipVisibility (bool showtooltip)
|
|
{
|
|
if (tpc) {
|
|
tpc->updateShowtooltipVisibility (showtooltip);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::showTopPanel (bool show)
|
|
{
|
|
if (tbTopPanel_1->get_active() != show) {
|
|
tbTopPanel_1->set_active (show);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::setAspect ()
|
|
{
|
|
int winW, winH;
|
|
parentWindow->get_size (winW, winH);
|
|
hpanedl->set_position (options.historyPanelWidth);
|
|
hpanedr->set_position (winW - options.toolPanelWidth);
|
|
|
|
// initialize components
|
|
if (info->get_active() != options.showInfo) {
|
|
info->set_active (options.showInfo);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::on_realize ()
|
|
{
|
|
realized = true;
|
|
Gtk::Box::on_realize ();
|
|
// This line is needed to avoid autoexpansion of the window :-/
|
|
//vboxright->set_size_request (options.toolPanelWidth, -1);
|
|
tpc->updateToolState();
|
|
}
|
|
|
|
void EditorPanel::open (Thumbnail* tmb, rtengine::InitialImage* isrc)
|
|
{
|
|
|
|
close();
|
|
|
|
isProcessing = true; // prevents closing-on-init
|
|
|
|
// initialize everything
|
|
openThm = tmb;
|
|
openThm->increaseRef ();
|
|
|
|
fname = openThm->getFileName();
|
|
lastSaveAsFileName = removeExtension (Glib::path_get_basename (fname));
|
|
|
|
previewHandler = new PreviewHandler ();
|
|
|
|
this->isrc = isrc;
|
|
ipc = rtengine::StagedImageProcessor::create (isrc);
|
|
ipc->setProgressListener (this);
|
|
colorMgmtToolBar->updateProcessor();
|
|
ipc->setPreviewImageListener (previewHandler);
|
|
ipc->setPreviewScale (10); // Important
|
|
tpc->initImage (ipc, tmb->getType() == FT_Raw);
|
|
ipc->setHistogramListener (this);
|
|
iareapanel->imageArea->indClippedPanel->silentlyDisableSharpMask();
|
|
|
|
// iarea->fitZoom (); // tell to the editorPanel that the next image has to be fitted to the screen
|
|
iareapanel->imageArea->setPreviewHandler (previewHandler);
|
|
iareapanel->imageArea->setImProcCoordinator (ipc);
|
|
navigator->previewWindow->setPreviewHandler (previewHandler);
|
|
navigator->previewWindow->setImageArea (iareapanel->imageArea);
|
|
|
|
rtengine::ImageSource* is = isrc->getImageSource();
|
|
is->setProgressListener ( this );
|
|
|
|
// try to load the last saved parameters from the cache or from the paramfile file
|
|
ProcParams* ldprof = openThm->createProcParamsForUpdate (true, false); // will be freed by initProfile
|
|
|
|
// initialize profile
|
|
Glib::ustring defProf = openThm->getType() == FT_Raw ? options.defProfRaw : options.defProfImg;
|
|
profilep->initProfile (defProf, ldprof);
|
|
profilep->setInitialFileName (fname);
|
|
|
|
openThm->addThumbnailListener (this);
|
|
info_toggled ();
|
|
|
|
if (beforeIarea) {
|
|
beforeAfterToggled();
|
|
beforeAfterToggled();
|
|
}
|
|
|
|
// If in single tab mode, the main crop window is not constructed the very first time
|
|
// since there was no resize event
|
|
if (iareapanel->imageArea->mainCropWindow) {
|
|
iareapanel->imageArea->mainCropWindow->cropHandler.newImage (ipc, false);
|
|
} else {
|
|
Gtk::Allocation alloc;
|
|
iareapanel->imageArea->on_resized (alloc);
|
|
|
|
// When passing a photo as an argument to the RawTherapee executable, the user wants
|
|
// this auto-loaded photo's thumbnail to be selected and visible in the Filmstrip.
|
|
EditorPanel::syncFileBrowser();
|
|
}
|
|
|
|
history->resetSnapShotNumber();
|
|
navigator->setInvalid(ipc->getFullWidth(),ipc->getFullHeight());
|
|
}
|
|
|
|
void EditorPanel::close ()
|
|
{
|
|
if (ipc) {
|
|
saveProfile ();
|
|
// close image processor and the current thumbnail
|
|
tpc->closeImage (); // this call stops image processing
|
|
tpc->writeOptions ();
|
|
rtengine::ImageSource* is = isrc->getImageSource();
|
|
is->setProgressListener ( nullptr );
|
|
|
|
if (ipc) {
|
|
ipc->setPreviewImageListener (nullptr);
|
|
}
|
|
|
|
if (beforeIpc) {
|
|
beforeIpc->setPreviewImageListener (nullptr);
|
|
}
|
|
|
|
delete previewHandler;
|
|
previewHandler = nullptr;
|
|
|
|
if (iareapanel) {
|
|
iareapanel->imageArea->setPreviewHandler (nullptr);
|
|
iareapanel->imageArea->setImProcCoordinator (nullptr);
|
|
tpc->editModeSwitchedOff();
|
|
}
|
|
|
|
rtengine::StagedImageProcessor::destroy (ipc);
|
|
ipc = nullptr;
|
|
navigator->previewWindow->setPreviewHandler (nullptr);
|
|
|
|
// If the file was deleted somewhere, the openThm.descreaseRef delete the object, but we don't know here
|
|
if (Glib::file_test (fname, Glib::FILE_TEST_EXISTS)) {
|
|
openThm->removeThumbnailListener (this);
|
|
openThm->decreaseRef ();
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorPanel::saveProfile ()
|
|
{
|
|
if (!ipc || !openThm) {
|
|
return;
|
|
}
|
|
|
|
// If the file was deleted, do not generate ghost entries
|
|
if (Glib::file_test (fname, Glib::FILE_TEST_EXISTS)) {
|
|
ProcParams params;
|
|
ipc->getParams (¶ms);
|
|
|
|
// Will call updateCache, which will update both the cached and sidecar files if necessary
|
|
openThm->setProcParams (params, nullptr, EDITOR);
|
|
}
|
|
}
|
|
|
|
Glib::ustring EditorPanel::getShortName ()
|
|
{
|
|
if (openThm) {
|
|
return Glib::path_get_basename (openThm->getFileName ());
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
Glib::ustring EditorPanel::getFileName () const
|
|
{
|
|
if (openThm) {
|
|
return openThm->getFileName ();
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
// TODO!!!
|
|
void EditorPanel::procParamsChanged(
|
|
const rtengine::procparams::ProcParams* params,
|
|
const rtengine::ProcEvent& ev,
|
|
const Glib::ustring& descr,
|
|
const ParamsEdited* paramsEdited
|
|
)
|
|
{
|
|
|
|
// if (ev!=EvPhotoLoaded)
|
|
// saveLabel->set_markup (Glib::ustring("<span foreground=\"#AA0000\" weight=\"bold\">") + M("MAIN_BUTTON_SAVE") + "</span>");
|
|
|
|
rtengine::eSensorType sensorType = isrc->getImageSource()->getSensorType();
|
|
|
|
selectedFrame = 0;
|
|
if (sensorType == rtengine::ST_BAYER) {
|
|
selectedFrame = params->raw.bayersensor.imageNum;
|
|
//} else if (sensorType == rtengine::ST_FUJI_XTRANS) {
|
|
// selectedFrame = params->raw.xtranssensor.imageNum;
|
|
}
|
|
selectedFrame = rtengine::LIM<int>(selectedFrame, 0, isrc->getImageSource()->getMetaData()->getFrameCount() - 1);
|
|
|
|
info_toggled();
|
|
}
|
|
|
|
void EditorPanel::clearParamChanges()
|
|
{
|
|
}
|
|
|
|
void EditorPanel::setProgress(double p)
|
|
{
|
|
MyProgressBar* const pl = progressLabel;
|
|
|
|
idle_register.add(
|
|
[p, pl]() -> bool
|
|
{
|
|
setprogressStrUI(p, {}, pl);
|
|
return false;
|
|
}
|
|
);
|
|
}
|
|
|
|
void EditorPanel::setProgressStr(const Glib::ustring& str)
|
|
{
|
|
MyProgressBar* const pl = progressLabel;
|
|
|
|
idle_register.add(
|
|
[str, pl]() -> bool
|
|
{
|
|
setprogressStrUI(-1.0, str, pl);
|
|
return false;
|
|
}
|
|
);
|
|
}
|
|
|
|
void EditorPanel::setProgressState(bool inProcessing)
|
|
{
|
|
epih->pending++;
|
|
|
|
idle_register.add(
|
|
[this, inProcessing]() -> bool
|
|
{
|
|
if (epih->destroyed)
|
|
{
|
|
if (epih->pending == 1) {
|
|
delete epih;
|
|
} else {
|
|
--epih->pending;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
epih->epanel->refreshProcessingState(inProcessing);
|
|
--epih->pending;
|
|
|
|
return false;
|
|
}
|
|
);
|
|
}
|
|
|
|
void EditorPanel::error(const Glib::ustring& descr)
|
|
{
|
|
}
|
|
|
|
void EditorPanel::error(const Glib::ustring& title, const Glib::ustring& descr)
|
|
{
|
|
epih->pending++;
|
|
|
|
idle_register.add(
|
|
[this, descr, title]() -> bool
|
|
{
|
|
if (epih->destroyed) {
|
|
if (epih->pending == 1) {
|
|
delete epih;
|
|
} else {
|
|
--epih->pending;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
epih->epanel->displayError(title, descr);
|
|
--epih->pending;
|
|
|
|
return false;
|
|
}
|
|
);
|
|
}
|
|
|
|
void EditorPanel::displayError(const Glib::ustring& title, const Glib::ustring& descr)
|
|
{
|
|
GtkWidget* msgd = gtk_message_dialog_new_with_markup (nullptr,
|
|
GTK_DIALOG_DESTROY_WITH_PARENT,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_OK,
|
|
"<b>%s</b>",
|
|
descr.data());
|
|
gtk_window_set_title ((GtkWindow*)msgd, title.data());
|
|
g_signal_connect_swapped (msgd, "response",
|
|
G_CALLBACK (gtk_widget_destroy),
|
|
msgd);
|
|
gtk_widget_show_all (msgd);
|
|
}
|
|
|
|
// This is only called from the ThreadUI, so within the gtk thread
|
|
void EditorPanel::refreshProcessingState (bool inProcessingP)
|
|
{
|
|
double val;
|
|
Glib::ustring str;
|
|
|
|
if (inProcessingP) {
|
|
if (processingStartedTime == 0) {
|
|
processingStartedTime = ::time (nullptr);
|
|
}
|
|
|
|
val = 1.0;
|
|
str = "PROGRESSBAR_PROCESSING";
|
|
} else {
|
|
// Set proc params of thumbnail. It saves it into the cache and updates the file browser.
|
|
if (ipc && openThm && tpc->getChangedState()) {
|
|
rtengine::procparams::ProcParams pparams;
|
|
ipc->getParams (&pparams);
|
|
openThm->setProcParams (pparams, nullptr, EDITOR, false);
|
|
}
|
|
|
|
// Ring a sound if it was a long event
|
|
if (processingStartedTime != 0) {
|
|
time_t curTime = ::time (nullptr);
|
|
|
|
if (::difftime (curTime, processingStartedTime) > options.sndLngEditProcDoneSecs) {
|
|
SoundManager::playSoundAsync (options.sndLngEditProcDone);
|
|
}
|
|
|
|
processingStartedTime = 0;
|
|
}
|
|
|
|
// Set progress bar "done"
|
|
val = 0.0;
|
|
str = "PROGRESSBAR_READY";
|
|
|
|
#ifdef WIN32
|
|
|
|
// Maybe accessing "parent", which is a Gtk object, can justify to get the Gtk lock...
|
|
if (!firstProcessingDone && static_cast<RTWindow*> (parent)->getIsFullscreen()) {
|
|
parent->fullscreen();
|
|
}
|
|
|
|
#endif
|
|
firstProcessingDone = true;
|
|
}
|
|
|
|
isProcessing = inProcessingP;
|
|
|
|
setprogressStrUI(val, str, progressLabel);
|
|
}
|
|
|
|
void EditorPanel::info_toggled ()
|
|
{
|
|
|
|
Glib::ustring infoString;
|
|
Glib::ustring expcomp;
|
|
|
|
if (!ipc || !openThm) {
|
|
return;
|
|
}
|
|
|
|
const rtengine::FramesMetaData* idata = ipc->getInitialImage()->getMetaData();
|
|
|
|
if (idata && idata->hasExif(selectedFrame)) {
|
|
infoString = Glib::ustring::compose ("%1 + %2\n<span size=\"small\">f/</span><span size=\"large\">%3</span> <span size=\"large\">%4</span><span size=\"small\">s</span> <span size=\"small\">%5</span><span size=\"large\">%6</span> <span size=\"large\">%7</span><span size=\"small\">mm</span>",
|
|
escapeHtmlChars (idata->getMake() + " " + idata->getModel()),
|
|
escapeHtmlChars (idata->getLens()),
|
|
Glib::ustring (idata->apertureToString (idata->getFNumber(selectedFrame))),
|
|
Glib::ustring (idata->shutterToString (idata->getShutterSpeed(selectedFrame))),
|
|
M ("QINFO_ISO"), idata->getISOSpeed(selectedFrame),
|
|
Glib::ustring::format (std::setw (3), std::fixed, std::setprecision (2), idata->getFocalLen(selectedFrame)));
|
|
|
|
expcomp = Glib::ustring (idata->expcompToString (idata->getExpComp(selectedFrame), true)); // maskZeroexpcomp
|
|
|
|
if (!expcomp.empty ()) {
|
|
infoString = Glib::ustring::compose ("%1 <span size=\"large\">%2</span><span size=\"small\">EV</span>",
|
|
infoString,
|
|
expcomp /*Glib::ustring(idata->expcompToString(idata->getExpComp()))*/);
|
|
}
|
|
|
|
infoString = Glib::ustring::compose ("%1\n<span size=\"small\">%2</span><span>%3</span>",
|
|
infoString,
|
|
escapeHtmlChars (Glib::path_get_dirname (openThm->getFileName())) + G_DIR_SEPARATOR_S,
|
|
escapeHtmlChars (Glib::path_get_basename (openThm->getFileName())) );
|
|
|
|
int ww = ipc->getFullWidth();
|
|
int hh = ipc->getFullHeight();
|
|
//megapixels
|
|
infoString = Glib::ustring::compose ("%1\n<span size=\"small\">%2 MP (%3x%4)</span>",
|
|
infoString,
|
|
Glib::ustring::format (std::setw (4), std::fixed, std::setprecision (1), (float)ww * hh / 1000000),
|
|
ww, hh);
|
|
|
|
//adding special characteristics
|
|
bool isHDR = idata->getHDR();
|
|
bool isPixelShift = idata->getPixelShift();
|
|
unsigned int numFrames = idata->getFrameCount();
|
|
if (isHDR) {
|
|
infoString = Glib::ustring::compose ("%1\n" + M("QINFO_HDR"), infoString, numFrames);
|
|
if (numFrames == 1) {
|
|
int sampleFormat = idata->getSampleFormat(selectedFrame);
|
|
infoString = Glib::ustring::compose ("%1 / %2", infoString, M(Glib::ustring::compose("SAMPLEFORMAT_%1", sampleFormat)));
|
|
}
|
|
} else if (isPixelShift) {
|
|
infoString = Glib::ustring::compose ("%1\n" + M("QINFO_PIXELSHIFT"), infoString, numFrames);
|
|
} else if (numFrames > 1) {
|
|
infoString = Glib::ustring::compose ("%1\n" + M("QINFO_FRAMECOUNT"), infoString, numFrames);
|
|
}
|
|
} else {
|
|
infoString = M ("QINFO_NOEXIF");
|
|
}
|
|
|
|
iareapanel->imageArea->setInfoText (std::move(infoString));
|
|
iareapanel->imageArea->infoEnabled (info->get_active ());
|
|
}
|
|
|
|
void EditorPanel::hideHistoryActivated ()
|
|
{
|
|
|
|
removeIfThere (hpanedl, leftbox, false);
|
|
|
|
if (hidehp->get_active()) {
|
|
hpanedl->pack1 (*leftbox, false, false);
|
|
}
|
|
|
|
options.showHistory = hidehp->get_active();
|
|
|
|
if (options.showHistory) {
|
|
hidehp->set_image (*iHistoryHide);
|
|
} else {
|
|
hidehp->set_image (*iHistoryShow);
|
|
}
|
|
|
|
tbShowHideSidePanels_managestate();
|
|
}
|
|
|
|
|
|
void EditorPanel::tbRightPanel_1_toggled ()
|
|
{
|
|
/*
|
|
removeIfThere (hpanedr, vboxright, false);
|
|
if (tbRightPanel_1->get_active()){
|
|
hpanedr->pack2(*vboxright, false, true);
|
|
tbRightPanel_1->set_image (*iRightPanel_1_Hide);
|
|
}
|
|
else {
|
|
tbRightPanel_1->set_image (*iRightPanel_1_Show);
|
|
}
|
|
tbShowHideSidePanels_managestate();
|
|
*/
|
|
if (vboxright) {
|
|
if (tbRightPanel_1->get_active()) {
|
|
vboxright->show();
|
|
tbRightPanel_1->set_image (*iRightPanel_1_Hide);
|
|
} else {
|
|
vboxright->hide();
|
|
tbRightPanel_1->set_image (*iRightPanel_1_Show);
|
|
}
|
|
|
|
tbShowHideSidePanels_managestate();
|
|
}
|
|
}
|
|
|
|
void EditorPanel::tbTopPanel_1_visible (bool visible)
|
|
{
|
|
if (!tbTopPanel_1) {
|
|
return;
|
|
}
|
|
|
|
if (visible) {
|
|
tbTopPanel_1->show();
|
|
} else {
|
|
tbTopPanel_1->hide();
|
|
}
|
|
}
|
|
|
|
void EditorPanel::tbTopPanel_1_toggled ()
|
|
{
|
|
|
|
if (catalogPane) { // catalogPane does not exist in multitab mode
|
|
|
|
if (tbTopPanel_1->get_active()) {
|
|
catalogPane->show();
|
|
tbTopPanel_1->set_image (*iTopPanel_1_Hide);
|
|
options.editorFilmStripOpened = true;
|
|
} else {
|
|
catalogPane->hide();
|
|
tbTopPanel_1->set_image (*iTopPanel_1_Show);
|
|
options.editorFilmStripOpened = false;
|
|
}
|
|
|
|
tbShowHideSidePanels_managestate();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* WARNING: Take care of the simpleEditor value when adding or modifying shortcut keys,
|
|
* since handleShortcutKey is now also triggered in simple editor mode
|
|
*/
|
|
bool EditorPanel::handleShortcutKey (GdkEventKey* event)
|
|
{
|
|
|
|
bool ctrl = event->state & GDK_CONTROL_MASK;
|
|
bool shift = event->state & GDK_SHIFT_MASK;
|
|
bool alt = event->state & GDK_MOD1_MASK;
|
|
#ifdef __WIN32__
|
|
bool altgr = event->state & GDK_MOD2_MASK;
|
|
#else
|
|
bool altgr = event->state & GDK_MOD5_MASK;
|
|
#endif
|
|
|
|
// Editor Layout
|
|
switch (event->keyval) {
|
|
case GDK_KEY_L:
|
|
if (tbTopPanel_1) {
|
|
tbTopPanel_1->set_active (!tbTopPanel_1->get_active()); // toggle top panel
|
|
}
|
|
|
|
if (ctrl) {
|
|
hidehp->set_active (!hidehp->get_active()); // toggle History (left panel)
|
|
}
|
|
|
|
if (alt) {
|
|
tbRightPanel_1->set_active (!tbRightPanel_1->get_active()); // toggle right panel
|
|
}
|
|
|
|
return true;
|
|
break;
|
|
|
|
case GDK_KEY_l:
|
|
if (!shift && !alt /*&& !ctrl*/) {
|
|
hidehp->set_active (!hidehp->get_active()); // toggle History (left panel)
|
|
return true;
|
|
}
|
|
|
|
if (alt && !ctrl) { // toggle right panel
|
|
tbRightPanel_1->set_active (!tbRightPanel_1->get_active());
|
|
return true;
|
|
}
|
|
|
|
if (alt && ctrl) { // toggle left and right panels
|
|
hidehp->set_active (!hidehp->get_active());
|
|
tbRightPanel_1->set_active (!tbRightPanel_1->get_active());
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
case GDK_KEY_m: // Maximize preview panel: hide top AND right AND history panels
|
|
if (!ctrl && !alt) {
|
|
toggleSidePanels();
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
|
|
case GDK_KEY_M: // Maximize preview panel: hide top AND right AND history panels AND (fit image preview)
|
|
if (!ctrl && !alt) {
|
|
toggleSidePanelsZoomFit();
|
|
return true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
#ifdef __WIN32__
|
|
|
|
if (!alt && !ctrl && !altgr && event->hardware_keycode == 0x39 ) {
|
|
iareapanel->imageArea->previewModePanel->togglebackColor();
|
|
return true;
|
|
}
|
|
|
|
#else
|
|
|
|
if (!alt && !ctrl && !altgr && event->hardware_keycode == 0x12 ) {
|
|
iareapanel->imageArea->previewModePanel->togglebackColor();
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
if (!alt) {
|
|
if (!ctrl) {
|
|
// Normal
|
|
switch (event->keyval) {
|
|
case GDK_KEY_bracketright:
|
|
tpc->coarse->rotateRight();
|
|
return true;
|
|
|
|
case GDK_KEY_bracketleft:
|
|
tpc->coarse->rotateLeft();
|
|
return true;
|
|
|
|
case GDK_KEY_i:
|
|
case GDK_KEY_I:
|
|
info->set_active (!info->get_active());
|
|
return true;
|
|
|
|
case GDK_KEY_B:
|
|
beforeAfter->set_active (!beforeAfter->get_active());
|
|
return true;
|
|
|
|
case GDK_KEY_plus:
|
|
case GDK_KEY_equal:
|
|
case GDK_KEY_KP_Add:
|
|
iareapanel->imageArea->zoomPanel->zoomInClicked();
|
|
return true;
|
|
|
|
case GDK_KEY_minus:
|
|
case GDK_KEY_underscore:
|
|
case GDK_KEY_KP_Subtract:
|
|
iareapanel->imageArea->zoomPanel->zoomOutClicked();
|
|
return true;
|
|
|
|
case GDK_KEY_z://GDK_1
|
|
iareapanel->imageArea->zoomPanel->zoom11Clicked();
|
|
return true;
|
|
|
|
/*
|
|
#ifndef __WIN32__
|
|
case GDK_KEY_9: // toggle background color of the preview
|
|
iareapanel->imageArea->previewModePanel->togglebackColor();
|
|
return true;
|
|
#endif
|
|
*/
|
|
case GDK_KEY_r: //preview mode Red
|
|
iareapanel->imageArea->previewModePanel->toggleR();
|
|
return true;
|
|
|
|
case GDK_KEY_g: //preview mode Green
|
|
iareapanel->imageArea->previewModePanel->toggleG();
|
|
return true;
|
|
|
|
case GDK_KEY_b: //preview mode Blue
|
|
iareapanel->imageArea->previewModePanel->toggleB();
|
|
return true;
|
|
|
|
case GDK_KEY_p: //preview mode Sharpening Contrast mask
|
|
iareapanel->imageArea->indClippedPanel->toggleSharpMask();
|
|
return true;
|
|
|
|
case GDK_KEY_v: //preview mode Luminosity
|
|
iareapanel->imageArea->previewModePanel->toggleL();
|
|
return true;
|
|
|
|
case GDK_KEY_F: //preview mode Focus Mask
|
|
iareapanel->imageArea->indClippedPanel->toggleFocusMask();
|
|
return true;
|
|
|
|
case GDK_KEY_less:
|
|
iareapanel->imageArea->indClippedPanel->toggleClipped (false);
|
|
return true;
|
|
|
|
case GDK_KEY_greater:
|
|
iareapanel->imageArea->indClippedPanel->toggleClipped (true);
|
|
return true;
|
|
|
|
case GDK_KEY_f:
|
|
iareapanel->imageArea->zoomPanel->zoomFitCropClicked();
|
|
return true;
|
|
|
|
case GDK_KEY_F5:
|
|
openThm->openDefaultViewer ((event->state & GDK_SHIFT_MASK) ? 2 : 1);
|
|
return true;
|
|
|
|
case GDK_KEY_y: // synchronize filebrowser with image in Editor
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
fPanel->fileCatalog->selectImage (fname, false);
|
|
return true;
|
|
}
|
|
|
|
break; // to avoid gcc complain
|
|
|
|
case GDK_KEY_x: // clear filters and synchronize filebrowser with image in Editor
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
fPanel->fileCatalog->selectImage (fname, true);
|
|
return true;
|
|
}
|
|
|
|
break; // to avoid gcc complain
|
|
}
|
|
} else {
|
|
// With control
|
|
switch (event->keyval) {
|
|
case GDK_KEY_S:
|
|
saveProfile();
|
|
setProgressStr (M ("PROGRESSBAR_PROCESSING_PROFILESAVED"));
|
|
return true;
|
|
|
|
case GDK_KEY_s:
|
|
if (!gimpPlugin) {
|
|
saveAsPressed();
|
|
}
|
|
|
|
return true;
|
|
|
|
case GDK_KEY_b:
|
|
if (!gimpPlugin && !simpleEditor) {
|
|
queueImgPressed();
|
|
}
|
|
|
|
return true;
|
|
|
|
case GDK_KEY_e:
|
|
if (!gimpPlugin) {
|
|
sendToExternalPressed();
|
|
}
|
|
|
|
return true;
|
|
|
|
case GDK_KEY_z:
|
|
history->undo ();
|
|
return true;
|
|
|
|
case GDK_KEY_Z:
|
|
history->redo ();
|
|
return true;
|
|
|
|
case GDK_KEY_F5:
|
|
openThm->openDefaultViewer (3);
|
|
return true;
|
|
|
|
case GDK_KEY_f:
|
|
case GDK_KEY_F:
|
|
// No action is performed to avoid Gtk-CRITICAL due to Locallab treeview when treeview isn't focused
|
|
return true;
|
|
}
|
|
} //if (!ctrl)
|
|
} //if (!alt)
|
|
|
|
if (alt) {
|
|
switch (event->keyval) {
|
|
case GDK_KEY_s:
|
|
history->addBookmarkPressed ();
|
|
setProgressStr (M ("PROGRESSBAR_SNAPSHOT_ADDED"));
|
|
return true;
|
|
|
|
case GDK_KEY_f:
|
|
iareapanel->imageArea->zoomPanel->zoomFitClicked();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (shift) {
|
|
switch (event->keyval) {
|
|
case GDK_KEY_F3: // open Previous image from Editor's perspective
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
EditorPanel::openPreviousEditorImage();
|
|
return true;
|
|
}
|
|
|
|
break; // to avoid gcc complain
|
|
|
|
case GDK_KEY_F4: // open next image from Editor's perspective
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
EditorPanel::openNextEditorImage();
|
|
return true;
|
|
}
|
|
|
|
break; // to avoid gcc complain
|
|
}
|
|
}
|
|
|
|
if (tpc->getToolBar() && tpc->getToolBar()->handleShortcutKey (event)) {
|
|
return true;
|
|
}
|
|
|
|
if (tpc->handleShortcutKey (event)) {
|
|
return true;
|
|
}
|
|
|
|
if (!simpleEditor && fPanel) {
|
|
if (fPanel->handleShortcutKey (event)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void EditorPanel::procParamsChanged (Thumbnail* thm, int whoChangedIt, bool upgradeHint)
|
|
{
|
|
|
|
if (whoChangedIt != EDITOR) {
|
|
PartialProfile pp (true);
|
|
pp.set (true);
|
|
* (pp.pparams) = openThm->getProcParams();
|
|
pp.pedited->locallab.spots.resize(pp.pparams->locallab.spots.size(), LocallabParamsEdited::LocallabSpotEdited(true));
|
|
tpc->profileChange (&pp, rtengine::EvProfileChangeNotification, M ("PROGRESSDLG_PROFILECHANGEDINBROWSER"));
|
|
pp.deleteInstance();
|
|
}
|
|
}
|
|
|
|
bool EditorPanel::idle_saveImage (ProgressConnector<rtengine::IImagefloat*> *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams)
|
|
{
|
|
rtengine::IImagefloat* img = pc->returnValue();
|
|
delete pc;
|
|
|
|
if ( img ) {
|
|
setProgressStr (M ("GENERAL_SAVE"));
|
|
setProgress (0.9f);
|
|
|
|
ProgressConnector<int> *ld = new ProgressConnector<int>();
|
|
img->setSaveProgressListener (parent->getProgressListener());
|
|
|
|
if (sf.format == "tif")
|
|
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fname, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed, sf.bigTiff),
|
|
sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams));
|
|
else if (sf.format == "png")
|
|
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsPNG), fname, sf.pngBits),
|
|
sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams));
|
|
else if (sf.format == "jpg")
|
|
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsJPEG), fname, sf.jpegQuality, sf.jpegSubSamp),
|
|
sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams));
|
|
else {
|
|
delete ld;
|
|
}
|
|
} else {
|
|
Glib::ustring msg_ = Glib::ustring ("<b>") + escapeHtmlChars(fname) + ": Error during image processing\n</b>";
|
|
Gtk::MessageDialog msgd (*parent, msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.run ();
|
|
|
|
saveimgas->set_sensitive (true);
|
|
send_to_external->set_sensitive(true);
|
|
isProcessing = false;
|
|
|
|
}
|
|
|
|
rtengine::ImageSource* imgsrc = isrc->getImageSource ();
|
|
imgsrc->setProgressListener (this);
|
|
return false;
|
|
}
|
|
|
|
bool EditorPanel::idle_imageSaved (ProgressConnector<int> *pc, rtengine::IImagefloat* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams)
|
|
{
|
|
delete img;
|
|
|
|
if (! pc->returnValue() ) {
|
|
openThm->imageDeveloped ();
|
|
|
|
// save processing parameters, if needed
|
|
if (sf.saveParams) {
|
|
// We keep the extension to avoid overwriting the profile when we have
|
|
// the same output filename with different extension
|
|
pparams.save (fname + ".out" + paramFileExtension);
|
|
}
|
|
} else {
|
|
error (M ("MAIN_MSG_CANNOTSAVE"), fname);
|
|
}
|
|
|
|
saveimgas->set_sensitive (true);
|
|
send_to_external->set_sensitive(true);
|
|
|
|
parent->setProgressStr ("");
|
|
parent->setProgress (0.);
|
|
|
|
setProgressState (false);
|
|
|
|
delete pc;
|
|
SoundManager::playSoundAsync (options.sndBatchQueueDone);
|
|
isProcessing = false;
|
|
return false;
|
|
}
|
|
|
|
BatchQueueEntry* EditorPanel::createBatchQueueEntry ()
|
|
{
|
|
|
|
rtengine::procparams::ProcParams pparams;
|
|
ipc->getParams (&pparams);
|
|
//rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
|
|
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (openThm->getFileName (), openThm->getType() == FT_Raw, pparams);
|
|
int fullW = 0, fullH = 0;
|
|
isrc->getImageSource()->getFullSize (fullW, fullH, pparams.coarse.rotate == 90 || pparams.coarse.rotate == 270 ? TR_R90 : TR_NONE);
|
|
int prevh = BatchQueue::calcMaxThumbnailHeight();
|
|
int prevw = int ((size_t)fullW * (size_t)prevh / (size_t)fullH);
|
|
return new BatchQueueEntry (job, pparams, openThm->getFileName(), prevw, prevh, openThm, options.overwriteOutputFile);
|
|
}
|
|
|
|
|
|
|
|
void EditorPanel::saveAsPressed ()
|
|
{
|
|
if (!ipc || !openThm) {
|
|
return;
|
|
}
|
|
|
|
bool fnameOK = false;
|
|
Glib::ustring fnameOut;
|
|
|
|
SaveAsDialog* saveAsDialog;
|
|
auto toplevel = static_cast<Gtk::Window*> (get_toplevel ());
|
|
|
|
if (Glib::file_test (options.lastSaveAsPath, Glib::FILE_TEST_IS_DIR)) {
|
|
saveAsDialog = new SaveAsDialog (options.lastSaveAsPath, toplevel);
|
|
} else {
|
|
saveAsDialog = new SaveAsDialog (PlacesBrowser::userPicturesDir (), toplevel);
|
|
}
|
|
|
|
saveAsDialog->set_default_size (options.saveAsDialogWidth, options.saveAsDialogHeight);
|
|
saveAsDialog->setInitialFileName (lastSaveAsFileName);
|
|
saveAsDialog->setImagePath (fname);
|
|
|
|
do {
|
|
int result = saveAsDialog->run ();
|
|
|
|
// The SaveAsDialog ensure that a filename has been specified
|
|
fnameOut = saveAsDialog->getFileName ();
|
|
|
|
options.lastSaveAsPath = saveAsDialog->getDirectory ();
|
|
saveAsDialog->get_size (options.saveAsDialogWidth, options.saveAsDialogHeight);
|
|
options.autoSuffix = saveAsDialog->getAutoSuffix ();
|
|
options.saveMethodNum = saveAsDialog->getSaveMethodNum ();
|
|
lastSaveAsFileName = Glib::path_get_basename (removeExtension (fnameOut));
|
|
SaveFormat sf = saveAsDialog->getFormat ();
|
|
options.saveFormat = sf;
|
|
options.forceFormatOpts = saveAsDialog->getForceFormatOpts ();
|
|
|
|
if (result != Gtk::RESPONSE_OK) {
|
|
break;
|
|
}
|
|
|
|
if (saveAsDialog->getImmediately ()) {
|
|
// separate filename and the path to the destination directory
|
|
Glib::ustring dstdir = Glib::path_get_dirname (fnameOut);
|
|
Glib::ustring dstfname = Glib::path_get_basename (removeExtension (fnameOut));
|
|
Glib::ustring dstext = getExtension (fnameOut);
|
|
|
|
if (saveAsDialog->getAutoSuffix()) {
|
|
|
|
Glib::ustring fnameTemp;
|
|
|
|
for (int tries = 0; tries < 100; tries++) {
|
|
if (tries == 0) {
|
|
fnameTemp = Glib::ustring::compose ("%1.%2", Glib::build_filename (dstdir, dstfname), dstext);
|
|
} else {
|
|
fnameTemp = Glib::ustring::compose ("%1-%2.%3", Glib::build_filename (dstdir, dstfname), tries, dstext);
|
|
}
|
|
|
|
if (!Glib::file_test (fnameTemp, Glib::FILE_TEST_EXISTS)) {
|
|
fnameOut = fnameTemp;
|
|
fnameOK = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check if it exists
|
|
if (!fnameOK) {
|
|
fnameOK = confirmOverwrite (*saveAsDialog, fnameOut);
|
|
}
|
|
|
|
if (fnameOK) {
|
|
isProcessing = true;
|
|
// save image
|
|
rtengine::procparams::ProcParams pparams;
|
|
ipc->getParams (&pparams);
|
|
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_saveImage ), ld, fnameOut, sf, pparams));
|
|
saveimgas->set_sensitive (false);
|
|
send_to_external->set_sensitive(false);
|
|
}
|
|
} else {
|
|
BatchQueueEntry* bqe = createBatchQueueEntry ();
|
|
bqe->outFileName = fnameOut;
|
|
bqe->saveFormat = saveAsDialog->getFormat ();
|
|
bqe->overwriteFile = !saveAsDialog->getAutoSuffix();
|
|
bqe->forceFormatOpts = saveAsDialog->getForceFormatOpts ();
|
|
parent->addBatchQueueJob (bqe, saveAsDialog->getToHeadOfQueue ());
|
|
fnameOK = true;
|
|
}
|
|
|
|
// ask parent to redraw file browser
|
|
// ... or does it automatically when the tab is switched to it
|
|
} while (!fnameOK);
|
|
|
|
saveAsDialog->hide();
|
|
|
|
delete saveAsDialog;
|
|
}
|
|
|
|
void EditorPanel::queueImgPressed ()
|
|
{
|
|
if (!ipc || !openThm) {
|
|
return;
|
|
}
|
|
|
|
saveProfile ();
|
|
parent->addBatchQueueJob (createBatchQueueEntry ());
|
|
}
|
|
|
|
void EditorPanel::sendToExternal()
|
|
{
|
|
if (!ipc || !openThm) {
|
|
return;
|
|
}
|
|
|
|
// develop image
|
|
rtengine::procparams::ProcParams pparams;
|
|
ipc->getParams (&pparams);
|
|
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);
|
|
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);
|
|
external_editor_native_command = editor.native_command;
|
|
sendToExternal();
|
|
}
|
|
}
|
|
|
|
|
|
bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveFormat &sf)
|
|
{
|
|
rtengine::procparams::ProcParams pparams;
|
|
ipc->getParams (&pparams);
|
|
|
|
rtengine::ProcessingJob *job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
|
|
|
|
// save immediately
|
|
rtengine::IImagefloat *img = rtengine::processImage (job, err, nullptr, false);
|
|
|
|
int err = 0;
|
|
|
|
if (gimpPlugin) {
|
|
err = img->saveAsTIFF (filename, 32, true, true);
|
|
} else if (sf.format == "tif") {
|
|
err = img->saveAsTIFF (filename, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed, sf.bigTiff);
|
|
} else if (sf.format == "png") {
|
|
err = img->saveAsPNG (filename, sf.pngBits);
|
|
} else if (sf.format == "jpg") {
|
|
err = img->saveAsJPEG (filename, sf.jpegQuality, sf.jpegSubSamp);
|
|
} else {
|
|
err = 1;
|
|
}
|
|
|
|
delete img;
|
|
return !err;
|
|
}
|
|
|
|
|
|
void EditorPanel::openPreviousEditorImage()
|
|
{
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
fPanel->fileCatalog->openNextPreviousEditorImage (fname, false, NAV_PREVIOUS);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::openNextEditorImage()
|
|
{
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
fPanel->fileCatalog->openNextPreviousEditorImage (fname, false, NAV_NEXT);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::syncFileBrowser() // synchronize filebrowser with image in Editor
|
|
{
|
|
if (!simpleEditor && fPanel && !fname.empty()) {
|
|
fPanel->fileCatalog->selectImage (fname, false);
|
|
}
|
|
}
|
|
|
|
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();
|
|
colorMgmtToolBar->updateHistogram();
|
|
}
|
|
|
|
bool EditorPanel::idle_sendToGimp ( ProgressConnector<rtengine::IImagefloat*> *pc, Glib::ustring fname)
|
|
{
|
|
|
|
rtengine::IImagefloat* img = pc->returnValue();
|
|
delete pc;
|
|
|
|
if (img) {
|
|
// get file name base
|
|
Glib::ustring shortname = removeExtension(Glib::path_get_basename(fname));
|
|
Glib::ustring dirname;
|
|
switch (options.editor_out_dir) {
|
|
case Options::EDITOR_OUT_DIR_CURRENT:
|
|
dirname = Glib::path_get_dirname(fname);
|
|
break;
|
|
case Options::EDITOR_OUT_DIR_CUSTOM:
|
|
dirname = options.editor_custom_out_dir;
|
|
break;
|
|
default: // Options::EDITOR_OUT_DIR_TEMP
|
|
dirname = getTmpDirectory();
|
|
break;
|
|
}
|
|
Glib::ustring fullFileName = Glib::build_filename(dirname, shortname);
|
|
|
|
SaveFormat sf;
|
|
sf.format = "tif";
|
|
if (options.editor_float32) {
|
|
sf.tiffBits = 32;
|
|
sf.tiffFloat = true;
|
|
} else {
|
|
sf.tiffBits = 16;
|
|
sf.tiffFloat = false;
|
|
}
|
|
|
|
sf.tiffUncompressed = true;
|
|
sf.saveParams = true;
|
|
|
|
Glib::ustring fileName = Glib::ustring::compose ("%1.%2", fullFileName, sf.format);
|
|
|
|
// TODO: Just list all file with a suitable name instead of brute force...
|
|
int tries = 1;
|
|
while (Glib::file_test (fileName, Glib::FILE_TEST_EXISTS) && tries < 1000) {
|
|
fileName = Glib::ustring::compose ("%1-%2.%3", fullFileName, tries, sf.format);
|
|
tries++;
|
|
}
|
|
|
|
if (tries == 1000) {
|
|
delete img;
|
|
return false;
|
|
}
|
|
|
|
ProgressConnector<int> *ld = new ProgressConnector<int>();
|
|
img->setSaveProgressListener (parent->getProgressListener());
|
|
ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fileName, sf.tiffBits, sf.tiffFloat, sf.tiffUncompressed, sf.bigTiff),
|
|
sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_sentToGimp), ld, img, fileName));
|
|
} else {
|
|
Glib::ustring msg_ = Glib::ustring ("<b> Error during image processing\n</b>");
|
|
Gtk::MessageDialog msgd (*parent, msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.run ();
|
|
saveimgas->set_sensitive (true);
|
|
send_to_external->set_sensitive(true);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool EditorPanel::idle_sentToGimp (ProgressConnector<int> *pc, rtengine::IImagefloat* img, Glib::ustring filename)
|
|
{
|
|
if (img) {
|
|
delete img;
|
|
cached_exported_filename = filename;
|
|
}
|
|
int errore = 0;
|
|
setProgressState(false);
|
|
if (pc) {
|
|
errore = pc->returnValue();
|
|
delete pc;
|
|
}
|
|
|
|
if ((!img && Glib::file_test(filename, Glib::FILE_TEST_IS_REGULAR)) || (img && !errore)) {
|
|
saveimgas->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);
|
|
|
|
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);
|
|
msgd.set_secondary_text (M ("MAIN_MSG_CANNOTSTARTEDITOR_SECONDARY"));
|
|
msgd.set_title (M ("MAIN_BUTTON_SENDTOEDITOR"));
|
|
msgd.run ();
|
|
}
|
|
}
|
|
|
|
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();
|
|
external_editor_native_command = false;
|
|
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)
|
|
{
|
|
|
|
if (beforeIpc) {
|
|
ProcParams* pparams = beforeIpc->beginUpdateParams ();
|
|
*pparams = params;
|
|
beforeIpc->endUpdateParams (rtengine::EvProfileChanged); // starts the IPC processing
|
|
}
|
|
}
|
|
|
|
void EditorPanel::beforeAfterToggled ()
|
|
{
|
|
|
|
if (!ipc) {
|
|
return;
|
|
}
|
|
|
|
removeIfThere (beforeAfterBox, beforeBox, false);
|
|
removeIfThere (afterBox, afterHeaderBox, false);
|
|
|
|
if (beforeIarea) {
|
|
if (beforeIpc) {
|
|
beforeIpc->stopProcessing ();
|
|
}
|
|
|
|
iareapanel->setBeforeAfterViews (nullptr, iareapanel);
|
|
iareapanel->imageArea->iLinkedImageArea = nullptr;
|
|
delete beforeIarea;
|
|
beforeIarea = nullptr;
|
|
|
|
if (beforeIpc) {
|
|
beforeIpc->setPreviewImageListener (nullptr);
|
|
}
|
|
|
|
delete beforePreviewHandler;
|
|
beforePreviewHandler = nullptr;
|
|
|
|
if (beforeIpc) {
|
|
rtengine::StagedImageProcessor::destroy (beforeIpc);
|
|
}
|
|
|
|
beforeIpc = nullptr;
|
|
}
|
|
|
|
if (beforeAfter->get_active ()) {
|
|
|
|
int errorCode = 0;
|
|
rtengine::InitialImage *beforeImg = rtengine::InitialImage::load ( isrc->getImageSource ()->getFileName(), openThm->getType() == FT_Raw, &errorCode, nullptr);
|
|
|
|
if ( !beforeImg || errorCode ) {
|
|
return;
|
|
}
|
|
|
|
beforeIarea = new ImageAreaPanel ();
|
|
|
|
int HeaderBoxHeight = 17;
|
|
|
|
beforeLabel = Gtk::manage (new Gtk::Label ());
|
|
beforeLabel->set_markup (Glib::ustring ("<b>") + M ("GENERAL_BEFORE") + "</b>");
|
|
tbBeforeLock = Gtk::manage (new Gtk::ToggleButton ());
|
|
tbBeforeLock->set_relief(Gtk::RELIEF_NONE);
|
|
tbBeforeLock->set_tooltip_markup (M ("MAIN_TOOLTIP_BEFOREAFTERLOCK"));
|
|
tbBeforeLock->signal_toggled().connect ( sigc::mem_fun (*this, &EditorPanel::tbBeforeLock_toggled) );
|
|
beforeHeaderBox = Gtk::manage (new Gtk::Box (Gtk::ORIENTATION_HORIZONTAL));
|
|
beforeHeaderBox->get_style_context()->add_class("smallbuttonbox");
|
|
beforeHeaderBox->pack_end (*tbBeforeLock, Gtk::PACK_SHRINK, 2);
|
|
beforeHeaderBox->pack_end (*beforeLabel, Gtk::PACK_SHRINK, 2);
|
|
beforeHeaderBox->set_size_request (0, HeaderBoxHeight);
|
|
|
|
history->blistenerLock ? tbBeforeLock->set_image (*iBeforeLockON) : tbBeforeLock->set_image (*iBeforeLockOFF);
|
|
tbBeforeLock->set_active (history->blistenerLock);
|
|
|
|
beforeBox = Gtk::manage (new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
|
|
beforeBox->pack_start (*beforeHeaderBox, Gtk::PACK_SHRINK, 2);
|
|
beforeBox->pack_start (*beforeIarea);
|
|
|
|
afterLabel = Gtk::manage (new Gtk::Label ());
|
|
afterLabel->set_markup (Glib::ustring ("<b>") + M ("GENERAL_AFTER") + "</b>");
|
|
afterHeaderBox = Gtk::manage (new Gtk::Box (Gtk::ORIENTATION_HORIZONTAL));
|
|
afterHeaderBox->set_size_request (0, HeaderBoxHeight);
|
|
afterHeaderBox->pack_end (*afterLabel, Gtk::PACK_SHRINK, 2);
|
|
afterBox->pack_start (*afterHeaderBox, Gtk::PACK_SHRINK, 2);
|
|
afterBox->reorder_child (*afterHeaderBox, 0);
|
|
|
|
beforeAfterBox->pack_start (*beforeBox);
|
|
beforeAfterBox->reorder_child (*beforeBox, 0);
|
|
beforeAfterBox->show_all ();
|
|
|
|
beforePreviewHandler = new PreviewHandler ();
|
|
|
|
beforeIpc = rtengine::StagedImageProcessor::create (beforeImg);
|
|
beforeIpc->setPreviewScale (10);
|
|
beforeIpc->setPreviewImageListener (beforePreviewHandler);
|
|
Glib::ustring monitorProfile;
|
|
rtengine::RenderingIntent intent;
|
|
ipc->getMonitorProfile(monitorProfile, intent);
|
|
beforeIpc->setMonitorProfile(monitorProfile, intent);
|
|
|
|
beforeIarea->imageArea->setPreviewHandler (beforePreviewHandler);
|
|
beforeIarea->imageArea->setImProcCoordinator (beforeIpc);
|
|
|
|
beforeIarea->imageArea->setPreviewModePanel (iareapanel->imageArea->previewModePanel);
|
|
beforeIarea->imageArea->setIndicateClippedPanel (iareapanel->imageArea->indClippedPanel);
|
|
iareapanel->imageArea->iLinkedImageArea = beforeIarea->imageArea;
|
|
|
|
iareapanel->setBeforeAfterViews (beforeIarea, iareapanel);
|
|
beforeIarea->setBeforeAfterViews (beforeIarea, iareapanel);
|
|
|
|
rtengine::procparams::ProcParams params;
|
|
|
|
if (history->getBeforeLineParams (params)) {
|
|
historyBeforeLineChanged (params);
|
|
}
|
|
}
|
|
}
|
|
|
|
void EditorPanel::tbBeforeLock_toggled ()
|
|
{
|
|
history->blistenerLock = tbBeforeLock->get_active();
|
|
tbBeforeLock->get_active() ? tbBeforeLock->set_image (*iBeforeLockON) : tbBeforeLock->set_image (*iBeforeLockOFF);
|
|
}
|
|
|
|
void EditorPanel::histogramChanged(
|
|
const LUTu& histRed,
|
|
const LUTu& histGreen,
|
|
const LUTu& histBlue,
|
|
const LUTu& histLuma,
|
|
const LUTu& histToneCurve,
|
|
const LUTu& histLCurve,
|
|
const LUTu& histCCurve,
|
|
const LUTu& histLCAM,
|
|
const LUTu& histCCAM,
|
|
const LUTu& histRedRaw,
|
|
const LUTu& histGreenRaw,
|
|
const LUTu& histBlueRaw,
|
|
const LUTu& histChroma,
|
|
const LUTu& histLRETI,
|
|
int vectorscopeScale,
|
|
const array2D<int>& vectorscopeHC,
|
|
const array2D<int>& vectorscopeHS,
|
|
int waveformScale,
|
|
const array2D<int>& waveformRed,
|
|
const array2D<int>& waveformGreen,
|
|
const array2D<int>& waveformBlue,
|
|
const array2D<int>& waveformLuma
|
|
)
|
|
{
|
|
if (histogramPanel) {
|
|
histogramPanel->histogramChanged(histRed, histGreen, histBlue, histLuma, histChroma, histRedRaw, histGreenRaw, histBlueRaw, vectorscopeScale, vectorscopeHC, vectorscopeHS, waveformScale, waveformRed, waveformGreen, waveformBlue, waveformLuma);
|
|
}
|
|
|
|
tpc->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
|
|
}
|
|
|
|
void EditorPanel::setObservable(rtengine::HistogramObservable* observable)
|
|
{
|
|
histogram_observable = observable;
|
|
}
|
|
|
|
bool EditorPanel::updateHistogram(void) const
|
|
{
|
|
return histogram_scope_type == ScopeType::HISTOGRAM
|
|
|| histogram_scope_type == ScopeType::NONE;
|
|
}
|
|
|
|
bool EditorPanel::updateHistogramRaw(void) const
|
|
{
|
|
return histogram_scope_type == ScopeType::HISTOGRAM_RAW
|
|
|| histogram_scope_type == ScopeType::NONE;
|
|
}
|
|
|
|
bool EditorPanel::updateVectorscopeHC(void) const
|
|
{
|
|
return
|
|
histogram_scope_type == ScopeType::VECTORSCOPE_HC
|
|
|| histogram_scope_type == ScopeType::NONE;
|
|
}
|
|
|
|
bool EditorPanel::updateVectorscopeHS(void) const
|
|
{
|
|
return
|
|
histogram_scope_type == ScopeType::VECTORSCOPE_HS
|
|
|| histogram_scope_type == ScopeType::NONE;
|
|
}
|
|
|
|
bool EditorPanel::updateWaveform(void) const
|
|
{
|
|
return histogram_scope_type == ScopeType::WAVEFORM
|
|
|| histogram_scope_type == ScopeType::PARADE
|
|
|| histogram_scope_type == ScopeType::NONE;
|
|
}
|
|
|
|
void EditorPanel::scopeTypeChanged(ScopeType new_type)
|
|
{
|
|
histogram_scope_type = new_type;
|
|
|
|
if (!histogram_observable) {
|
|
return;
|
|
}
|
|
|
|
// Make sure the new scope is updated since we only actively update the
|
|
// current scope.
|
|
switch (new_type) {
|
|
case ScopeType::HISTOGRAM:
|
|
histogram_observable->requestUpdateHistogram();
|
|
break;
|
|
case ScopeType::HISTOGRAM_RAW:
|
|
histogram_observable->requestUpdateHistogramRaw();
|
|
break;
|
|
case ScopeType::VECTORSCOPE_HC:
|
|
histogram_observable->requestUpdateVectorscopeHC();
|
|
break;
|
|
case ScopeType::VECTORSCOPE_HS:
|
|
histogram_observable->requestUpdateVectorscopeHS();
|
|
break;
|
|
case ScopeType::PARADE:
|
|
case ScopeType::WAVEFORM:
|
|
histogram_observable->requestUpdateWaveform();
|
|
break;
|
|
case ScopeType::NONE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool EditorPanel::CheckSidePanelsVisibility()
|
|
{
|
|
if (tbTopPanel_1) {
|
|
return tbTopPanel_1->get_active() || tbRightPanel_1->get_active() || hidehp->get_active();
|
|
}
|
|
|
|
return tbRightPanel_1->get_active() || hidehp->get_active();
|
|
}
|
|
|
|
void EditorPanel::toggleSidePanels()
|
|
{
|
|
// Maximize preview panel:
|
|
// toggle top AND right AND history panels
|
|
|
|
bool bAllSidePanelsVisible;
|
|
bAllSidePanelsVisible = CheckSidePanelsVisibility();
|
|
|
|
if (tbTopPanel_1) {
|
|
tbTopPanel_1->set_active (!bAllSidePanelsVisible);
|
|
}
|
|
|
|
tbRightPanel_1->set_active (!bAllSidePanelsVisible);
|
|
hidehp->set_active (!bAllSidePanelsVisible);
|
|
|
|
if (!bAllSidePanelsVisible) {
|
|
tbShowHideSidePanels->set_image (*iShowHideSidePanels);
|
|
} else {
|
|
tbShowHideSidePanels->set_image (*iShowHideSidePanels_exit);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::toggleSidePanelsZoomFit()
|
|
{
|
|
toggleSidePanels();
|
|
|
|
// fit image preview
|
|
// !!! TODO this does not want to work... seems to have an effect on a subsequent key press
|
|
// iarea->imageArea->zoomPanel->zoomFitClicked();
|
|
}
|
|
|
|
void EditorPanel::tbShowHideSidePanels_managestate()
|
|
{
|
|
bool bAllSidePanelsVisible;
|
|
bAllSidePanelsVisible = CheckSidePanelsVisibility();
|
|
ShowHideSidePanelsconn.block (true);
|
|
|
|
tbShowHideSidePanels->set_active (!bAllSidePanelsVisible);
|
|
|
|
ShowHideSidePanelsconn.block (false);
|
|
}
|
|
|
|
void EditorPanel::updateExternalEditorWidget(int selectedIndex, const std::vector<ExternalEditor> &editors)
|
|
{
|
|
// Remove the editors.
|
|
while (send_to_external->getEntryCount()) {
|
|
send_to_external->removeEntry(send_to_external->getEntryCount() - 1);
|
|
}
|
|
|
|
// Create new radio button group because they cannot be reused: https://developer-old.gnome.org/gtkmm/3.16/classGtk_1_1RadioButtonGroup.html#details.
|
|
send_to_external_radio_group = Gtk::RadioButtonGroup();
|
|
|
|
// 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, &send_to_external_radio_group);
|
|
} else {
|
|
send_to_external->insertEntry(i, "palette-brush.png", name, &send_to_external_radio_group);
|
|
}
|
|
}
|
|
send_to_external->addEntry("palette-brush.png", M("GENERAL_OTHER"), &send_to_external_radio_group);
|
|
send_to_external->setSelected(selectedIndex);
|
|
send_to_external->show();
|
|
}
|
|
|
|
void EditorPanel::updateProfiles (const Glib::ustring &printerProfile, rtengine::RenderingIntent printerIntent, bool printerBPC)
|
|
{
|
|
}
|
|
|
|
void EditorPanel::updateTPVScrollbar (bool hide)
|
|
{
|
|
tpc->updateTPVScrollbar (hide);
|
|
}
|
|
|
|
void EditorPanel::updateHistogramPosition (int oldPosition, int newPosition)
|
|
{
|
|
|
|
switch (newPosition) {
|
|
case 0:
|
|
|
|
// No histogram
|
|
if (!oldPosition) {
|
|
// An histogram actually exist, we delete it
|
|
delete histogramPanel;
|
|
histogramPanel = nullptr;
|
|
}
|
|
|
|
// else no need to create it
|
|
break;
|
|
|
|
case 1:
|
|
|
|
// Histogram on the left pane
|
|
if (oldPosition == 0) {
|
|
// There was no Histogram before, so we create it
|
|
histogramPanel = Gtk::manage (new HistogramPanel ());
|
|
leftbox->pack1(*histogramPanel, false, false);
|
|
} else if (oldPosition == 2) {
|
|
// The histogram was on the right side, so we move it to the left
|
|
histogramPanel->reference();
|
|
removeIfThere (vboxright, histogramPanel, false);
|
|
leftbox->pack1(*histogramPanel, false, false);
|
|
histogramPanel->unreference();
|
|
}
|
|
|
|
leftbox->set_position(options.histogramHeight);
|
|
histogramPanel->reorder (Gtk::POS_LEFT);
|
|
break;
|
|
|
|
case 2:
|
|
default:
|
|
|
|
// Histogram on the right pane
|
|
if (oldPosition == 0) {
|
|
// There was no Histogram before, so we create it
|
|
histogramPanel = Gtk::manage (new HistogramPanel ());
|
|
vboxright->pack1 (*histogramPanel, false, false);
|
|
} else if (oldPosition == 1) {
|
|
// The histogram was on the left side, so we move it to the right
|
|
histogramPanel->reference();
|
|
removeIfThere (leftbox, histogramPanel, false);
|
|
vboxright->pack1 (*histogramPanel, false, false);
|
|
histogramPanel->unreference();
|
|
}
|
|
|
|
vboxright->set_position(options.histogramHeight);
|
|
histogramPanel->reorder (Gtk::POS_RIGHT);
|
|
break;
|
|
}
|
|
|
|
if (histogramPanel) {
|
|
histogramPanel->setPanelListener(this);
|
|
}
|
|
|
|
iareapanel->imageArea->setPointerMotionHListener (histogramPanel);
|
|
|
|
}
|
|
|
|
void EditorPanel::updateToolPanelToolLocations(
|
|
const std::vector<Glib::ustring> &favorites, bool cloneFavoriteTools)
|
|
{
|
|
if (tpc) {
|
|
tpc->updateToolLocations(favorites, cloneFavoriteTools);
|
|
}
|
|
}
|
|
|
|
void EditorPanel::defaultMonitorProfileChanged (const Glib::ustring &profile_name, bool auto_monitor_profile)
|
|
{
|
|
colorMgmtToolBar->defaultMonitorProfileChanged (profile_name, auto_monitor_profile);
|
|
}
|
|
|