rawTherapee/rtgui/filebrowser.cc
Hombre 8b2eac9a3d Pipette and "On Preview Widgets" branch. See issue 227
The pipette part is already working quite nice but need to be finished. The widgets part needs more work...
2014-01-21 23:37:36 +01:00

1732 lines
68 KiB
C++
Executable File

/*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "filebrowser.h"
#include <glibmm.h>
#include "options.h"
#include "multilangmgr.h"
#include "clipboard.h"
#include "procparamchangers.h"
#include "batchqueue.h"
#include "../rtengine/dfmanager.h"
#include "../rtengine/ffmanager.h"
#include "rtimage.h"
#include "threadutils.h"
extern Options options;
FileBrowser::FileBrowser ()
: tbl(NULL),numFiltered(0), partialPasteDlg(M("PARTIALPASTE_DIALOGLABEL")) {
fbih = new FileBrowserIdleHelper;
fbih->fbrowser = this;
fbih->destroyed = false;
fbih->pending = 0;
profileStore.addListener(this);
signal_style_changed().connect( sigc::mem_fun(*this, &FileBrowser::styleChanged) );
int p = 0;
pmenu = new Gtk::Menu ();
pmenu->attach (*Gtk::manage(open = new Gtk::MenuItem (M("FILEBROWSER_POPUPOPEN"))), 0, 1, p, p+1); p++;
pmenu->attach (*Gtk::manage(develop = new Gtk::ImageMenuItem (M("FILEBROWSER_POPUPPROCESS"))), 0, 1, p, p+1); p++;
develop->set_image(*Gtk::manage(new RTImage ("processing.png")));
pmenu->attach (*Gtk::manage(developfast = new Gtk::ImageMenuItem (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++;
/***********************
* 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
***********************/
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 Gtk::ImageMenuItem (M(Glib::ustring::compose("%1%2","FILEBROWSER_POPUPCOLORLABEL",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 Gtk::ImageMenuItem (M(Glib::ustring::compose("%1%2","FILEBROWSER_POPUPCOLORLABEL",i)))), 0, 1, p, p+1); p++;
}
}
//set color label images
colorlabel[0]->set_image(*Gtk::manage(new RTImage ("cglabel0.png")));
for (int i=1; i<=5; i++){
colorlabel[i]->set_image(*Gtk::manage(new RTImage (Glib::ustring::compose("%1%2%3","clabel",i,".png"))));
}
pmenu->attach (*Gtk::manage(new Gtk::SeparatorMenuItem ()), 0, 1, p, p+1); p++;
/***********************
* external programs
* *********************/
#if PROTECT_VECTORS
Gtk::manage(miOpenDefaultViewer=new Gtk::MenuItem (M("FILEBROWSER_OPENDEFAULTVIEWER")));
#else
miOpenDefaultViewer=NULL;
#endif
// Build a list of menu items
mMenuExtProgs.clear(); amiExtProg=NULL;
for (std::list<ExtProgAction*>::iterator it=extProgStore->lActions.begin();it!=extProgStore->lActions.end();it++) {
ExtProgAction* pAct=*it;
if (pAct->target==1 || pAct->target==2) mMenuExtProgs[pAct->GetFullName()]=pAct;
}
// Attach them to menu
if (!mMenuExtProgs.empty() || miOpenDefaultViewer!=NULL) {
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());
if (miOpenDefaultViewer!=NULL) {
submenuExtProg->attach (*miOpenDefaultViewer, 0, 1, p, p+1); p++;
}
for (std::map<Glib::ustring, ExtProgAction*>::iterator 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 {
if (miOpenDefaultViewer!=NULL) {
pmenu->attach (*miOpenDefaultViewer, 0, 1, p, p+1); p++;
}
for (std::map<Glib::ustring, ExtProgAction*>::iterator 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::ImageMenuItem (M("FILEBROWSER_POPUPPROFILEOPERATIONS"))), 0, 1, p, p+1); p++;
menuProfileOperations->set_image(*Gtk::manage(new RTImage ("logoicon-wind.png")));
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(execcustprof = new Gtk::MenuItem (M("FILEBROWSER_EXEC_CPB"))), 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(execcustprof = new Gtk::MenuItem (M("FILEBROWSER_EXEC_CPB"))), 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); p++;
pmenu->show_all ();
/***********************
* Accelerators
* *********************/
pmaccelgroup = Gtk::AccelGroup::create ();
pmenu->set_accel_group (pmaccelgroup);
selall->add_accelerator ("activate", pmenu->get_accel_group(), GDK_a, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
trash->add_accelerator ("activate", pmenu->get_accel_group(), GDK_Delete, (Gdk::ModifierType)0, Gtk::ACCEL_VISIBLE);
untrash->add_accelerator ("activate", pmenu->get_accel_group(), GDK_Delete, Gdk::SHIFT_MASK, Gtk::ACCEL_VISIBLE);
develop->add_accelerator ("activate", pmenu->get_accel_group(), GDK_B, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
copyprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_C, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
pasteprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_V, Gdk::CONTROL_MASK, Gtk::ACCEL_VISIBLE);
partpasteprof->add_accelerator ("activate", pmenu->get_accel_group(), GDK_V, 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));
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 (int i=0; i<mMenuExtProgs.size(); i++)
amiExtProg[i]->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), amiExtProg[i]));
if (miOpenDefaultViewer!=NULL) {
miOpenDefaultViewer->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), miOpenDefaultViewer));
}
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));
execcustprof->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::menuItemActivated), execcustprof));
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 Gtk::ImageMenuItem (M(Glib::ustring::compose("%1%2","FILEBROWSER_POPUPCOLORLABEL",i)))), 0, 1, c, c+1); c++;
}
//set color label images
colorlabel_pop[0]->set_image(*Gtk::manage(new RTImage ("cglabel0.png")));
for (int i=1; i<=5; i++){
colorlabel_pop[i]->set_image(*Gtk::manage(new RTImage (Glib::ustring::compose("%1%2%3","clabel",i,".png"))));
}
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 ()
{
profileStore.removeListener(this);
delete pmenu;
delete pmenuColorLabels;
delete[] amiExtProg;
}
void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) {
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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()==1) {
untrash->set_sensitive (true);
break;
}
for (size_t i=0; i<selected.size(); i++)
if ((static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->getStage()==0) {
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());
}
// 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); p++;
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); p++;
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);
}
}
struct addparams {
FileBrowserIdleHelper* fbih;
FileBrowserEntry* entry;
};
int AddEntryUIThread (void* data) {
addparams* ap = static_cast<addparams*>(data);
FileBrowserIdleHelper* fbih = ap->fbih;
if (fbih->destroyed) {
if (fbih->pending == 1)
delete fbih;
else
fbih->pending--;
delete ap->entry;
delete ap;
return 0;
}
ap->fbih->fbrowser->addEntry_ (ap->entry);
delete ap;
fbih->pending--;
return 0;
}
void FileBrowser::addEntry (FileBrowserEntry* entry) {
fbih->pending++;
entry->setParent (this);
addparams* ap = new addparams;
ap->fbih = fbih;
ap->entry = entry;
g_idle_add (AddEntryUIThread, ap);
}
void FileBrowser::addEntry_ (FileBrowserEntry* entry) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
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()==1);
entry->getThumbButtonSet()->setButtonListener (this);
entry->resize (getThumbnailHeight());
// find place in abc order
{
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
std::vector<ThumbBrowserEntryBase*>::iterator i = fd.begin();
while (i!=fd.end() && *entry < *((FileBrowserEntry*)*i))
i++;
fd.insert (i, entry);
initEntry (entry);
}
redraw ();
}
FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) {
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
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);
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
if (j!=selected.end()) {
if (checkFilter (*j)) numFiltered--;
selected.erase (j);
notifySelectionListener ();
}
if (lastClicked==entry)
lastClicked = NULL;
redraw ();
return (static_cast<FileBrowserEntry*>(entry));
}
return NULL;
}
void FileBrowser::close () {
if (fbih->pending)
fbih->destroyed = true;
else
delete fbih;
fbih = new FileBrowserIdleHelper;
fbih->fbrowser = this;
fbih->destroyed = false;
fbih->pending = 0;
{
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
selected.clear ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l); // notifySelectionListener will need read access!
#endif
notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// 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 = NULL;
}
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;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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<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 (int j=0; j<mMenuExtProgs.size(); j++) {
if (m==amiExtProg[j]) {
ExtProgAction* pAct = mMenuExtProgs[m->get_label()];
// Build vector of all file names
std::vector<Glib::ustring> selFileNames;
for (int 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) {
std::vector<Thumbnail*> entries;
for (size_t i=0; i<mselected.size(); i++)
entries.push_back (mselected[i]->thumbnail);
tbl->openRequested (entries);
}
else if (m==remove)
tbl->deleteRequested (mselected, false);
else if (m==removeInclProc)
tbl->deleteRequested (mselected, 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)
tbl->developRequested (mselected, true);
else if (m==rename)
tbl->renameRequested (mselected);
else if (m==selall) {
lastClicked = NULL;
{
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
selected.clear ();
for (size_t i=0; i<fd.size(); i++)
if (checkFilter (fd[i])) {
fd[i]->selected = true;
selected.push_back (fd[i]);
}
}
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,NULL,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("Dark Frame",Gtk::FILE_CHOOSER_ACTION_OPEN );
FileChooserLastFolderPersister persister(&fc, options.lastDarkframeDir);
fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
fc.add_button( Gtk::StockID("gtk-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 pp=mselected[i]->thumbnail->getProcParams();
pp.raw.dark_frame= fc.get_filename();
pp.raw.df_autoselect= false;
mselected[i]->thumbnail->setProcParams(pp,NULL,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::dfm.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"), 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,NULL,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("Flat Field",Gtk::FILE_CHOOSER_ACTION_OPEN );
FileChooserLastFolderPersister persister(&fc, options.lastFlatfieldDir);
fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
fc.add_button( Gtk::StockID("gtk-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 pp=mselected[i]->thumbnail->getProcParams();
pp.raw.ff_file= fc.get_filename();
pp.raw.ff_AutoSelect= false;
mselected[i]->thumbnail->setProcParams(pp,NULL,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"), 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==execcustprof) {
if (!mselected.empty() && bppcl)
bppcl->beginBatchPParamsChange(mselected.size());
for (size_t i=0; i<mselected.size(); i++) {
mselected[i]->thumbnail->createProcParamsForUpdate (false, true);
// Empty run to update the thumb
rtengine::procparams::ProcParams params = mselected[i]->thumbnail->getProcParams ();
mselected[i]->thumbnail->setProcParams (params, NULL, FILEBROWSER);
}
if (!mselected.empty() && bppcl)
bppcl->endBatchPParamsChange();
} else if (m==clearFromCache) {
for (size_t i=0; i<mselected.size(); i++)
tbl->clearFromCacheRequested (mselected, false);
//queue_draw ();
}
else if (m==clearFromCacheFull) {
for (size_t i=0; i<mselected.size(); i++)
tbl->clearFromCacheRequested (mselected, true);
//queue_draw ();
} else if (miOpenDefaultViewer!=NULL && m==miOpenDefaultViewer) {
openDefaultViewer(1);
}
}
void FileBrowser::copyProfile () {
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (selected.size()==1)
clipboard.setProcParams ((static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->getProcParams());
}
void FileBrowser::pasteProfile () {
if (clipboard.hasProcParams()) {
std::vector<FileBrowserEntry*> mselected;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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
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;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
for (unsigned int i=0; i<selected.size(); i++)
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
}
if (!tbl || mselected.empty())
return;
int i = partialPasteDlg.run ();
if (i == Gtk::RESPONSE_OK) {
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, initialized to the thumb's ProcParams
mselected[i]->thumbnail->createProcParamsForUpdate(false,false); // this can execute customprofilebuilder to generate param file
rtengine::procparams::PartialProfile cbPartProf = clipboard.getPartialProfile();
rtengine::procparams::PartialProfile pastedPartProf(&mselected[i]->thumbnail->getProcParams (), NULL);
// 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
mselected[i]->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER);
pastedPartProf.deleteInstance();
}
if (!mselected.empty() && bppcl)
bppcl->endBatchPParamsChange();
queue_draw ();
}
partialPasteDlg.hide ();
}
}
void FileBrowser::openDefaultViewer (int destination) {
bool success=true;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (selected.size()==1)
success=(static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->openDefaultViewer(destination);
}
if (!success) {
Gtk::MessageDialog msgd (M("MAIN_MSG_IMAGEUNPROCESSED"), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.run ();
}
}
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;
bool altgr = event->state & GDK_MOD2_MASK;
if ((event->keyval==GDK_C || event->keyval==GDK_c) && ctrl) {
copyProfile ();
return true;
}
else if ((event->keyval==GDK_V || event->keyval==GDK_v) && ctrl && !shift) {
pasteProfile ();
return true;
}
else if ((event->keyval==GDK_V || event->keyval==GDK_v) && ctrl && shift) {
partPasteProfile ();
return true;
}
else if (event->keyval==GDK_Delete && !shift) {
menuItemActivated (trash);
return true;
}
else if (event->keyval==GDK_Delete && shift) {
menuItemActivated (untrash);
return true;
}
else if ((event->keyval==GDK_B || event->keyval==GDK_b) && ctrl) {
menuItemActivated (develop);
return true;
}
else if ((event->keyval==GDK_A || event->keyval==GDK_a) && ctrl) {
menuItemActivated (selall);
return true;
}
else if (event->keyval==GDK_F2 && !ctrl) {
menuItemActivated (rename);
return true;
}
else if (event->keyval==GDK_F3 && !(ctrl || shift || alt)) { // open Previous image from FileBrowser perspective
FileBrowser::openPrevImage ();
return true;
}
else if (event->keyval==GDK_F4 && !(ctrl || shift || alt)) { // open Next image from FileBrowser perspective
FileBrowser::openNextImage ();
return true;
}
else if (event->keyval==GDK_Left) {
selectPrev (1, shift);
return true;
}
else if (event->keyval==GDK_Right) {
selectNext (1, shift);
return true;
}
else if (event->keyval==GDK_Up) {
selectPrev (numOfCols, shift);
return true;
}
else if (event->keyval==GDK_Down) {
selectNext (numOfCols, shift);
return true;
}
else if (event->keyval==GDK_Home) {
selectFirst (shift);
return true;
}
else if (event->keyval==GDK_End) {
selectLast (shift);
return true;
}
else if (event->keyval==GDK_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;
}
else if (event->keyval==GDK_Page_Up) {
scrollPage(GDK_SCROLL_UP);
return true;
}
else if (event->keyval==GDK_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 && inTabMode)
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 && inTabMode)
return std::max(std::min(options.thumbSizeTab, 800), 10);
else
return std::max(std::min(options.thumbSize, 800), 10);
}
void FileBrowser::applyMenuItemActivated (ProfileStoreLabel *label) {
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
const rtengine::procparams::PartialProfile* partProfile = profileStore.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) {
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (!tbl || selected.empty())
return;
}
const rtengine::procparams::PartialProfile* srcProfiles = profileStore.getProfile (label->entry);
if (srcProfiles->pparams) {
if (partialPasteDlg.run()==Gtk::RESPONSE_OK) {
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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);
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;
{
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW); // Don't make this a writer lock! HOMBRE: Why? 'selected' is modified here
#endif
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 = NULL;
selchanged = true;
}
}
}
if (selchanged)
notifySelectionListener ();
redraw ();
}
bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) { // true -> entry complies filter
FileBrowserEntry* entry = static_cast<FileBrowserEntry*>(entryb);
// return false if basic filter settings are not satisfied
if ((filter.showRanked[entry->thumbnail->getRank()]==false ) ||
(filter.showCLabeled[entry->thumbnail->getColorLabel()]==false ) ||
((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()==1 && !filter.showTrash) ||
(entry->thumbnail->getStage()==0 && !filter.showNotTrash))
return false;
// return false is query is not satisfied
if (!filter.queryFileName.empty()){
// check if image's FileName contains queryFileName (case insensitive)
// TODO should we provide case-sensitive search option via preferences?
Glib::ustring FileName;
FileName = Glib::path_get_basename (entry->thumbnail->getFileName());
FileName = FileName.uppercase();
//printf("FileBrowser::checkFilter FileName = '%s'; find() result= %i \n",FileName.c_str(), FileName.find(filter.queryFileName.uppercase()));
// Consider that queryFileName consist of comma separated values (FilterString)
// Evaluate if ANY of these FilterString are contained in the filename
// This will construct OR filter within the filter.queryFileName
int iFilenameMatch=0;
std::vector<Glib::ustring> vFilterStrings = Glib::Regex::split_simple(",", filter.queryFileName.uppercase());
for(int i=0; i<vFilterStrings.size(); i++) {
// ignore empty vFilterStrings. Otherwise filter will always return true if
// e.g. filter.queryFileName ends on "," and will stop being a filter
if (!vFilterStrings.at(i).empty()){
if (FileName.find(vFilterStrings.at(i))!=-1) iFilenameMatch++;
}
}
if (iFilenameMatch==0) //none of the vFilterStrings found in FileName
return false;
/*experimental Regex support, this is unlikely to be useful to photographers*/
//bool matchfound=Glib::Regex::match_simple(filter.queryFileName.uppercase(),FileName);
//if (!matchfound) return false;
}
// check exif filter
const CacheImageData* cfs = entry->thumbnail->getCacheImageData();
double tol = 0.01;
double tol2 = 1e-8;
if (!filter.exifFilterEnabled)
return true;
Glib::ustring camera(cfs->getCamera());
if (!cfs->exifValid)
return (!filter.exifFilter.filterCamera || filter.exifFilter.cameras.count(camera)>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::ImageMetaData::shutterFromString(rtengine::ImageMetaData::shutterToString(cfs->shutter)) >= filter.exifFilter.shutterFrom-tol2 && rtengine::ImageMetaData::shutterFromString(rtengine::ImageMetaData::shutterToString(cfs->shutter)) <= filter.exifFilter.shutterTo+tol2))
&& (!filter.exifFilter.filterFNumber || (rtengine::ImageMetaData::apertureFromString(rtengine::ImageMetaData::apertureToString(cfs->fnumber)) >= filter.exifFilter.fnumberFrom-tol2 && rtengine::ImageMetaData::apertureFromString(rtengine::ImageMetaData::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(camera)>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()==1)
continue;
tbe[i]->thumbnail->setStage (1);
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()==0)
continue;
tbe[i]->thumbnail->setStage (0);
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::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;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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()==0)
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 () {
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
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 ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// this will require a read access
notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// scroll to the selected position
double h1, v1;
getScrollPosition(h1,v1);
double h2=selected[0]->getStartX();
double v2=selected[0]->getStartY();
Thumbnail* thumb = (static_cast<FileBrowserEntry*>(fd[k]))->thumbnail;
int minWidth = get_width()-fd[k]->getMinimalWidth();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// scroll only when selected[0] is outside of the displayed bounds
if (h2+minWidth-h1 > get_width())
setScrollPosition(h2-minWidth,v2);
if (h1>h2)
setScrollPosition(h2,v2);
// open the selected image
std::vector<Thumbnail*> entries;
entries.push_back (thumb);
tbl->openRequested (entries);
return;
}
}
}
}
}
}
}
void FileBrowser::openPrevImage () {
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
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 (size_t k=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 ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// this will require a read access
notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// scroll to the selected position
double h1, v1;
getScrollPosition(h1,v1);
double h2=selected[0]->getStartX();
double v2=selected[0]->getStartY();
Thumbnail* thumb = (static_cast<FileBrowserEntry*>(fd[k]))->thumbnail;
int minWidth = get_width()-fd[k]->getMinimalWidth();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// scroll only when selected[0] is outside of the displayed bounds
if (h2+minWidth-h1 > get_width())
setScrollPosition(h2-minWidth,v2);
if (h1>h2)
setScrollPosition(h2,v2);
// open the selected image
std::vector<Thumbnail*> entries;
entries.push_back (thumb);
tbl->openRequested (entries);
return;
}
}
}
}
}
}
}
void FileBrowser::selectImage (Glib::ustring fname) {
// need to clear the filter in filecatalog
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
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 ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// this will require a read access
notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// scroll to the selected position
double h=selected[0]->getStartX();
double v=selected[0]->getStartY();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
setScrollPosition(h,v);
return;
}
}
}
}
void FileBrowser::openNextPreviousEditorImage (Glib::ustring fname, eRTNav nextPrevious) {
// let FileBrowser acquire Editor's perspective
selectImage (fname);
// now switch to the requested image
if (nextPrevious==NAV_NEXT)
openNextImage();
else if (nextPrevious==NAV_PREVIOUS)
openPrevImage();
}
int refreshThumbImagesUI (void* data) {
(static_cast<FileBrowser*>(data))->_thumbRearrangementNeeded ();
return 0;
}
void FileBrowser::_thumbRearrangementNeeded () {
refreshThumbImages (); // arrangeFiles is NOT enough
}
void FileBrowser::thumbRearrangementNeeded () {
// refreshThumbImagesUI will handle thread safety itself
g_idle_add (refreshThumbImagesUI, this);
}
void FileBrowser::selectionChanged () {
notifySelectionListener ();
}
void FileBrowser::notifySelectionListener () {
if (tbl) {
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
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::updateProfileList () {
// submenu applmenu
int p = 0;
const std::vector<const ProfileStoreEntry*> *profEntries = profileStore.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.releaseFileList();
subMenuList.clear();
}