* Use mtime as fallback timestamp for files without EXIF data As suggested in #6449, with date-based sorting it can be useful to have at least *some* sort of time-relevant information for EXIF-less files, to prevent them from falling back to getting sorted alphabetically all the time. This commit simply defaults the file timestamp to the file's mtime as returned by g_stat. For annoying reasons, it doesn't suffice to merely forward the timestamp to the FileData structs - we also need to keep track of it inside FilesData to cover the case of a file with 0 frames in it. * Add DateTime to Thumbnail Putting it here facilitate easier sorting without having to re-construct the DateTime on every comparison. To simplify things moving forwards, use the Glib::DateTime struct right away. This struct also contains timezone information, but we don't currently care about timezone - so just use the local timezone as the best approximation. (Nothing currently depends on getting the timezone right, anyway) In addition to the above, this commit also changes the logic to allow generating datetime strings even for files with missing EXIF (which makes sense as a result of the previous commit allowing the use of mtime instead). * Implement file sorting in thumbnail view For simplicity, I decided to only implement the attributes that I could verily easily reach from the existing metadata exported by Thumbnail. Ideally, I would also like to be able to sort by "last modified" but I'm not sure of the best way to reach this from this place in the code. It's worth pointing out that, with the current implementation, the list will not dynamically re-sort itself until you re-select the sorting method - even if you make changes to the files that would otherwise affect the sorting (e.g. changing the rank while sorting by rank). One might even call this a feature, not a bug, since it prevents thumbnails from moving around while you're trying to re-label them. You can always re-select "sort by ..." from the context menu to force a re-sort. Fixes #3317 Co-authored-by: Thanatomanic <6567747+Thanatomanic@users.noreply.github.com>
2165 lines
78 KiB
C++
2165 lines
78 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
* Copyright (c) 2011 Oliver Duis <www.oliverduis.de>
|
|
* Copyright (c) 2011 Michael Ezra <www.michaelezra.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 <algorithm>
|
|
#include <map>
|
|
|
|
#include <glibmm/ustring.h>
|
|
|
|
#include "filebrowser.h"
|
|
|
|
#include "batchqueue.h"
|
|
#include "clipboard.h"
|
|
#include "inspector.h"
|
|
#include "multilangmgr.h"
|
|
#include "options.h"
|
|
#include "paramsedited.h"
|
|
#include "profilestorecombobox.h"
|
|
#include "procparamchangers.h"
|
|
#include "rtimage.h"
|
|
#include "threadutils.h"
|
|
#include "thumbnail.h"
|
|
|
|
#include "../rtengine/dfmanager.h"
|
|
#include "../rtengine/ffmanager.h"
|
|
#include "../rtengine/procparams.h"
|
|
|
|
namespace
|
|
{
|
|
|
|
const Glib::ustring* getOriginalExtension (const ThumbBrowserEntryBase* entry)
|
|
{
|
|
// We use the parsed extensions as a priority list,
|
|
// i.e. what comes earlier in the list is considered an original of what comes later.
|
|
typedef std::vector<Glib::ustring> ExtensionVector;
|
|
typedef ExtensionVector::const_iterator ExtensionIterator;
|
|
|
|
const ExtensionVector& originalExtensions = options.parsedExtensions;
|
|
|
|
// Extract extension from basename
|
|
const Glib::ustring basename = Glib::path_get_basename (entry->filename.lowercase());
|
|
|
|
const Glib::ustring::size_type pos = basename.find_last_of ('.');
|
|
if (pos >= basename.length () - 1) {
|
|
return nullptr;
|
|
}
|
|
|
|
const Glib::ustring extension = basename.substr (pos + 1);
|
|
|
|
// Try to find a matching original extension
|
|
for (ExtensionIterator originalExtension = originalExtensions.begin(); originalExtension != originalExtensions.end(); ++originalExtension) {
|
|
if (*originalExtension == extension) {
|
|
return &*originalExtension;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
ThumbBrowserEntryBase* selectOriginalEntry (ThumbBrowserEntryBase* original, ThumbBrowserEntryBase* candidate)
|
|
{
|
|
if (original == nullptr) {
|
|
return candidate;
|
|
}
|
|
|
|
// The candidate will become the new original, if it has an original extension
|
|
// and if its extension is higher in the list than the old original.
|
|
if (const Glib::ustring* candidateExtension = getOriginalExtension (candidate)) {
|
|
if (const Glib::ustring* originalExtension = getOriginalExtension (original)) {
|
|
return candidateExtension < originalExtension ? candidate : original;
|
|
}
|
|
}
|
|
|
|
return original;
|
|
}
|
|
|
|
void findOriginalEntries (const std::vector<ThumbBrowserEntryBase*>& entries)
|
|
{
|
|
// Sort all entries into buckets by basename without extension
|
|
std::map<Glib::ustring, std::vector<ThumbBrowserEntryBase*>> byBasename;
|
|
|
|
for (const auto entry : entries) {
|
|
const auto basename = Glib::path_get_basename(entry->filename.lowercase());
|
|
|
|
const auto pos = basename.find_last_of('.');
|
|
if (pos >= basename.length() - 1) {
|
|
entry->setOriginal(nullptr);
|
|
continue;
|
|
}
|
|
|
|
const auto withoutExtension = basename.substr(0, pos);
|
|
|
|
byBasename[withoutExtension].push_back(entry);
|
|
}
|
|
|
|
// Find the original image for each bucket
|
|
for (const auto& bucket : byBasename) {
|
|
const auto& lentries = bucket.second;
|
|
ThumbBrowserEntryBase* original = nullptr;
|
|
|
|
// Select the most likely original in a first pass...
|
|
for (const auto entry : lentries) {
|
|
original = selectOriginalEntry(original, entry);
|
|
}
|
|
|
|
// ...and link all other images to it in a second pass.
|
|
for (const auto entry : lentries) {
|
|
entry->setOriginal(entry != original ? original : nullptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
FileBrowser::FileBrowser () :
|
|
menuLabel(nullptr),
|
|
miOpenDefaultViewer(nullptr),
|
|
selectDF(nullptr),
|
|
thisIsDF(nullptr),
|
|
autoDF(nullptr),
|
|
selectFF(nullptr),
|
|
thisIsFF(nullptr),
|
|
autoFF(nullptr),
|
|
clearFromCache(nullptr),
|
|
clearFromCacheFull(nullptr),
|
|
colorLabel_actionData(nullptr),
|
|
bppcl(nullptr),
|
|
tbl(nullptr),
|
|
numFiltered(0),
|
|
exportPanel(nullptr)
|
|
{
|
|
session_id_ = 0;
|
|
|
|
ProfileStore::getInstance()->addListener(this);
|
|
|
|
int p = 0;
|
|
pmenu = new Gtk::Menu ();
|
|
pmenu->attach (*Gtk::manage(open = new Gtk::MenuItem (M("FILEBROWSER_POPUPOPEN"))), 0, 1, p, p + 1);
|
|
p++;
|
|
if (options.inspectorWindow) {
|
|
pmenu->attach (*Gtk::manage(inspect = new Gtk::MenuItem (M("FILEBROWSER_POPUPINSPECT"))), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
pmenu->attach (*Gtk::manage(develop = new MyImageMenuItem (M("FILEBROWSER_POPUPPROCESS"), "gears.png")), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(developfast = new Gtk::MenuItem (M("FILEBROWSER_POPUPPROCESSFAST"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(selall = new Gtk::MenuItem (M("FILEBROWSER_POPUPSELECTALL"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
/***********************
|
|
* sort
|
|
***********************/
|
|
const std::array<std::string, 2> cnameSortOrders = {
|
|
M("SORT_ASCENDING"),
|
|
M("SORT_DESCENDING"),
|
|
};
|
|
|
|
const std::array<std::string, Options::SORT_METHOD_COUNT> cnameSortMethods = {
|
|
M("SORT_BY_NAME"),
|
|
M("SORT_BY_DATE"),
|
|
M("SORT_BY_EXIF"),
|
|
M("SORT_BY_RANK"),
|
|
M("SORT_BY_LABEL"),
|
|
};
|
|
|
|
pmenu->attach (*Gtk::manage(menuSort = new Gtk::MenuItem (M("FILEBROWSER_POPUPSORTBY"))), 0, 1, p, p + 1);
|
|
p++;
|
|
Gtk::Menu* submenuSort = Gtk::manage (new Gtk::Menu ());
|
|
Gtk::RadioButtonGroup sortOrderGroup, sortMethodGroup;
|
|
for (size_t i = 0; i < cnameSortOrders.size(); i++) {
|
|
submenuSort->attach (*Gtk::manage(sortOrder[i] = new Gtk::RadioMenuItem (sortOrderGroup, cnameSortOrders[i])), 0, 1, p, p + 1);
|
|
p++;
|
|
sortOrder[i]->set_active (i == options.sortDescending);
|
|
}
|
|
submenuSort->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
for (size_t i = 0; i < cnameSortMethods.size(); i++) {
|
|
submenuSort->attach (*Gtk::manage(sortMethod[i] = new Gtk::RadioMenuItem (sortMethodGroup, cnameSortMethods[i])), 0, 1, p, p + 1);
|
|
p++;
|
|
sortMethod[i]->set_active (i == options.sortMethod);
|
|
}
|
|
submenuSort->show_all ();
|
|
menuSort->set_submenu (*submenuSort);
|
|
|
|
/***********************
|
|
* rank
|
|
***********************/
|
|
if (options.menuGroupRank) {
|
|
pmenu->attach (*Gtk::manage(menuRank = new Gtk::MenuItem (M("FILEBROWSER_POPUPRANK"))), 0, 1, p, p + 1);
|
|
p++;
|
|
Gtk::Menu* submenuRank = Gtk::manage (new Gtk::Menu ());
|
|
submenuRank->attach (*Gtk::manage(rank[0] = new Gtk::MenuItem (M("FILEBROWSER_POPUPUNRANK"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
for (int i = 1; i <= 5; i++) {
|
|
submenuRank->attach (*Gtk::manage(rank[i] = new Gtk::MenuItem (M(Glib::ustring::compose("%1%2", "FILEBROWSER_POPUPRANK", i)))), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
submenuRank->show_all ();
|
|
menuRank->set_submenu (*submenuRank);
|
|
} else {
|
|
pmenu->attach (*Gtk::manage(rank[0] = new Gtk::MenuItem (M("FILEBROWSER_POPUPUNRANK"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
for (int i = 1; i <= 5; i++) {
|
|
pmenu->attach (*Gtk::manage(rank[i] = new Gtk::MenuItem (M(Glib::ustring::compose("%1%2", "FILEBROWSER_POPUPRANK", i)))), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
if (!options.menuGroupRank || !options.menuGroupLabel) { // separate Rank and Color Labels if either is not grouped
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
}
|
|
|
|
p++;
|
|
|
|
/***********************
|
|
* color labels
|
|
***********************/
|
|
|
|
// Thumbnail context menu
|
|
// Similar image arrays in filecatalog.cc
|
|
std::array<std::string, 6> clabelActiveIcons = {"circle-empty-gray-small.png", "circle-red-small.png", "circle-yellow-small.png", "circle-green-small.png", "circle-blue-small.png", "circle-purple-small.png"};
|
|
std::array<std::string, 6> clabelInactiveIcons = {"circle-empty-darkgray-small.png", "circle-empty-red-small.png", "circle-empty-yellow-small.png", "circle-empty-green-small.png", "circle-empty-blue-small.png", "circle-empty-purple-small.png"};
|
|
|
|
if (options.menuGroupLabel) {
|
|
pmenu->attach (*Gtk::manage(menuLabel = new Gtk::MenuItem (M("FILEBROWSER_POPUPCOLORLABEL"))), 0, 1, p, p + 1);
|
|
p++;
|
|
Gtk::Menu* submenuLabel = Gtk::manage (new Gtk::Menu ());
|
|
|
|
for (int i = 0; i <= 5; i++) {
|
|
submenuLabel->attach(*Gtk::manage(colorlabel[i] = new MyImageMenuItem(M(Glib::ustring::compose("%1%2", "FILEBROWSER_POPUPCOLORLABEL", i)), clabelActiveIcons[i])), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
submenuLabel->show_all ();
|
|
menuLabel->set_submenu (*submenuLabel);
|
|
} else {
|
|
for (int i = 0; i <= 5; i++) {
|
|
pmenu->attach(*Gtk::manage(colorlabel[i] = new MyImageMenuItem(M(Glib::ustring::compose("%1%2", "FILEBROWSER_POPUPCOLORLABEL", i)), clabelInactiveIcons[i])), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
/***********************
|
|
* external programs
|
|
* *********************/
|
|
#if defined(WIN32)
|
|
Gtk::manage(miOpenDefaultViewer = new Gtk::MenuItem (M("FILEBROWSER_OPENDEFAULTVIEWER")));
|
|
#endif
|
|
|
|
// Build a list of menu items
|
|
mMenuExtProgs.clear();
|
|
amiExtProg = nullptr;
|
|
|
|
for (const auto& action : extProgStore->getActions ()) {
|
|
if (action.target == 1 || action.target == 2) {
|
|
mMenuExtProgs[action.getFullName ()] = &action;
|
|
}
|
|
}
|
|
|
|
// Attach them to menu
|
|
if (!mMenuExtProgs.empty() || miOpenDefaultViewer) {
|
|
amiExtProg = new Gtk::MenuItem*[mMenuExtProgs.size()];
|
|
int itemNo = 0;
|
|
|
|
if (options.menuGroupExtProg) {
|
|
pmenu->attach (*Gtk::manage(menuExtProg = new Gtk::MenuItem (M("FILEBROWSER_EXTPROGMENU"))), 0, 1, p, p + 1);
|
|
p++;
|
|
Gtk::Menu* submenuExtProg = Gtk::manage (new Gtk::Menu());
|
|
|
|
#ifdef WIN32
|
|
if (miOpenDefaultViewer) {
|
|
submenuExtProg->attach (*miOpenDefaultViewer, 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
#endif
|
|
for (auto it = mMenuExtProgs.begin(); it != mMenuExtProgs.end(); it++, itemNo++) {
|
|
submenuExtProg->attach (*Gtk::manage(amiExtProg[itemNo] = new Gtk::MenuItem ((*it).first)), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
submenuExtProg->show_all ();
|
|
menuExtProg->set_submenu (*submenuExtProg);
|
|
} else {
|
|
#ifdef WIN32
|
|
if (miOpenDefaultViewer) {
|
|
pmenu->attach (*miOpenDefaultViewer, 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
#endif
|
|
for (auto it = mMenuExtProgs.begin(); it != mMenuExtProgs.end(); it++, itemNo++) {
|
|
pmenu->attach (*Gtk::manage(amiExtProg[itemNo] = new Gtk::MenuItem ((*it).first)), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
}
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
/***********************
|
|
* File Operations
|
|
* *********************/
|
|
if (options.menuGroupFileOperations) {
|
|
pmenu->attach (*Gtk::manage(menuFileOperations = new Gtk::MenuItem (M("FILEBROWSER_POPUPFILEOPERATIONS"))), 0, 1, p, p + 1);
|
|
p++;
|
|
Gtk::Menu* submenuFileOperations = Gtk::manage (new Gtk::Menu ());
|
|
|
|
submenuFileOperations->attach (*Gtk::manage(trash = new Gtk::MenuItem (M("FILEBROWSER_POPUPTRASH"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(untrash = new Gtk::MenuItem (M("FILEBROWSER_POPUPUNTRASH"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(rename = new Gtk::MenuItem (M("FILEBROWSER_POPUPRENAME"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(remove = new Gtk::MenuItem (M("FILEBROWSER_POPUPREMOVE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(removeInclProc = new Gtk::MenuItem (M("FILEBROWSER_POPUPREMOVEINCLPROC"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(copyTo = new Gtk::MenuItem (M("FILEBROWSER_POPUPCOPYTO"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFileOperations->attach (*Gtk::manage(moveTo = new Gtk::MenuItem (M("FILEBROWSER_POPUPMOVETO"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
submenuFileOperations->show_all ();
|
|
menuFileOperations->set_submenu (*submenuFileOperations);
|
|
} else {
|
|
pmenu->attach (*Gtk::manage(trash = new Gtk::MenuItem (M("FILEBROWSER_POPUPTRASH"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(untrash = new Gtk::MenuItem (M("FILEBROWSER_POPUPUNTRASH"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(rename = new Gtk::MenuItem (M("FILEBROWSER_POPUPRENAME"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(remove = new Gtk::MenuItem (M("FILEBROWSER_POPUPREMOVE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(removeInclProc = new Gtk::MenuItem (M("FILEBROWSER_POPUPREMOVEINCLPROC"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(copyTo = new Gtk::MenuItem (M("FILEBROWSER_POPUPCOPYTO"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(moveTo = new Gtk::MenuItem (M("FILEBROWSER_POPUPMOVETO"))), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
/***********************
|
|
* Profile Operations
|
|
* *********************/
|
|
if (options.menuGroupProfileOperations) {
|
|
pmenu->attach (*Gtk::manage(menuProfileOperations = new Gtk::MenuItem (M("FILEBROWSER_POPUPPROFILEOPERATIONS"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
Gtk::Menu* submenuProfileOperations = Gtk::manage (new Gtk::Menu ());
|
|
|
|
submenuProfileOperations->attach (*Gtk::manage(copyprof = new Gtk::MenuItem (M("FILEBROWSER_COPYPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuProfileOperations->attach (*Gtk::manage(pasteprof = new Gtk::MenuItem (M("FILEBROWSER_PASTEPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuProfileOperations->attach (*Gtk::manage(partpasteprof = new Gtk::MenuItem (M("FILEBROWSER_PARTIALPASTEPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuProfileOperations->attach (*Gtk::manage(applyprof = new Gtk::MenuItem (M("FILEBROWSER_APPLYPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuProfileOperations->attach (*Gtk::manage(applypartprof = new Gtk::MenuItem (M("FILEBROWSER_APPLYPROFILE_PARTIAL"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuProfileOperations->attach (*Gtk::manage(resetdefaultprof = new Gtk::MenuItem (M("FILEBROWSER_RESETDEFAULTPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuProfileOperations->attach (*Gtk::manage(clearprof = new Gtk::MenuItem (M("FILEBROWSER_CLEARPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
submenuProfileOperations->show_all ();
|
|
menuProfileOperations->set_submenu (*submenuProfileOperations);
|
|
} else {
|
|
pmenu->attach (*Gtk::manage(copyprof = new Gtk::MenuItem (M("FILEBROWSER_COPYPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(pasteprof = new Gtk::MenuItem (M("FILEBROWSER_PASTEPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(partpasteprof = new Gtk::MenuItem (M("FILEBROWSER_PARTIALPASTEPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(applyprof = new Gtk::MenuItem (M("FILEBROWSER_APPLYPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(applypartprof = new Gtk::MenuItem (M("FILEBROWSER_APPLYPROFILE_PARTIAL"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(resetdefaultprof = new Gtk::MenuItem (M("FILEBROWSER_RESETDEFAULTPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(clearprof = new Gtk::MenuItem (M("FILEBROWSER_CLEARPROFILE"))), 0, 1, p, p + 1);
|
|
p++;
|
|
}
|
|
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(menuDF = new Gtk::MenuItem (M("FILEBROWSER_DARKFRAME"))), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(menuFF = new Gtk::MenuItem (M("FILEBROWSER_FLATFIELD"))), 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p + 1);
|
|
p++;
|
|
pmenu->attach (*Gtk::manage(cachemenu = new Gtk::MenuItem (M("FILEBROWSER_CACHE"))), 0, 1, p, p + 1);
|
|
|
|
pmenu->show_all ();
|
|
|
|
/***********************
|
|
* Accelerators
|
|
* *********************/
|
|
pmaccelgroup = Gtk::AccelGroup::create ();
|
|
pmenu->set_accel_group (pmaccelgroup);
|
|
selall->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_a, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
|
|
trash->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_Delete, (Gdk::ModifierType)0, Gtk::ACCEL_VISIBLE);
|
|
untrash->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_Delete, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
|
|
open->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_Return, (Gdk::ModifierType)0, Gtk::ACCEL_VISIBLE);
|
|
if (options.inspectorWindow)
|
|
inspect->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_f, (Gdk::ModifierType)0, Gtk::ACCEL_VISIBLE);
|
|
develop->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_B, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
|
|
developfast->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_B, Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
|
|
copyprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_C, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
|
|
pasteprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_V, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
|
|
partpasteprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_V, Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
|
|
copyTo->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_C, Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
|
|
moveTo->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_M, Gdk::CONTROL_MASK | Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
|
|
|
|
// Bind to event handlers
|
|
open->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), open));
|
|
|
|
if (options.inspectorWindow) {
|
|
inspect->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), inspect));
|
|
}
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
sortOrder[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), sortOrder[i]));
|
|
}
|
|
|
|
for (int i = 0; i < Options::SORT_METHOD_COUNT; i++) {
|
|
sortMethod[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), sortMethod[i]));
|
|
}
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
rank[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), rank[i]));
|
|
}
|
|
|
|
for (int i = 0; i < 6; i++) {
|
|
colorlabel[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), colorlabel[i]));
|
|
}
|
|
|
|
for (size_t i = 0; i < mMenuExtProgs.size(); i++) {
|
|
amiExtProg[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), amiExtProg[i]));
|
|
}
|
|
|
|
#ifdef WIN32
|
|
if (miOpenDefaultViewer) {
|
|
miOpenDefaultViewer->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), miOpenDefaultViewer));
|
|
}
|
|
#endif
|
|
|
|
trash->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), trash));
|
|
untrash->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), untrash));
|
|
develop->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), develop));
|
|
developfast->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), developfast));
|
|
rename->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), rename));
|
|
remove->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), remove));
|
|
removeInclProc->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), removeInclProc));
|
|
selall->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), selall));
|
|
copyTo->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), copyTo));
|
|
moveTo->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), moveTo));
|
|
copyprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), copyprof));
|
|
pasteprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), pasteprof));
|
|
partpasteprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), partpasteprof));
|
|
applyprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), applyprof));
|
|
applypartprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), applypartprof));
|
|
resetdefaultprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), resetdefaultprof));
|
|
clearprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), clearprof));
|
|
cachemenu->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), cachemenu));
|
|
|
|
// A separate pop-up menu for Color Labels
|
|
int c = 0;
|
|
pmenuColorLabels = new Gtk::Menu();
|
|
|
|
for (int i = 0; i <= 5; i++) {
|
|
pmenuColorLabels->attach(*Gtk::manage(colorlabel_pop[i] = new MyImageMenuItem(M(Glib::ustring::compose("%1%2", "FILEBROWSER_POPUPCOLORLABEL", i)), clabelActiveIcons[i])), 0, 1, c, c + 1);
|
|
c++;
|
|
}
|
|
|
|
pmenuColorLabels->show_all();
|
|
|
|
// Has to be located after creation of applyprof and applypartprof
|
|
updateProfileList ();
|
|
|
|
// Bind to event handlers
|
|
for (int i = 0; i <= 5; i++) {
|
|
colorlabel_pop[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuColorlabelActivated), colorlabel_pop[i]));
|
|
}
|
|
}
|
|
|
|
FileBrowser::~FileBrowser ()
|
|
{
|
|
idle_register.destroy();
|
|
|
|
ProfileStore::getInstance()->removeListener(this);
|
|
delete pmenu;
|
|
delete pmenuColorLabels;
|
|
delete[] amiExtProg;
|
|
}
|
|
|
|
void FileBrowser::rightClicked ()
|
|
{
|
|
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
trash->set_sensitive (false);
|
|
untrash->set_sensitive (false);
|
|
|
|
for (size_t i = 0; i < selected.size(); i++)
|
|
if ((static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->getStage()) {
|
|
untrash->set_sensitive (true);
|
|
break;
|
|
}
|
|
|
|
for (size_t i = 0; i < selected.size(); i++)
|
|
if (!(static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->getStage()) {
|
|
trash->set_sensitive (true);
|
|
break;
|
|
}
|
|
|
|
pasteprof->set_sensitive (clipboard.hasProcParams());
|
|
partpasteprof->set_sensitive (clipboard.hasProcParams());
|
|
copyprof->set_sensitive (selected.size() == 1);
|
|
clearprof->set_sensitive (!selected.empty());
|
|
copyTo->set_sensitive (!selected.empty());
|
|
moveTo->set_sensitive (!selected.empty());
|
|
}
|
|
|
|
// submenuDF
|
|
int p = 0;
|
|
Gtk::Menu* submenuDF = Gtk::manage (new Gtk::Menu ());
|
|
submenuDF->attach (*Gtk::manage(selectDF = new Gtk::MenuItem (M("FILEBROWSER_SELECTDARKFRAME"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuDF->attach (*Gtk::manage(autoDF = new Gtk::MenuItem (M("FILEBROWSER_AUTODARKFRAME"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuDF->attach (*Gtk::manage(thisIsDF = new Gtk::MenuItem (M("FILEBROWSER_MOVETODARKFDIR"))), 0, 1, p, p + 1);
|
|
selectDF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), selectDF));
|
|
autoDF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), autoDF));
|
|
thisIsDF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), thisIsDF ));
|
|
submenuDF->show_all ();
|
|
menuDF->set_submenu (*submenuDF);
|
|
|
|
// submenuFF
|
|
p = 0;
|
|
Gtk::Menu* submenuFF = Gtk::manage (new Gtk::Menu ());
|
|
submenuFF->attach (*Gtk::manage(selectFF = new Gtk::MenuItem (M("FILEBROWSER_SELECTFLATFIELD"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFF->attach (*Gtk::manage(autoFF = new Gtk::MenuItem (M("FILEBROWSER_AUTOFLATFIELD"))), 0, 1, p, p + 1);
|
|
p++;
|
|
submenuFF->attach (*Gtk::manage(thisIsFF = new Gtk::MenuItem (M("FILEBROWSER_MOVETOFLATFIELDDIR"))), 0, 1, p, p + 1);
|
|
selectFF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), selectFF));
|
|
autoFF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), autoFF));
|
|
thisIsFF->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), thisIsFF ));
|
|
submenuFF->show_all ();
|
|
menuFF->set_submenu (*submenuFF);
|
|
|
|
// build cache sub menu
|
|
p = 0;
|
|
Gtk::Menu* cachesubmenu = Gtk::manage (new Gtk::Menu ());
|
|
cachesubmenu->attach (*Gtk::manage(clearFromCache = new Gtk::MenuItem (M("FILEBROWSER_CACHECLEARFROMPARTIAL"))), 0, 1, p, p + 1);
|
|
p++;
|
|
cachesubmenu->attach (*Gtk::manage(clearFromCacheFull = new Gtk::MenuItem (M("FILEBROWSER_CACHECLEARFROMFULL"))), 0, 1, p, p + 1);
|
|
p++;
|
|
clearFromCache->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), clearFromCache));
|
|
clearFromCacheFull->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), clearFromCacheFull));
|
|
cachesubmenu->show_all ();
|
|
cachemenu->set_submenu (*cachesubmenu);
|
|
|
|
pmenu->popup (3, this->eventTime);
|
|
}
|
|
|
|
void FileBrowser::doubleClicked (ThumbBrowserEntryBase* entry)
|
|
{
|
|
|
|
if (tbl && entry) {
|
|
std::vector<Thumbnail*> entries;
|
|
entries.push_back ((static_cast<FileBrowserEntry*>(entry))->thumbnail);
|
|
tbl->openRequested (entries);
|
|
}
|
|
}
|
|
|
|
void FileBrowser::addEntry (FileBrowserEntry* entry)
|
|
{
|
|
entry->setParent(this);
|
|
|
|
const unsigned int sid = session_id();
|
|
|
|
idle_register.add(
|
|
[this, entry, sid]() -> bool
|
|
{
|
|
if (sid != session_id()) {
|
|
delete entry;
|
|
} else {
|
|
addEntry_(entry);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
);
|
|
}
|
|
|
|
void FileBrowser::addEntry_ (FileBrowserEntry* entry)
|
|
{
|
|
entry->selected = false;
|
|
entry->drawable = false;
|
|
entry->framed = editedFiles.find(entry->filename) != editedFiles.end();
|
|
|
|
// add button set to the thumbbrowserentry
|
|
entry->addButtonSet(new FileThumbnailButtonSet(entry));
|
|
entry->getThumbButtonSet()->setRank(entry->thumbnail->getRank());
|
|
entry->getThumbButtonSet()->setColorLabel(entry->thumbnail->getColorLabel());
|
|
entry->getThumbButtonSet()->setInTrash(entry->thumbnail->getStage());
|
|
entry->getThumbButtonSet()->setButtonListener(this);
|
|
entry->resize(getThumbnailHeight());
|
|
entry->filtered = !checkFilter(entry);
|
|
insertEntry(entry);
|
|
}
|
|
|
|
FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname)
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
for (std::vector<ThumbBrowserEntryBase*>::iterator i = fd.begin(); i != fd.end(); ++i)
|
|
if ((*i)->filename == fname) {
|
|
ThumbBrowserEntryBase* entry = *i;
|
|
entry->selected = false;
|
|
fd.erase (i);
|
|
std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), entry);
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
if (j != selected.end()) {
|
|
if (checkFilter (*j)) {
|
|
numFiltered--;
|
|
}
|
|
|
|
selected.erase (j);
|
|
notifySelectionListener ();
|
|
}
|
|
|
|
if (lastClicked == entry) {
|
|
lastClicked = nullptr;
|
|
}
|
|
|
|
redraw ();
|
|
|
|
return (static_cast<FileBrowserEntry*>(entry));
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void FileBrowser::close ()
|
|
{
|
|
++session_id_;
|
|
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
selected.clear ();
|
|
anchor = nullptr;
|
|
|
|
MYWRITERLOCK_RELEASE(l); // notifySelectionListener will need read access!
|
|
|
|
notifySelectionListener ();
|
|
|
|
MYWRITERLOCK_ACQUIRE(l);
|
|
|
|
// The listener merges parameters with old values, so delete afterwards
|
|
for (size_t i = 0; i < fd.size(); i++) {
|
|
delete fd.at(i);
|
|
}
|
|
|
|
fd.clear ();
|
|
}
|
|
|
|
lastClicked = nullptr;
|
|
}
|
|
|
|
void FileBrowser::menuColorlabelActivated (Gtk::MenuItem* m)
|
|
{
|
|
|
|
std::vector<FileBrowserEntry*> tbe;
|
|
tbe.push_back (static_cast<FileBrowserEntry*>(colorLabel_actionData));
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
if (m == colorlabel_pop[i]) {
|
|
colorlabelRequested (tbe, i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void FileBrowser::menuItemActivated (Gtk::MenuItem* m)
|
|
{
|
|
std::vector<FileBrowserEntry*> mselected;
|
|
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
|
|
}
|
|
}
|
|
|
|
|
|
if (!tbl || (m != selall && mselected.empty()) ) {
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
if (m == sortOrder[i]) {
|
|
sortOrderRequested (i);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < Options::SORT_METHOD_COUNT; i++)
|
|
if (m == sortMethod[i]) {
|
|
sortMethodRequested (i);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
if (m == rank[i]) {
|
|
rankingRequested (mselected, i);
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 6; i++)
|
|
if (m == colorlabel[i]) {
|
|
colorlabelRequested (mselected, i);
|
|
return;
|
|
}
|
|
|
|
for (size_t j = 0; j < mMenuExtProgs.size(); j++) {
|
|
if (m == amiExtProg[j]) {
|
|
const auto pAct = mMenuExtProgs[m->get_label()];
|
|
|
|
// Build vector of all file names
|
|
std::vector<Glib::ustring> selFileNames;
|
|
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
Glib::ustring fn = mselected[i]->thumbnail->getFileName();
|
|
|
|
// Maybe batch processed version
|
|
if (pAct->target == 2) {
|
|
fn = Glib::ustring::compose ("%1.%2", BatchQueue::calcAutoFileNameBase(fn), options.saveFormatBatch.format);
|
|
}
|
|
|
|
selFileNames.push_back(fn);
|
|
}
|
|
|
|
pAct->execute (selFileNames);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (m == open) {
|
|
openRequested(mselected);
|
|
} else if (options.inspectorWindow && m == inspect) {
|
|
inspectRequested(mselected);
|
|
} else if (m == remove) {
|
|
tbl->deleteRequested (mselected, false, true);
|
|
} else if (m == removeInclProc) {
|
|
tbl->deleteRequested (mselected, true, true);
|
|
} else if (m == trash) {
|
|
toTrashRequested (mselected);
|
|
} else if (m == untrash) {
|
|
fromTrashRequested (mselected);
|
|
}
|
|
|
|
else if (m == develop) {
|
|
tbl->developRequested (mselected, false);
|
|
} else if (m == developfast) {
|
|
if (exportPanel) {
|
|
// force saving export panel settings
|
|
exportPanel->setExportPanelListener(nullptr);
|
|
exportPanel->FastExportPressed();
|
|
exportPanel->setExportPanelListener(this);
|
|
}
|
|
tbl->developRequested (mselected, true);
|
|
}
|
|
|
|
else if (m == rename) {
|
|
tbl->renameRequested (mselected);
|
|
} else if (m == selall) {
|
|
lastClicked = nullptr;
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
selected.clear();
|
|
|
|
for (size_t i = 0; i < fd.size(); ++i) {
|
|
if (checkFilter(fd[i])) {
|
|
fd[i]->selected = true;
|
|
selected.push_back(fd[i]);
|
|
}
|
|
}
|
|
if (!anchor && !selected.empty()) {
|
|
anchor = selected[0];
|
|
}
|
|
}
|
|
queue_draw ();
|
|
notifySelectionListener();
|
|
} else if( m == copyTo) {
|
|
tbl->copyMoveRequested (mselected, false);
|
|
}
|
|
|
|
else if( m == moveTo) {
|
|
tbl->copyMoveRequested (mselected, true);
|
|
}
|
|
|
|
else if (m == autoDF) {
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
rtengine::procparams::ProcParams pp = mselected[i]->thumbnail->getProcParams();
|
|
pp.raw.df_autoselect = true;
|
|
pp.raw.dark_frame.clear();
|
|
mselected[i]->thumbnail->setProcParams(pp, nullptr, FILEBROWSER, false);
|
|
}
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
|
|
} else if (m == selectDF) {
|
|
if( !mselected.empty() ) {
|
|
rtengine::procparams::ProcParams pp = mselected[0]->thumbnail->getProcParams();
|
|
Gtk::FileChooserDialog fc (getToplevelWindow (this), "Dark Frame", Gtk::FILE_CHOOSER_ACTION_OPEN );
|
|
bindCurrentFolder (fc, options.lastDarkframeDir);
|
|
fc.add_button( M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL);
|
|
fc.add_button( M("GENERAL_APPLY"), Gtk::RESPONSE_APPLY);
|
|
|
|
if(!pp.raw.dark_frame.empty()) {
|
|
fc.set_filename( pp.raw.dark_frame );
|
|
}
|
|
|
|
if( fc.run() == Gtk::RESPONSE_APPLY ) {
|
|
if (bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
rtengine::procparams::ProcParams lpp = mselected[i]->thumbnail->getProcParams();
|
|
lpp.raw.dark_frame = fc.get_filename();
|
|
lpp.raw.df_autoselect = false;
|
|
mselected[i]->thumbnail->setProcParams(lpp, nullptr, FILEBROWSER, false);
|
|
}
|
|
|
|
if (bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
}
|
|
}
|
|
} else if( m == thisIsDF) {
|
|
if( !options.rtSettings.darkFramesPath.empty()) {
|
|
if (Gio::File::create_for_path(options.rtSettings.darkFramesPath)->query_exists() ) {
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path ( mselected[i]->filename );
|
|
|
|
if( !file ) {
|
|
continue;
|
|
}
|
|
|
|
Glib::ustring destName = options.rtSettings.darkFramesPath + "/" + file->get_basename();
|
|
Glib::RefPtr<Gio::File> dest = Gio::File::create_for_path ( destName );
|
|
file->move( dest );
|
|
}
|
|
|
|
// Reinit cache
|
|
rtengine::DFManager::getInstance().init( options.rtSettings.darkFramesPath );
|
|
} else {
|
|
// Target directory creation failed, we clear the darkFramesPath setting
|
|
options.rtSettings.darkFramesPath.clear();
|
|
Glib::ustring msg_ = Glib::ustring::compose (M("MAIN_MSG_PATHDOESNTEXIST"), escapeHtmlChars(options.rtSettings.darkFramesPath))
|
|
+ "\n\n" + M("MAIN_MSG_OPERATIONCANCELLED");
|
|
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.set_title(M("TP_DARKFRAME_LABEL"));
|
|
msgd.run ();
|
|
}
|
|
} else {
|
|
Glib::ustring msg_ = M("MAIN_MSG_SETPATHFIRST") + "\n\n" + M("MAIN_MSG_OPERATIONCANCELLED");
|
|
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.set_title(M("TP_DARKFRAME_LABEL"));
|
|
msgd.run ();
|
|
}
|
|
} else if (m == autoFF) {
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
rtengine::procparams::ProcParams pp = mselected[i]->thumbnail->getProcParams();
|
|
pp.raw.ff_AutoSelect = true;
|
|
pp.raw.ff_file.clear();
|
|
mselected[i]->thumbnail->setProcParams(pp, nullptr, FILEBROWSER, false);
|
|
}
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
} else if (m == selectFF) {
|
|
if( !mselected.empty() ) {
|
|
rtengine::procparams::ProcParams pp = mselected[0]->thumbnail->getProcParams();
|
|
Gtk::FileChooserDialog fc (getToplevelWindow (this), "Flat Field", Gtk::FILE_CHOOSER_ACTION_OPEN );
|
|
bindCurrentFolder (fc, options.lastFlatfieldDir);
|
|
fc.add_button( M("GENERAL_CANCEL"), Gtk::RESPONSE_CANCEL);
|
|
fc.add_button( M("GENERAL_APPLY"), Gtk::RESPONSE_APPLY);
|
|
|
|
if(!pp.raw.ff_file.empty()) {
|
|
fc.set_filename( pp.raw.ff_file );
|
|
}
|
|
|
|
if( fc.run() == Gtk::RESPONSE_APPLY ) {
|
|
if (bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
rtengine::procparams::ProcParams lpp = mselected[i]->thumbnail->getProcParams();
|
|
lpp.raw.ff_file = fc.get_filename();
|
|
lpp.raw.ff_AutoSelect = false;
|
|
mselected[i]->thumbnail->setProcParams(lpp, nullptr, FILEBROWSER, false);
|
|
}
|
|
|
|
if (bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
}
|
|
}
|
|
} else if( m == thisIsFF) {
|
|
if( !options.rtSettings.flatFieldsPath.empty()) {
|
|
if (Gio::File::create_for_path(options.rtSettings.flatFieldsPath)->query_exists() ) {
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path ( mselected[i]->filename );
|
|
|
|
if( !file ) {
|
|
continue;
|
|
}
|
|
|
|
Glib::ustring destName = options.rtSettings.flatFieldsPath + "/" + file->get_basename();
|
|
Glib::RefPtr<Gio::File> dest = Gio::File::create_for_path ( destName );
|
|
file->move( dest );
|
|
}
|
|
|
|
// Reinit cache
|
|
rtengine::ffm.init( options.rtSettings.flatFieldsPath );
|
|
} else {
|
|
// Target directory creation failed, we clear the flatFieldsPath setting
|
|
options.rtSettings.flatFieldsPath.clear();
|
|
Glib::ustring msg_ = Glib::ustring::compose (M("MAIN_MSG_PATHDOESNTEXIST"), escapeHtmlChars(options.rtSettings.flatFieldsPath))
|
|
+ "\n\n" + M("MAIN_MSG_OPERATIONCANCELLED");
|
|
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.set_title(M("TP_FLATFIELD_LABEL"));
|
|
msgd.run ();
|
|
}
|
|
} else {
|
|
Glib::ustring msg_ = M("MAIN_MSG_SETPATHFIRST") + "\n\n" + M("MAIN_MSG_OPERATIONCANCELLED");
|
|
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.set_title(M("TP_FLATFIELD_LABEL"));
|
|
msgd.run ();
|
|
}
|
|
} else if (m == copyprof) {
|
|
copyProfile ();
|
|
} else if (m == pasteprof) {
|
|
pasteProfile ();
|
|
} else if (m == partpasteprof) {
|
|
partPasteProfile ();
|
|
} else if (m == clearprof) {
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
mselected[i]->thumbnail->clearProcParams (FILEBROWSER);
|
|
}
|
|
|
|
queue_draw ();
|
|
} else if (m == resetdefaultprof) {
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < mselected.size(); i++) {
|
|
const auto thumbnail = mselected[i]->thumbnail;
|
|
const auto rank = thumbnail->getRank();
|
|
const auto colorLabel = thumbnail->getColorLabel();
|
|
const auto stage = thumbnail->getStage();
|
|
|
|
thumbnail->createProcParamsForUpdate (false, true);
|
|
thumbnail->setRank(rank);
|
|
thumbnail->setColorLabel(colorLabel);
|
|
thumbnail->setStage(stage);
|
|
|
|
// Empty run to update the thumb
|
|
rtengine::procparams::ProcParams params = thumbnail->getProcParams ();
|
|
thumbnail->setProcParams (params, nullptr, FILEBROWSER, true, true);
|
|
}
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
} else if (m == clearFromCache) {
|
|
tbl->clearFromCacheRequested (mselected, false);
|
|
|
|
//queue_draw ();
|
|
} else if (m == clearFromCacheFull) {
|
|
tbl->clearFromCacheRequested (mselected, true);
|
|
|
|
//queue_draw ();
|
|
#ifdef WIN32
|
|
} else if (miOpenDefaultViewer && m == miOpenDefaultViewer) {
|
|
openDefaultViewer(1);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void FileBrowser::copyProfile ()
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
if (selected.size() == 1) {
|
|
clipboard.setProcParams ((static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->getProcParams());
|
|
}
|
|
}
|
|
|
|
void FileBrowser::pasteProfile ()
|
|
{
|
|
|
|
if (clipboard.hasProcParams()) {
|
|
std::vector<FileBrowserEntry*> mselected;
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
for (unsigned int i = 0; i < selected.size(); i++) {
|
|
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
|
|
}
|
|
}
|
|
|
|
if (!tbl || mselected.empty()) {
|
|
return;
|
|
}
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (unsigned int i = 0; i < mselected.size(); i++) {
|
|
// copying read only clipboard PartialProfile to a temporary one
|
|
const rtengine::procparams::PartialProfile& cbPartProf = clipboard.getPartialProfile();
|
|
rtengine::procparams::PartialProfile pastedPartProf(cbPartProf.pparams, cbPartProf.pedited, true);
|
|
|
|
// applying the PartialProfile to the thumb's ProcParams
|
|
mselected[i]->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER);
|
|
pastedPartProf.deleteInstance();
|
|
}
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
|
|
queue_draw ();
|
|
}
|
|
}
|
|
|
|
void FileBrowser::partPasteProfile ()
|
|
{
|
|
|
|
if (clipboard.hasProcParams()) {
|
|
|
|
std::vector<FileBrowserEntry*> mselected;
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
for (unsigned int i = 0; i < selected.size(); i++) {
|
|
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
|
|
}
|
|
}
|
|
|
|
if (!tbl || mselected.empty()) {
|
|
return;
|
|
}
|
|
|
|
auto toplevel = static_cast<Gtk::Window*> (get_toplevel ());
|
|
PartialPasteDlg partialPasteDlg (M("PARTIALPASTE_DIALOGLABEL"), toplevel);
|
|
|
|
partialPasteDlg.updateSpotWidget(clipboard.getPartialProfile().pparams);
|
|
|
|
int i = partialPasteDlg.run ();
|
|
|
|
if (i == Gtk::RESPONSE_OK) {
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(mselected.size());
|
|
}
|
|
|
|
for (auto entry : mselected) {
|
|
// copying read only clipboard PartialProfile to a temporary one, initialized to the thumb's ProcParams
|
|
entry->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
|
|
const rtengine::procparams::PartialProfile& cbPartProf = clipboard.getPartialProfile();
|
|
rtengine::procparams::PartialProfile pastedPartProf(&entry->thumbnail->getProcParams (), nullptr);
|
|
|
|
// pushing the selected values of the clipboard PartialProfile to the temporary PartialProfile
|
|
partialPasteDlg.applyPaste (pastedPartProf.pparams, pastedPartProf.pedited, cbPartProf.pparams, cbPartProf.pedited);
|
|
|
|
// applying the temporary PartialProfile to the thumb's ProcParams
|
|
entry->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER);
|
|
pastedPartProf.deleteInstance();
|
|
}
|
|
|
|
if (!mselected.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
|
|
queue_draw ();
|
|
}
|
|
|
|
partialPasteDlg.hide ();
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
void FileBrowser::openDefaultViewer (int destination)
|
|
{
|
|
bool success = true;
|
|
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
if (selected.size() == 1) {
|
|
success = (static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->openDefaultViewer(destination);
|
|
}
|
|
}
|
|
|
|
if (!success) {
|
|
Gtk::MessageDialog msgd(getToplevelWindow(this), M("MAIN_MSG_IMAGEUNPROCESSED"), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
|
|
msgd.run ();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bool FileBrowser::keyPressed (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;
|
|
#endif
|
|
|
|
if ((event->keyval == GDK_KEY_C || event->keyval == GDK_KEY_c) && ctrl && shift) {
|
|
menuItemActivated (copyTo);
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_M || event->keyval == GDK_KEY_m) && ctrl && shift) {
|
|
menuItemActivated (moveTo);
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_C || event->keyval == GDK_KEY_c || event->keyval == GDK_KEY_Insert) && ctrl) {
|
|
copyProfile ();
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_V || event->keyval == GDK_KEY_v) && ctrl && !shift) {
|
|
pasteProfile ();
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Insert && shift) {
|
|
pasteProfile ();
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_V || event->keyval == GDK_KEY_v) && ctrl && shift) {
|
|
partPasteProfile ();
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Delete && !shift) {
|
|
menuItemActivated (trash);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Delete && shift) {
|
|
menuItemActivated (untrash);
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_B || event->keyval == GDK_KEY_b) && ctrl && !shift) {
|
|
menuItemActivated (develop);
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_B || event->keyval == GDK_KEY_b) && ctrl && shift) {
|
|
menuItemActivated (developfast);
|
|
return true;
|
|
} else if ((event->keyval == GDK_KEY_A || event->keyval == GDK_KEY_a) && ctrl) {
|
|
menuItemActivated (selall);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_F2 && !ctrl) {
|
|
menuItemActivated (rename);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_F3 && !(ctrl || shift || alt)) { // open Previous image from FileBrowser perspective
|
|
FileBrowser::openPrevImage ();
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_F4 && !(ctrl || shift || alt)) { // open Next image from FileBrowser perspective
|
|
FileBrowser::openNextImage ();
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Left) {
|
|
selectPrev (1, shift);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Right) {
|
|
selectNext (1, shift);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Up) {
|
|
selectPrev (numOfCols, shift);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Down) {
|
|
selectNext (numOfCols, shift);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Home) {
|
|
selectFirst (shift);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_End) {
|
|
selectLast (shift);
|
|
return true;
|
|
} else if(event->keyval == GDK_KEY_Return || event->keyval == GDK_KEY_KP_Enter) {
|
|
std::vector<FileBrowserEntry*> mselected;
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
|
|
}
|
|
|
|
openRequested(mselected);
|
|
#ifdef WIN32
|
|
} else if (event->keyval == GDK_KEY_F5) {
|
|
int dest = 1;
|
|
|
|
if (event->state & GDK_SHIFT_MASK) {
|
|
dest = 2;
|
|
} else if (event->state & GDK_CONTROL_MASK) {
|
|
dest = 3;
|
|
}
|
|
|
|
openDefaultViewer (dest);
|
|
return true;
|
|
#endif
|
|
} else if (event->keyval == GDK_KEY_Page_Up) {
|
|
scrollPage(GDK_SCROLL_UP);
|
|
return true;
|
|
} else if (event->keyval == GDK_KEY_Page_Down) {
|
|
scrollPage(GDK_SCROLL_DOWN);
|
|
return true;
|
|
}
|
|
|
|
#ifdef __WIN32__
|
|
else if (shift && !ctrl && !alt && !altgr) { // rank
|
|
switch(event->hardware_keycode) {
|
|
case 0x30: // 0-key
|
|
requestRanking (0);
|
|
return true;
|
|
|
|
case 0x31: // 1-key
|
|
requestRanking (1);
|
|
return true;
|
|
|
|
case 0x32: // 2-key
|
|
requestRanking (2);
|
|
return true;
|
|
|
|
case 0x33: // 3-key
|
|
requestRanking (3);
|
|
return true;
|
|
|
|
case 0x34: // 4-key
|
|
requestRanking (4);
|
|
return true;
|
|
|
|
case 0x35: // 5-key
|
|
requestRanking (5);
|
|
return true;
|
|
}
|
|
} else if (shift && ctrl && !alt && !altgr) { // color labels
|
|
switch(event->hardware_keycode) {
|
|
case 0x30: // 0-key
|
|
requestColorLabel (0);
|
|
return true;
|
|
|
|
case 0x31: // 1-key
|
|
requestColorLabel (1);
|
|
return true;
|
|
|
|
case 0x32: // 2-key
|
|
requestColorLabel (2);
|
|
return true;
|
|
|
|
case 0x33: // 3-key
|
|
requestColorLabel (3);
|
|
return true;
|
|
|
|
case 0x34: // 4-key
|
|
requestColorLabel (4);
|
|
return true;
|
|
|
|
case 0x35: // 5-key
|
|
requestColorLabel (5);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#else
|
|
else if (shift && !ctrl && !alt) { // rank
|
|
switch(event->hardware_keycode) {
|
|
case 0x13:
|
|
requestRanking (0);
|
|
return true;
|
|
|
|
case 0x0a:
|
|
requestRanking (1);
|
|
return true;
|
|
|
|
case 0x0b:
|
|
requestRanking (2);
|
|
return true;
|
|
|
|
case 0x0c:
|
|
requestRanking (3);
|
|
return true;
|
|
|
|
case 0x0d:
|
|
requestRanking (4);
|
|
return true;
|
|
|
|
case 0x0e:
|
|
requestRanking (5);
|
|
return true;
|
|
}
|
|
} else if (shift && ctrl && !alt) { // color labels
|
|
switch(event->hardware_keycode) {
|
|
case 0x13:
|
|
requestColorLabel (0);
|
|
return true;
|
|
|
|
case 0x0a:
|
|
requestColorLabel (1);
|
|
return true;
|
|
|
|
case 0x0b:
|
|
requestColorLabel (2);
|
|
return true;
|
|
|
|
case 0x0c:
|
|
requestColorLabel (3);
|
|
return true;
|
|
|
|
case 0x0d:
|
|
requestColorLabel (4);
|
|
return true;
|
|
|
|
case 0x0e:
|
|
requestColorLabel (5);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
void FileBrowser::saveThumbnailHeight (int height)
|
|
{
|
|
if (!options.sameThumbSize && getLocation() == THLOC_EDITOR) {
|
|
options.thumbSizeTab = height;
|
|
} else {
|
|
options.thumbSize = height;
|
|
}
|
|
}
|
|
|
|
int FileBrowser::getThumbnailHeight ()
|
|
{
|
|
// The user could have manually forced the option to a too big value
|
|
if (!options.sameThumbSize && getLocation() == THLOC_EDITOR) {
|
|
return std::max(std::min(options.thumbSizeTab, 800), 10);
|
|
} else {
|
|
return std::max(std::min(options.thumbSize, 800), 10);
|
|
}
|
|
}
|
|
|
|
void FileBrowser::enableTabMode(bool enable)
|
|
{
|
|
ThumbBrowserBase::enableTabMode(enable);
|
|
if (options.inspectorWindow) {
|
|
if (enable) {
|
|
inspect->remove_accelerator(pmenu->get_accel_group(), GDK_KEY_f, (Gdk::ModifierType)0);
|
|
}
|
|
else {
|
|
inspect->add_accelerator ("activate", pmenu->get_accel_group(), GDK_KEY_f, (Gdk::ModifierType)0, Gtk::ACCEL_VISIBLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileBrowser::applyMenuItemActivated (ProfileStoreLabel *label)
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
const rtengine::procparams::PartialProfile* partProfile = ProfileStore::getInstance()->getProfile (label->entry);
|
|
|
|
if (partProfile->pparams && !selected.empty()) {
|
|
if (bppcl) {
|
|
bppcl->beginBatchPParamsChange(selected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
(static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->setProcParams (*partProfile->pparams, partProfile->pedited, FILEBROWSER);
|
|
}
|
|
|
|
if (bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
|
|
queue_draw ();
|
|
}
|
|
}
|
|
|
|
void FileBrowser::applyPartialMenuItemActivated (ProfileStoreLabel *label)
|
|
{
|
|
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
if (!tbl || selected.empty()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
const rtengine::procparams::PartialProfile* srcProfiles = ProfileStore::getInstance()->getProfile (label->entry);
|
|
|
|
if (srcProfiles->pparams) {
|
|
|
|
auto toplevel = static_cast<Gtk::Window*> (get_toplevel ());
|
|
PartialPasteDlg partialPasteDlg (M("PARTIALPASTE_DIALOGLABEL"), toplevel);
|
|
|
|
partialPasteDlg.updateSpotWidget(srcProfiles->pparams);
|
|
|
|
if (partialPasteDlg.run() == Gtk::RESPONSE_OK) {
|
|
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
if (bppcl) {
|
|
bppcl->beginBatchPParamsChange(selected.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
selected[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
|
|
|
|
rtengine::procparams::PartialProfile dstProfile(true);
|
|
*dstProfile.pparams = (static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->getProcParams ();
|
|
dstProfile.set(true);
|
|
dstProfile.pedited->locallab.spots.resize(dstProfile.pparams->locallab.spots.size(), LocallabParamsEdited::LocallabSpotEdited(true));
|
|
partialPasteDlg.applyPaste (dstProfile.pparams, dstProfile.pedited, srcProfiles->pparams, srcProfiles->pedited);
|
|
(static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->setProcParams (*dstProfile.pparams, dstProfile.pedited, FILEBROWSER);
|
|
dstProfile.deleteInstance();
|
|
}
|
|
|
|
if (bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
|
|
queue_draw ();
|
|
}
|
|
|
|
partialPasteDlg.hide ();
|
|
}
|
|
}
|
|
|
|
void FileBrowser::applyFilter (const BrowserFilter& filter)
|
|
{
|
|
|
|
this->filter = filter;
|
|
|
|
// remove items not complying the filter from the selection
|
|
bool selchanged = false;
|
|
numFiltered = 0;
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
if (filter.showOriginal) {
|
|
findOriginalEntries(fd);
|
|
}
|
|
|
|
for (size_t i = 0; i < fd.size(); i++) {
|
|
if (checkFilter(fd[i])) {
|
|
numFiltered++;
|
|
} else if (fd[i]->selected) {
|
|
fd[i]->selected = false;
|
|
std::vector<ThumbBrowserEntryBase*>::iterator j = std::find(selected.begin(), selected.end(), fd[i]);
|
|
selected.erase(j);
|
|
|
|
if (lastClicked == fd[i]) {
|
|
lastClicked = nullptr;
|
|
}
|
|
|
|
selchanged = true;
|
|
}
|
|
}
|
|
if (selected.empty() || (anchor && std::find(selected.begin(), selected.end(), anchor) == selected.end())) {
|
|
anchor = nullptr;
|
|
}
|
|
}
|
|
|
|
if (selchanged) {
|
|
notifySelectionListener ();
|
|
}
|
|
|
|
tbl->filterApplied();
|
|
redraw ();
|
|
}
|
|
|
|
bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) const // true -> entry complies filter
|
|
{
|
|
|
|
FileBrowserEntry* entry = static_cast<FileBrowserEntry*>(entryb);
|
|
|
|
if (filter.showOriginal && entry->getOriginal()) {
|
|
return false;
|
|
}
|
|
|
|
// return false if basic filter settings are not satisfied
|
|
if ((!filter.showRanked[entry->thumbnail->getRank()] ) ||
|
|
(!filter.showCLabeled[entry->thumbnail->getColorLabel()] ) ||
|
|
|
|
((entry->thumbnail->hasProcParams() && filter.showEdited[0]) && !filter.showEdited[1]) ||
|
|
((!entry->thumbnail->hasProcParams() && filter.showEdited[1]) && !filter.showEdited[0]) ||
|
|
|
|
((entry->thumbnail->isRecentlySaved() && filter.showRecentlySaved[0]) && !filter.showRecentlySaved[1]) ||
|
|
((!entry->thumbnail->isRecentlySaved() && filter.showRecentlySaved[1]) && !filter.showRecentlySaved[0]) ||
|
|
|
|
(entry->thumbnail->getStage() && !filter.showTrash) ||
|
|
(!entry->thumbnail->getStage() && !filter.showNotTrash)) {
|
|
return false;
|
|
}
|
|
|
|
// return false if query is not satisfied
|
|
if (!filter.vFilterStrings.empty()) {
|
|
// check if image's FileName contains queryFileName (case insensitive)
|
|
// TODO should we provide case-sensitive search option via preferences?
|
|
std::string FileName = Glib::path_get_basename(entry->thumbnail->getFileName());
|
|
std::transform(FileName.begin(), FileName.end(), FileName.begin(), ::toupper);
|
|
int iFilenameMatch = 0;
|
|
|
|
for (const auto& filterString : filter.vFilterStrings) {
|
|
if (FileName.find(filterString) != std::string::npos) {
|
|
++iFilenameMatch;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (filter.matchEqual) {
|
|
if (iFilenameMatch == 0) { //none of the vFilterStrings found in FileName
|
|
return false;
|
|
}
|
|
} else {
|
|
if (iFilenameMatch > 0) { // match is found for at least one of vFilterStrings in FileName
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!filter.exifFilterEnabled) {
|
|
return true;
|
|
}
|
|
|
|
// check exif filter
|
|
const CacheImageData* cfs = entry->thumbnail->getCacheImageData();
|
|
double tol = 0.01;
|
|
double tol2 = 1e-8;
|
|
|
|
if (!cfs->exifValid) {
|
|
return (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(cfs->getCamera()) > 0)
|
|
&& (!filter.exifFilter.filterLens || filter.exifFilter.lenses.count(cfs->lens) > 0)
|
|
&& (!filter.exifFilter.filterFiletype || filter.exifFilter.filetypes.count(cfs->filetype) > 0)
|
|
&& (!filter.exifFilter.filterExpComp || filter.exifFilter.expcomp.count(cfs->expcomp) > 0);
|
|
}
|
|
|
|
return
|
|
(!filter.exifFilter.filterShutter || (rtengine::FramesMetaData::shutterFromString(rtengine::FramesMetaData::shutterToString(cfs->shutter)) >= filter.exifFilter.shutterFrom - tol2 && rtengine::FramesMetaData::shutterFromString(rtengine::FramesMetaData::shutterToString(cfs->shutter)) <= filter.exifFilter.shutterTo + tol2))
|
|
&& (!filter.exifFilter.filterFNumber || (rtengine::FramesMetaData::apertureFromString(rtengine::FramesMetaData::apertureToString(cfs->fnumber)) >= filter.exifFilter.fnumberFrom - tol2 && rtengine::FramesMetaData::apertureFromString(rtengine::FramesMetaData::apertureToString(cfs->fnumber)) <= filter.exifFilter.fnumberTo + tol2))
|
|
&& (!filter.exifFilter.filterFocalLen || (cfs->focalLen >= filter.exifFilter.focalFrom - tol && cfs->focalLen <= filter.exifFilter.focalTo + tol))
|
|
&& (!filter.exifFilter.filterISO || (cfs->iso >= filter.exifFilter.isoFrom && cfs->iso <= filter.exifFilter.isoTo))
|
|
&& (!filter.exifFilter.filterExpComp || filter.exifFilter.expcomp.count(cfs->expcomp) > 0)
|
|
&& (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(cfs->getCamera()) > 0)
|
|
&& (!filter.exifFilter.filterLens || filter.exifFilter.lenses.count(cfs->lens) > 0)
|
|
&& (!filter.exifFilter.filterFiletype || filter.exifFilter.filetypes.count(cfs->filetype) > 0);
|
|
}
|
|
|
|
void FileBrowser::toTrashRequested (std::vector<FileBrowserEntry*> tbe)
|
|
{
|
|
|
|
for (size_t i = 0; i < tbe.size(); i++) {
|
|
// try to load the last saved parameters from the cache or from the paramfile file
|
|
tbe[i]->thumbnail->createProcParamsForUpdate(false, false, true); // this can execute customprofilebuilder to generate param file in "flagging" mode
|
|
|
|
// no need to notify listeners as item goes to trash, likely to be deleted
|
|
|
|
if (tbe[i]->thumbnail->getStage()) {
|
|
continue;
|
|
}
|
|
|
|
tbe[i]->thumbnail->setStage (true);
|
|
|
|
if (tbe[i]->getThumbButtonSet()) {
|
|
tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
|
|
tbe[i]->getThumbButtonSet()->setColorLabel (tbe[i]->thumbnail->getColorLabel());
|
|
tbe[i]->getThumbButtonSet()->setInTrash (true);
|
|
tbe[i]->thumbnail->updateCache (); // needed to save the colorlabel to disk in the procparam file(s) and the cache image data file
|
|
}
|
|
}
|
|
|
|
trash_changed().emit();
|
|
applyFilter (filter);
|
|
}
|
|
|
|
void FileBrowser::fromTrashRequested (std::vector<FileBrowserEntry*> tbe)
|
|
{
|
|
|
|
for (size_t i = 0; i < tbe.size(); i++) {
|
|
// if thumbnail was marked inTrash=true then param file must be there, no need to run customprofilebuilder
|
|
|
|
if (!tbe[i]->thumbnail->getStage()) {
|
|
continue;
|
|
}
|
|
|
|
tbe[i]->thumbnail->setStage (false);
|
|
|
|
if (tbe[i]->getThumbButtonSet()) {
|
|
tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
|
|
tbe[i]->getThumbButtonSet()->setColorLabel (tbe[i]->thumbnail->getColorLabel());
|
|
tbe[i]->getThumbButtonSet()->setInTrash (false);
|
|
tbe[i]->thumbnail->updateCache (); // needed to save the colorlabel to disk in the procparam file(s) and the cache image data file
|
|
}
|
|
}
|
|
|
|
trash_changed().emit();
|
|
applyFilter (filter);
|
|
}
|
|
|
|
void FileBrowser::sortMethodRequested (int method)
|
|
{
|
|
options.sortMethod = Options::SortMethod(method);
|
|
resort ();
|
|
}
|
|
|
|
void FileBrowser::sortOrderRequested (int order)
|
|
{
|
|
options.sortDescending = !!order;
|
|
resort ();
|
|
}
|
|
|
|
void FileBrowser::rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank)
|
|
{
|
|
|
|
if (!tbe.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(tbe.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < tbe.size(); i++) {
|
|
|
|
// try to load the last saved parameters from the cache or from the paramfile file
|
|
tbe[i]->thumbnail->createProcParamsForUpdate(false, false, true); // this can execute customprofilebuilder to generate param file in "flagging" mode
|
|
|
|
// notify listeners TODO: should do this ONLY when params changed by customprofilebuilder?
|
|
tbe[i]->thumbnail->notifylisterners_procParamsChanged(FILEBROWSER);
|
|
|
|
tbe[i]->thumbnail->setRank (rank);
|
|
tbe[i]->thumbnail->updateCache (); // needed to save the colorlabel to disk in the procparam file(s) and the cache image data file
|
|
//TODO? - should update pparams instead?
|
|
|
|
if (tbe[i]->getThumbButtonSet()) {
|
|
tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
|
|
}
|
|
}
|
|
|
|
applyFilter (filter);
|
|
|
|
if (!tbe.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
}
|
|
|
|
void FileBrowser::colorlabelRequested (std::vector<FileBrowserEntry*> tbe, int colorlabel)
|
|
{
|
|
|
|
if (!tbe.empty() && bppcl) {
|
|
bppcl->beginBatchPParamsChange(tbe.size());
|
|
}
|
|
|
|
for (size_t i = 0; i < tbe.size(); i++) {
|
|
// try to load the last saved parameters from the cache or from the paramfile file
|
|
tbe[i]->thumbnail->createProcParamsForUpdate(false, false, true); // this can execute customprofilebuilder to generate param file in "flagging" mode
|
|
|
|
// notify listeners TODO: should do this ONLY when params changed by customprofilebuilder?
|
|
tbe[i]->thumbnail->notifylisterners_procParamsChanged(FILEBROWSER);
|
|
|
|
tbe[i]->thumbnail->setColorLabel (colorlabel);
|
|
tbe[i]->thumbnail->updateCache(); // needed to save the colorlabel to disk in the procparam file(s) and the cache image data file
|
|
|
|
//TODO? - should update pparams instead?
|
|
if (tbe[i]->getThumbButtonSet()) {
|
|
tbe[i]->getThumbButtonSet()->setColorLabel (tbe[i]->thumbnail->getColorLabel());
|
|
}
|
|
}
|
|
|
|
applyFilter (filter);
|
|
|
|
if (!tbe.empty() && bppcl) {
|
|
bppcl->endBatchPParamsChange();
|
|
}
|
|
}
|
|
|
|
void FileBrowser::requestRanking(int rank)
|
|
{
|
|
std::vector<FileBrowserEntry*> mselected;
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
|
|
}
|
|
}
|
|
|
|
rankingRequested (mselected, rank);
|
|
}
|
|
|
|
void FileBrowser::requestColorLabel(int colorlabel)
|
|
{
|
|
std::vector<FileBrowserEntry*> mselected;
|
|
{
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
|
|
}
|
|
}
|
|
|
|
colorlabelRequested (mselected, colorlabel);
|
|
}
|
|
|
|
void FileBrowser::buttonPressed (LWButton* button, int actionCode, void* actionData)
|
|
{
|
|
|
|
if (actionCode >= 0 && actionCode <= 5) { // rank
|
|
std::vector<FileBrowserEntry*> tbe;
|
|
tbe.push_back (static_cast<FileBrowserEntry*>(actionData));
|
|
rankingRequested (tbe, actionCode);
|
|
} else if (actionCode == 6 && tbl) { // to processing queue
|
|
std::vector<FileBrowserEntry*> tbe;
|
|
tbe.push_back (static_cast<FileBrowserEntry*>(actionData));
|
|
tbl->developRequested (tbe, false); // not a fast, but a FULL mode
|
|
} else if (actionCode == 7) { // to trash / undelete
|
|
std::vector<FileBrowserEntry*> tbe;
|
|
FileBrowserEntry* entry = static_cast<FileBrowserEntry*>(actionData);
|
|
tbe.push_back (entry);
|
|
|
|
if (!entry->thumbnail->getStage()) {
|
|
toTrashRequested (tbe);
|
|
} else {
|
|
fromTrashRequested (tbe);
|
|
}
|
|
} else if (actionCode == 8 && tbl) { // color label
|
|
// show popup menu
|
|
colorLabel_actionData = actionData;// this will be reused when pmenuColorLabels is clicked
|
|
pmenuColorLabels->popup (3, this->eventTime);
|
|
}
|
|
}
|
|
|
|
void FileBrowser::openNextImage()
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
if (!fd.empty() && selected.size() > 0 && !options.tabbedUI) {
|
|
for (size_t i = 0; i < fd.size() - 1; i++) {
|
|
if (selected[0]->thumbnail->getFileName() == fd[i]->filename) { // located 1-st image in current selection
|
|
if (i < fd.size() && tbl) {
|
|
// find the first not-filtered-out (next) image
|
|
for (size_t k = i + 1; k < fd.size(); k++) {
|
|
if (!fd[k]->filtered/*checkFilter (fd[k])*/) {
|
|
|
|
// clear current selection
|
|
for (size_t j = 0; j < selected.size(); j++) {
|
|
selected[j]->selected = false;
|
|
}
|
|
|
|
selected.clear();
|
|
|
|
// set new selection
|
|
fd[k]->selected = true;
|
|
selected.push_back(fd[k]);
|
|
//queue_draw();
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
// this will require a read access
|
|
notifySelectionListener();
|
|
|
|
MYWRITERLOCK_ACQUIRE(l);
|
|
|
|
// scroll to the selected position, centered horizontally in the container
|
|
double x1, y1;
|
|
getScrollPosition(x1, y1);
|
|
|
|
double x2 = selected[0]->getStartX();
|
|
double y2 = selected[0]->getStartY();
|
|
|
|
Thumbnail* thumb = (static_cast<FileBrowserEntry*>(fd[k]))->thumbnail;
|
|
int tw = fd[k]->getMinimalWidth(); // thumb width
|
|
|
|
int ww = get_width(); // window width
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
// scroll only when selected[0] is outside of the displayed bounds
|
|
// or less than a thumbnail's width from either edge.
|
|
if ((x2 > x1 + ww - 1.5 * tw) || (x2 - tw / 2 < x1)) {
|
|
setScrollPosition(x2 - (ww - tw) / 2, y2);
|
|
}
|
|
|
|
// open the selected image
|
|
tbl->openRequested({thumb});
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileBrowser::openPrevImage()
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
if (!fd.empty() && selected.size() > 0 && !options.tabbedUI) {
|
|
for (size_t i = 1; i < fd.size(); i++) {
|
|
if (selected[0]->thumbnail->getFileName() == fd[i]->filename) { // located 1-st image in current selection
|
|
if (i > 0 && tbl) {
|
|
// find the first not-filtered-out (previous) image
|
|
for (ssize_t k = (ssize_t)i - 1; k >= 0; k--) {
|
|
if (!fd[k]->filtered/*checkFilter (fd[k])*/) {
|
|
|
|
// clear current selection
|
|
for (size_t j = 0; j < selected.size(); j++) {
|
|
selected[j]->selected = false;
|
|
}
|
|
|
|
selected.clear();
|
|
|
|
// set new selection
|
|
fd[k]->selected = true;
|
|
selected.push_back(fd[k]);
|
|
//queue_draw();
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
// this will require a read access
|
|
notifySelectionListener();
|
|
|
|
MYWRITERLOCK_ACQUIRE(l);
|
|
|
|
// scroll to the selected position, centered horizontally in the container
|
|
double x1, y1;
|
|
getScrollPosition(x1, y1);
|
|
|
|
double x2 = selected[0]->getStartX();
|
|
double y2 = selected[0]->getStartY();
|
|
|
|
Thumbnail* thumb = (static_cast<FileBrowserEntry*>(fd[k]))->thumbnail;
|
|
int tw = fd[k]->getMinimalWidth(); // thumb width
|
|
|
|
int ww = get_width(); // window width
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
// scroll only when selected[0] is outside of the displayed bounds
|
|
// or less than a thumbnail's width from either edge.
|
|
if ((x2 > x1 + ww - 1.5 * tw) || (x2 - tw / 2 < x1)) {
|
|
setScrollPosition(x2 - (ww - tw) / 2, y2);
|
|
}
|
|
|
|
// open the selected image
|
|
tbl->openRequested({thumb});
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileBrowser::selectImage(const Glib::ustring& fname, bool doScroll)
|
|
{
|
|
MYWRITERLOCK(l, entryRW);
|
|
|
|
if (!fd.empty() && !options.tabbedUI) {
|
|
for (size_t i = 0; i < fd.size(); i++) {
|
|
if (fname == fd[i]->filename && !fd[i]->filtered) {
|
|
// matching file found for sync
|
|
|
|
// clear current selection
|
|
for (size_t j = 0; j < selected.size(); j++) {
|
|
selected[j]->selected = false;
|
|
}
|
|
|
|
selected.clear();
|
|
|
|
// set new selection
|
|
fd[i]->selected = true;
|
|
selected.push_back(fd[i]);
|
|
queue_draw();
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
// this will require a read access
|
|
notifySelectionListener();
|
|
|
|
MYWRITERLOCK_ACQUIRE(l);
|
|
|
|
// scroll to the selected position, centered horizontally in the container
|
|
double x = selected[0]->getStartX();
|
|
double y = selected[0]->getStartY();
|
|
|
|
int tw = fd[i]->getMinimalWidth(); // thumb width
|
|
|
|
int ww = get_width(); // window width
|
|
|
|
MYWRITERLOCK_RELEASE(l);
|
|
|
|
if (doScroll) {
|
|
// Center thumb
|
|
setScrollPosition(x - (ww - tw) / 2, y);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileBrowser::openNextPreviousEditorImage (const Glib::ustring& fname, eRTNav nextPrevious)
|
|
{
|
|
|
|
// let FileBrowser acquire Editor's perspective
|
|
selectImage (fname, false);
|
|
|
|
// now switch to the requested image
|
|
if (nextPrevious == NAV_NEXT) {
|
|
openNextImage();
|
|
} else if (nextPrevious == NAV_PREVIOUS) {
|
|
openPrevImage();
|
|
}
|
|
}
|
|
|
|
void FileBrowser::thumbRearrangementNeeded ()
|
|
{
|
|
idle_register.add(
|
|
[this]() -> bool
|
|
{
|
|
refreshThumbImages();// arrangeFiles is NOT enough
|
|
return false;
|
|
}
|
|
);
|
|
}
|
|
|
|
void FileBrowser::selectionChanged ()
|
|
{
|
|
|
|
notifySelectionListener ();
|
|
}
|
|
|
|
void FileBrowser::notifySelectionListener ()
|
|
{
|
|
|
|
if (tbl) {
|
|
MYREADERLOCK(l, entryRW);
|
|
|
|
std::vector<Thumbnail*> thm;
|
|
|
|
for (size_t i = 0; i < selected.size(); i++) {
|
|
thm.push_back ((static_cast<FileBrowserEntry*>(selected[i]))->thumbnail);
|
|
}
|
|
|
|
tbl->selectionChanged (thm);
|
|
}
|
|
}
|
|
|
|
void FileBrowser::redrawNeeded (LWButton* button)
|
|
{
|
|
GThreadLock lock;
|
|
queue_draw ();
|
|
}
|
|
FileBrowser::type_trash_changed FileBrowser::trash_changed ()
|
|
{
|
|
return m_trash_changed;
|
|
}
|
|
|
|
|
|
// ExportPanel interface
|
|
void FileBrowser::exportRequested ()
|
|
{
|
|
FileBrowser::menuItemActivated(developfast);
|
|
}
|
|
|
|
void FileBrowser::setExportPanel (ExportPanel* expanel)
|
|
{
|
|
|
|
exportPanel = expanel;
|
|
exportPanel->set_sensitive (false);
|
|
exportPanel->setExportPanelListener (this);
|
|
}
|
|
|
|
void FileBrowser::storeCurrentValue()
|
|
{
|
|
}
|
|
|
|
void FileBrowser::updateProfileList()
|
|
{
|
|
// submenu applmenu
|
|
int p = 0;
|
|
|
|
const std::vector<const ProfileStoreEntry*> *profEntries = ProfileStore::getInstance()->getFileList(); // lock and get a pointer to the profiles' list
|
|
|
|
std::map<unsigned short /* folderId */, Gtk::Menu*> subMenuList; // store the Gtk::Menu that Gtk::MenuItem will have to be attached to
|
|
|
|
subMenuList[0] = Gtk::manage (new Gtk::Menu ()); // adding the root submenu
|
|
|
|
// iterate the profile store's profile list
|
|
for (size_t i = 0; i < profEntries->size(); i++) {
|
|
// create a new label for the current entry (be it a folder or file)
|
|
ProfileStoreLabel *currLabel = Gtk::manage(new ProfileStoreLabel( profEntries->at(i) ));
|
|
|
|
// create the MenuItem object
|
|
Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (*currLabel));
|
|
|
|
// create a new Menu object if the entry is a folder and not the root one
|
|
if (currLabel->entry->type == PSET_FOLDER) {
|
|
// creating the new sub-menu
|
|
Gtk::Menu* subMenu = Gtk::manage (new Gtk::Menu ());
|
|
|
|
// add it to the menu list
|
|
subMenuList[currLabel->entry->folderId] = subMenu;
|
|
|
|
// add it to the parent MenuItem
|
|
mi->set_submenu(*subMenu);
|
|
}
|
|
|
|
// Hombre: ... does parentMenuId sounds like a hack? ... Yes.
|
|
int parentMenuId = !options.useBundledProfiles && currLabel->entry->parentFolderId == 1 ? 0 : currLabel->entry->parentFolderId;
|
|
subMenuList[parentMenuId]->attach (*mi, 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
if (currLabel->entry->type == PSET_FILE) {
|
|
mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyMenuItemActivated), currLabel));
|
|
}
|
|
|
|
mi->show ();
|
|
}
|
|
|
|
if (subMenuList.size() && applyprof)
|
|
// TODO: Check that the previous one has been deleted, including all childrens
|
|
{
|
|
applyprof->set_submenu (*(subMenuList.at(0)));
|
|
}
|
|
|
|
subMenuList.clear();
|
|
subMenuList[0] = Gtk::manage (new Gtk::Menu ()); // adding the root submenu
|
|
// keep profEntries list
|
|
|
|
// submenu applpartmenu
|
|
p = 0;
|
|
|
|
for (size_t i = 0; i < profEntries->size(); i++) {
|
|
ProfileStoreLabel *currLabel = Gtk::manage(new ProfileStoreLabel( profEntries->at(i) ));
|
|
|
|
Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (*currLabel));
|
|
|
|
if (currLabel->entry->type == PSET_FOLDER) {
|
|
// creating the new sub-menu
|
|
Gtk::Menu* subMenu = Gtk::manage (new Gtk::Menu ());
|
|
|
|
// add it to the menu list
|
|
subMenuList[currLabel->entry->folderId] = subMenu;
|
|
|
|
// add it to the parent MenuItem
|
|
mi->set_submenu(*subMenu);
|
|
}
|
|
|
|
// Hombre: ... does parentMenuId sounds like a hack? ... yes.
|
|
int parentMenuId = !options.useBundledProfiles && currLabel->entry->parentFolderId == 1 ? 0 : currLabel->entry->parentFolderId;
|
|
subMenuList[parentMenuId]->attach (*mi, 0, 1, p, p + 1);
|
|
p++;
|
|
|
|
if (currLabel->entry->type == PSET_FILE) {
|
|
mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyPartialMenuItemActivated), currLabel));
|
|
}
|
|
|
|
mi->show ();
|
|
}
|
|
|
|
if (subMenuList.size() && applypartprof)
|
|
// TODO: Check that the previous one has been deleted, including all childrens
|
|
{
|
|
applypartprof->set_submenu (*(subMenuList.at(0)));
|
|
}
|
|
|
|
ProfileStore::getInstance()->releaseFileList();
|
|
subMenuList.clear();
|
|
}
|
|
|
|
void FileBrowser::restoreValue()
|
|
{
|
|
}
|
|
|
|
void FileBrowser::openRequested( std::vector<FileBrowserEntry*> mselected)
|
|
{
|
|
std::vector<Thumbnail*> entries;
|
|
// in Single Editor Mode open only last selected image
|
|
size_t openStart = options.tabbedUI ? 0 : ( mselected.size() > 0 ? mselected.size() - 1 : 0);
|
|
|
|
for (size_t i = openStart; i < mselected.size(); i++) {
|
|
entries.push_back (mselected[i]->thumbnail);
|
|
}
|
|
|
|
tbl->openRequested (entries);
|
|
}
|
|
|
|
void FileBrowser::inspectRequested(std::vector<FileBrowserEntry*> mselected)
|
|
{
|
|
getInspector()->showWindow(true);
|
|
}
|