* Use target_include_directories to specify include paths * Specify project root (parent of rtgui and rtengine) as include path * Replace relative includes with normal includes
380 lines
11 KiB
C++
380 lines
11 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* 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 <functional>
|
|
|
|
#include "saveasdlg.h"
|
|
|
|
#include "guiutils.h"
|
|
#include "multilangmgr.h"
|
|
#include "pathutils.h"
|
|
#include "rtimage.h"
|
|
|
|
#include "rtengine/utils.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
Glib::ustring getCurrentFilename(const Gtk::FileChooserWidget* fchooser)
|
|
{
|
|
Glib::ustring res = fchooser->get_filename();
|
|
|
|
// NB: There seem to be a bug in Gtkmm2.22 / FileChooserWidget : if you suppress the filename entry and
|
|
// click on a folder in the list, the filename field is empty but get_filename will return the folder's path :/
|
|
if (Glib::file_test(res, Glib::FILE_TEST_IS_DIR)) {
|
|
res = fchooser->get_current_name();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
}
|
|
|
|
SaveAsDialog::SaveAsDialog (const Glib::ustring &initialDir, Gtk::Window* parent)
|
|
: Gtk::Dialog (M("GENERAL_SAVE"), *parent)
|
|
{
|
|
Gtk::Box* box = get_content_area ();
|
|
|
|
fchooser = Gtk::manage( new Gtk::FileChooserWidget (Gtk::FILE_CHOOSER_ACTION_SAVE) );
|
|
fchooser->set_current_folder (initialDir);
|
|
fchooser->signal_file_activated().connect(sigc::mem_fun(*this, &SaveAsDialog::okPressed));
|
|
|
|
filter_jpg = Gtk::FileFilter::create();
|
|
filter_jpg->set_name(M("SAVEDLG_JPGFILTER"));
|
|
filter_jpg->add_pattern("*.jpg");
|
|
filter_jpg->add_pattern("*.JPG");
|
|
filter_jpg->add_pattern("*.jpeg");
|
|
filter_jpg->add_pattern("*.JPEG");
|
|
filter_jpg->add_pattern("*.jpe");
|
|
filter_jpg->add_pattern("*.JPE");
|
|
|
|
filter_tif = Gtk::FileFilter::create();
|
|
filter_tif->set_name(M("SAVEDLG_JPGFILTER"));
|
|
filter_tif->add_pattern("*.tif");
|
|
filter_tif->add_pattern("*.TIF");
|
|
filter_tif->add_pattern("*.tiff");
|
|
filter_tif->add_pattern("*.TIFF");
|
|
|
|
filter_png = Gtk::FileFilter::create();
|
|
filter_png->set_name(M("SAVEDLG_JPGFILTER"));
|
|
filter_png->add_pattern("*.png");
|
|
filter_png->add_pattern("*.PNG");
|
|
|
|
formatChanged (options.saveFormat.format);
|
|
|
|
// Output Options
|
|
// ~~~~~~~~~~~~~~
|
|
formatOpts = Gtk::manage( new SaveFormatPanel () );
|
|
setExpandAlignProperties(formatOpts, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_START);
|
|
formatOpts->setListener (this);
|
|
|
|
// queue/immediate
|
|
// ~~~~~~~~~~~~~~~
|
|
saveMethod[0] = Gtk::manage( new Gtk::RadioButton (M("SAVEDLG_SAVEIMMEDIATELY")) );
|
|
saveMethod[1] = Gtk::manage( new Gtk::RadioButton (M("SAVEDLG_PUTTOQUEUEHEAD")) );
|
|
saveMethod[2] = Gtk::manage( new Gtk::RadioButton (M("SAVEDLG_PUTTOQUEUETAIL")) );
|
|
|
|
Gtk::RadioButton::Group g = saveMethod[0]->get_group();
|
|
saveMethod[1]->set_group (g);
|
|
saveMethod[2]->set_group (g);
|
|
|
|
if (options.saveMethodNum >= 0 && options.saveMethodNum < 3) {
|
|
saveMethod[options.saveMethodNum]->set_active (true);
|
|
}
|
|
|
|
saveMethod[0]->signal_clicked().connect( sigc::mem_fun(*this, &SaveAsDialog::saveImmediatlyClicked) );
|
|
saveMethod[1]->signal_clicked().connect( sigc::mem_fun(*this, &SaveAsDialog::putToQueueClicked) );
|
|
saveMethod[2]->signal_clicked().connect( sigc::mem_fun(*this, &SaveAsDialog::putToQueueClicked) );
|
|
|
|
// Force output format option
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
forceFormatOpts = Gtk::manage( new Gtk::CheckButton (M("SAVEDLG_FORCEFORMATOPTS")) );
|
|
forceFormatOpts->set_active(options.forceFormatOpts);
|
|
forceFormatOpts->set_sensitive(options.saveMethodNum > 0);
|
|
forceFormatOpts->signal_clicked().connect( sigc::mem_fun(*this, &SaveAsDialog::forceFmtOptsSwitched) );
|
|
// update sensitivity of the SaveFormatPanel
|
|
formatOpts->set_sensitive(options.saveMethodNum == 0 || options.forceFormatOpts);
|
|
|
|
// Unique filename option
|
|
// ~~~~~~~~~~~~~~~~~~~~~~
|
|
autoSuffix = Gtk::manage( new Gtk::CheckButton (M("SAVEDLG_AUTOSUFFIX")) );
|
|
autoSuffix->set_active(options.autoSuffix);
|
|
|
|
// buttons
|
|
// ~~~~~~~
|
|
Gtk::Button* ok = Gtk::manage( new Gtk::Button (M("GENERAL_OK")) );
|
|
Gtk::Button* cancel = Gtk::manage( new Gtk::Button (M("GENERAL_CANCEL")) );
|
|
|
|
ok->set_tooltip_markup (M("TP_SAVEDIALOG_OK_TOOLTIP"));
|
|
|
|
ok->signal_clicked().connect( sigc::mem_fun(*this, &SaveAsDialog::okPressed) );
|
|
cancel->signal_clicked().connect( sigc::mem_fun(*this, &SaveAsDialog::cancelPressed) );
|
|
|
|
// pack everything
|
|
// ~~~~~~~~~~~~~~~
|
|
Gtk::Box* vbox_bottomRight = Gtk::manage(new Gtk::Box(Gtk::ORIENTATION_VERTICAL));
|
|
|
|
// There is no queue in simple mode, so no need to choose
|
|
if (!simpleEditor) {
|
|
vbox_bottomRight->pack_start (*saveMethod[0], Gtk::PACK_SHRINK, 2);
|
|
vbox_bottomRight->pack_start (*saveMethod[1], Gtk::PACK_SHRINK, 2);
|
|
vbox_bottomRight->pack_start (*saveMethod[2], Gtk::PACK_SHRINK, 2);
|
|
vbox_bottomRight->pack_start (*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_HORIZONTAL)), Gtk::PACK_SHRINK, 5);
|
|
}
|
|
|
|
vbox_bottomRight->pack_start (*forceFormatOpts, Gtk::PACK_SHRINK, 4);
|
|
vbox_bottomRight->pack_start (*autoSuffix, Gtk::PACK_SHRINK, 4);
|
|
|
|
Gtk::Box* hbox_bottom = Gtk::manage( new Gtk::Box() );
|
|
hbox_bottom->pack_start (*formatOpts, Gtk::PACK_EXPAND_WIDGET, 2);
|
|
hbox_bottom->pack_start (*Gtk::manage(new Gtk::Separator(Gtk::ORIENTATION_VERTICAL)), Gtk::PACK_SHRINK, 2);
|
|
hbox_bottom->pack_start (*vbox_bottomRight, Gtk::PACK_EXPAND_WIDGET, 2);
|
|
|
|
box->pack_start (*fchooser);
|
|
box->pack_start (*hbox_bottom, Gtk::PACK_SHRINK, 2);
|
|
|
|
get_action_area()->pack_end (*ok, Gtk::PACK_SHRINK, 4);
|
|
get_action_area()->pack_end (*cancel, Gtk::PACK_SHRINK, 4);
|
|
|
|
show_all_children ();
|
|
|
|
formatOpts->init (options.saveFormat);
|
|
|
|
signal_key_press_event().connect( sigc::mem_fun(*this, &SaveAsDialog::keyPressed) );
|
|
}
|
|
|
|
void SaveAsDialog::saveImmediatlyClicked ()
|
|
{
|
|
forceFormatOpts->set_sensitive(false);
|
|
formatOpts->set_sensitive(true);
|
|
}
|
|
|
|
void SaveAsDialog::putToQueueClicked ()
|
|
{
|
|
forceFormatOpts->set_sensitive(true);
|
|
formatOpts->set_sensitive(forceFormatOpts->get_active());
|
|
}
|
|
|
|
void SaveAsDialog::forceFmtOptsSwitched ()
|
|
{
|
|
formatOpts->set_sensitive(forceFormatOpts->get_active());
|
|
}
|
|
|
|
bool SaveAsDialog::getForceFormatOpts ()
|
|
{
|
|
|
|
return forceFormatOpts->get_active();
|
|
}
|
|
|
|
bool SaveAsDialog::getAutoSuffix ()
|
|
{
|
|
|
|
return autoSuffix->get_active();
|
|
}
|
|
|
|
bool SaveAsDialog::getImmediately ()
|
|
{
|
|
|
|
return simpleEditor ? true : saveMethod[0]->get_active ();
|
|
}
|
|
|
|
bool SaveAsDialog::getToHeadOfQueue ()
|
|
{
|
|
|
|
return saveMethod[1]->get_active ();
|
|
}
|
|
|
|
bool SaveAsDialog::getToTailOfQueue ()
|
|
{
|
|
|
|
return saveMethod[2]->get_active ();
|
|
}
|
|
|
|
int SaveAsDialog::getSaveMethodNum ()
|
|
{
|
|
if (simpleEditor) {
|
|
return 0;
|
|
}
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
if (saveMethod[i]->get_active()) {
|
|
return i;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
Glib::ustring SaveAsDialog::getFileName ()
|
|
{
|
|
|
|
return fname;
|
|
}
|
|
|
|
Glib::ustring SaveAsDialog::getDirectory ()
|
|
{
|
|
|
|
return fchooser->get_current_folder ();
|
|
}
|
|
|
|
SaveFormat SaveAsDialog::getFormat ()
|
|
{
|
|
|
|
return formatOpts->getFormat ();
|
|
}
|
|
|
|
void SaveAsDialog::okPressed ()
|
|
{
|
|
fname = getCurrentFilename(fchooser);
|
|
|
|
// Checking if the filename field is empty. The user have to click Cancel if he don't want to specify a filename
|
|
if (fname.empty()) {
|
|
Gtk::MessageDialog(
|
|
*this,
|
|
Glib::ustring("<b>")
|
|
+ M("MAIN_MSG_EMPTYFILENAME")
|
|
+ "</b>",
|
|
true,
|
|
Gtk::MESSAGE_WARNING,
|
|
Gtk::BUTTONS_OK,
|
|
true
|
|
).run();
|
|
return;
|
|
}
|
|
|
|
if (getExtension(fname).empty()) {
|
|
// Extension is either empty or unfamiliar
|
|
fname += '.' + formatOpts->getFormat().format;
|
|
} else if (
|
|
(
|
|
formatOpts->getFormat().format == "jpg"
|
|
&& !rtengine::hasJpegExtension(fname)
|
|
)
|
|
|| (
|
|
formatOpts->getFormat().format == "tif"
|
|
&& !rtengine::hasTiffExtension(fname)
|
|
)
|
|
|| (
|
|
formatOpts->getFormat().format == "png"
|
|
&& !rtengine::hasPngExtension(fname)
|
|
)
|
|
) {
|
|
// Create dialog to warn user that the filename may have two extensions on the end
|
|
Gtk::MessageDialog msgd(
|
|
*this,
|
|
Glib::ustring("<b>")
|
|
+ M("GENERAL_WARNING")
|
|
+ ": "
|
|
+ M("SAVEDLG_WARNFILENAME")
|
|
+ " \""
|
|
+ escapeHtmlChars(Glib::path_get_basename (fname))
|
|
+ '.'
|
|
+ escapeHtmlChars(formatOpts->getFormat().format)
|
|
+ "\"</b>",
|
|
true,
|
|
Gtk::MESSAGE_WARNING,
|
|
Gtk::BUTTONS_OK_CANCEL,
|
|
true
|
|
);
|
|
|
|
if (msgd.run() == Gtk::RESPONSE_OK) {
|
|
fname += "." + formatOpts->getFormat().format;
|
|
} else {
|
|
return;
|
|
}
|
|
}
|
|
|
|
response (Gtk::RESPONSE_OK);
|
|
}
|
|
|
|
void SaveAsDialog::cancelPressed ()
|
|
{
|
|
response (Gtk::RESPONSE_CANCEL);
|
|
}
|
|
|
|
void SaveAsDialog::formatChanged(const Glib::ustring& format)
|
|
{
|
|
const auto sanitize_suffix =
|
|
[this, format](const std::function<bool (const Glib::ustring&)>& has_suffix)
|
|
{
|
|
const Glib::ustring name = getCurrentFilename(fchooser);
|
|
|
|
if (!has_suffix(name)) {
|
|
fchooser->set_current_name(removeExtension(Glib::path_get_basename(name)) + '.' + format);
|
|
}
|
|
};
|
|
|
|
if (format == "jpg") {
|
|
fchooser->set_filter (filter_jpg);
|
|
sanitize_suffix(
|
|
[](const Glib::ustring& filename)
|
|
{
|
|
return rtengine::hasJpegExtension(filename);
|
|
}
|
|
);
|
|
} else if (format == "png") {
|
|
fchooser->set_filter (filter_png);
|
|
sanitize_suffix(
|
|
[](const Glib::ustring& filename)
|
|
{
|
|
return rtengine::hasPngExtension(filename);
|
|
}
|
|
);
|
|
} else if (format == "tif") {
|
|
fchooser->set_filter (filter_tif);
|
|
sanitize_suffix(
|
|
[](const Glib::ustring& filename)
|
|
{
|
|
return rtengine::hasTiffExtension(filename);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
void SaveAsDialog::setInitialFileName (const Glib::ustring& fname)
|
|
{
|
|
this->fname = fname;
|
|
fchooser->set_current_name(fname);
|
|
}
|
|
|
|
void SaveAsDialog::setImagePath (const Glib::ustring& imagePath)
|
|
{
|
|
const auto dirName = Glib::path_get_dirname (imagePath);
|
|
|
|
try {
|
|
fchooser->add_shortcut_folder (dirName);
|
|
} catch (Glib::Error&) {}
|
|
}
|
|
|
|
|
|
bool SaveAsDialog::keyPressed (GdkEventKey* event)
|
|
{
|
|
|
|
bool ctrl = event->state & GDK_CONTROL_MASK;
|
|
|
|
if (ctrl) {
|
|
switch(event->keyval) {
|
|
case GDK_KEY_Return: // Ctrl-Enter equivalent to pressing OK button
|
|
case GDK_KEY_KP_Enter:
|
|
SaveAsDialog::okPressed();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|