911 lines
31 KiB
C++
911 lines
31 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* RawTherapee is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* RawTherapee is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <filecatalog.h>
|
|
#include <options.h>
|
|
#include <cachemanager.h>
|
|
#include <multilangmgr.h>
|
|
#include <guiutils.h>
|
|
#include <glib/gstdio.h>
|
|
#include <iostream>
|
|
#include <renamedlg.h>
|
|
#include <thumbimageupdater.h>
|
|
#include <safegtk.h>
|
|
|
|
#define CHECKTIME 2000
|
|
|
|
extern Glib::ustring argv0;
|
|
|
|
#ifdef _WIN32
|
|
int _directoryUpdater (void* cat) {
|
|
|
|
((FileCatalog*)cat)->checkCounter++;
|
|
if (((FileCatalog*)cat)->checkCounter==2) {
|
|
gdk_threads_enter ();
|
|
((FileCatalog*)cat)->reparseDirectory ();
|
|
gdk_threads_leave ();
|
|
}
|
|
return 1;
|
|
}
|
|
#endif
|
|
|
|
FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb) : listener(NULL), fslistener(NULL), hasValidCurrentEFS(false), filterPanel(NULL), coarsePanel(cp), toolBar(tb) {
|
|
|
|
previewLoader.setPreviewLoaderListener (this);
|
|
|
|
// construct and initialize thumbnail browsers
|
|
fileBrowser = new FileBrowser();
|
|
fileBrowser->setFileBrowserListener (this);
|
|
fileBrowser->setArrangement (ThumbBrowserBase::TB_Vertical);
|
|
fileBrowser->show ();
|
|
|
|
// construct trash panel with the extra "empty trash" button
|
|
trashButtonBox = new Gtk::VBox;
|
|
Gtk::Button* emptyT = new Gtk::Button (M("FILEBROWSER_EMPTYTRASH"));
|
|
emptyT->set_tooltip_text (M("FILEBROWSER_EMPTYTRASHHINT"));
|
|
emptyT->set_image (*(new Gtk::Image (Gtk::StockID("gtk-delete"), Gtk::ICON_SIZE_BUTTON)));
|
|
emptyT->signal_pressed().connect (sigc::mem_fun(*this, &FileCatalog::emptyTrash));
|
|
trashButtonBox->pack_start (*emptyT, Gtk::PACK_SHRINK, 4);
|
|
emptyT->show ();
|
|
trashButtonBox->show ();
|
|
|
|
// setup button bar
|
|
buttonBar = new Gtk::HBox ();
|
|
pack_start (*buttonBar, Gtk::PACK_SHRINK);
|
|
|
|
buttonBar->pack_start (*(new Gtk::VSeparator), Gtk::PACK_SHRINK);
|
|
bDir = new Gtk::ToggleButton ();
|
|
bDir->set_active (true);
|
|
bDir->set_image (*(new Gtk::Image (argv0+"/images/folder.png")));
|
|
bDir->set_relief (Gtk::RELIEF_NONE);
|
|
bDir->set_tooltip_text (M("FILEBROWSER_SHOWDIRHINT"));
|
|
bDir->signal_button_press_event().connect (sigc::mem_fun(*this, &FileCatalog::capture_event),false);
|
|
bCateg[0] = bDir->signal_toggled().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::categoryButtonToggled), bDir));
|
|
buttonBar->pack_start (*bDir, Gtk::PACK_SHRINK);
|
|
buttonBar->pack_start (*(new Gtk::VSeparator), Gtk::PACK_SHRINK);
|
|
|
|
bUnRanked = new Gtk::ToggleButton ();
|
|
bUnRanked->set_active (false);
|
|
bUnRanked->set_image (*(new Gtk::Image (argv0+"/images/unrated.png")));
|
|
bUnRanked->set_relief (Gtk::RELIEF_NONE);
|
|
bUnRanked->set_tooltip_text (M("FILEBROWSER_SHOWUNRANKHINT"));
|
|
bCateg[1] = bUnRanked->signal_toggled().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::categoryButtonToggled), bUnRanked));
|
|
buttonBar->pack_start (*bUnRanked, Gtk::PACK_SHRINK);
|
|
bUnRanked->signal_button_press_event().connect (sigc::mem_fun(*this, &FileCatalog::capture_event),false);
|
|
|
|
for (int i=0; i<5; i++) {
|
|
iranked[i] = new Gtk::Image (argv0+"/images/rated.png");
|
|
igranked[i] = new Gtk::Image (argv0+"/images/grayrated.png");
|
|
iranked[i]->show ();
|
|
igranked[i]->show ();
|
|
bRank[i] = new Gtk::ToggleButton ();
|
|
bRank[i]->set_image (*igranked[i]);
|
|
bRank[i]->set_relief (Gtk::RELIEF_NONE);
|
|
buttonBar->pack_start (*bRank[i], Gtk::PACK_SHRINK);
|
|
bCateg[i+2] = bRank[i]->signal_toggled().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::categoryButtonToggled), bRank[i]));
|
|
bRank[i]->signal_button_press_event().connect (sigc::mem_fun(*this, &FileCatalog::capture_event),false);
|
|
}
|
|
bRank[0]->set_tooltip_text (M("FILEBROWSER_SHOWRANK1HINT"));
|
|
bRank[1]->set_tooltip_text (M("FILEBROWSER_SHOWRANK2HINT"));
|
|
bRank[2]->set_tooltip_text (M("FILEBROWSER_SHOWRANK3HINT"));
|
|
bRank[3]->set_tooltip_text (M("FILEBROWSER_SHOWRANK4HINT"));
|
|
bRank[4]->set_tooltip_text (M("FILEBROWSER_SHOWRANK5HINT"));
|
|
buttonBar->pack_start (*(new Gtk::VSeparator), Gtk::PACK_SHRINK);
|
|
|
|
iTrashEmpty = new Gtk::Image(argv0+"/images/trash-show-empty.png");
|
|
iTrashFull = new Gtk::Image(argv0+"/images/trash-show-full.png");
|
|
|
|
bTrash = new Gtk::ToggleButton ();
|
|
bTrash->set_image (*iTrashEmpty);
|
|
bTrash->set_relief (Gtk::RELIEF_NONE);
|
|
bTrash->set_tooltip_text (M("FILEBROWSER_SHOWTRASHHINT"));
|
|
bCateg[7] = bTrash->signal_toggled().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::categoryButtonToggled), bTrash));
|
|
bTrash->signal_button_press_event().connect (sigc::mem_fun(*this, &FileCatalog::capture_event),false);
|
|
buttonBar->pack_start (*bTrash, Gtk::PACK_SHRINK);
|
|
buttonBar->pack_start (*(new Gtk::VSeparator), Gtk::PACK_SHRINK);
|
|
fileBrowser->trash_changed().connect( sigc::mem_fun(*this, &FileCatalog::trashChanged) );
|
|
|
|
categoryButtons[0] = bDir;
|
|
categoryButtons[1] = bUnRanked;
|
|
for (int i=0; i<5; i++)
|
|
categoryButtons[i+2] = bRank[i];
|
|
categoryButtons[7] = bTrash;
|
|
|
|
exifInfo = Gtk::manage(new Gtk::ToggleButton ());
|
|
exifInfo->set_image (*(new Gtk::Image (argv0+"/images/info.png")));
|
|
exifInfo->set_relief (Gtk::RELIEF_NONE);
|
|
exifInfo->set_tooltip_text (M("FILEBROWSER_SHOWEXIFINFO"));
|
|
exifInfo->set_active( options.showFileNames );
|
|
exifInfo->signal_toggled().connect(sigc::mem_fun(*this, &FileCatalog::exifInfoButtonToggled));
|
|
buttonBar->pack_start (*exifInfo, Gtk::PACK_SHRINK);
|
|
|
|
// thumbnail zoom
|
|
Gtk::HBox* zoomBox = new Gtk::HBox ();
|
|
zoomInButton = new Gtk::Button ();
|
|
zoomInButton->set_image (*(new Gtk::Image (Gtk::StockID("gtk-zoom-in"), Gtk::ICON_SIZE_SMALL_TOOLBAR)));
|
|
zoomInButton->signal_pressed().connect (sigc::mem_fun(*this, &FileCatalog::zoomIn));
|
|
zoomInButton->set_relief (Gtk::RELIEF_NONE);
|
|
zoomInButton->set_tooltip_text (M("FILEBROWSER_ZOOMINHINT"));
|
|
zoomBox->pack_end (*zoomInButton, Gtk::PACK_SHRINK);
|
|
zoomOutButton = new Gtk::Button ();
|
|
zoomOutButton->set_image (*(new Gtk::Image (Gtk::StockID("gtk-zoom-out"), Gtk::ICON_SIZE_SMALL_TOOLBAR)));
|
|
zoomOutButton->signal_pressed().connect (sigc::mem_fun(*this, &FileCatalog::zoomOut));
|
|
zoomOutButton->set_relief (Gtk::RELIEF_NONE);
|
|
zoomOutButton->set_tooltip_text (M("FILEBROWSER_ZOOMOUTHINT"));
|
|
zoomBox->pack_end (*zoomOutButton, Gtk::PACK_SHRINK);
|
|
|
|
// add default panel
|
|
hBox = new Gtk::HBox ();
|
|
hBox->show ();
|
|
hBox->pack_end (*fileBrowser);
|
|
fileBrowser->applyFilter (getFilter());
|
|
pack_start (*hBox);
|
|
|
|
buttonBar2 = new Gtk::HBox ();
|
|
pack_end (*buttonBar2, Gtk::PACK_SHRINK);
|
|
progressBar = new Gtk::ProgressBar ();
|
|
buttonBar2->pack_start (*progressBar, Gtk::PACK_SHRINK, 4);
|
|
progressBar->set_size_request (-1, 16);
|
|
|
|
buttonBar->pack_start (*zoomBox, Gtk::PACK_SHRINK);
|
|
|
|
buttonBar->pack_end (*coarsePanel, Gtk::PACK_SHRINK);
|
|
buttonBar->pack_end (*Gtk::manage(new Gtk::VSeparator), Gtk::PACK_SHRINK, 4);
|
|
buttonBar->pack_end (*toolBar, Gtk::PACK_SHRINK);
|
|
buttonBar->pack_end (*Gtk::manage(new Gtk::VSeparator), Gtk::PACK_SHRINK, 4);
|
|
|
|
enabled = true;
|
|
|
|
lastScrollPos = 0;
|
|
for (int i=0; i<8; i++) {
|
|
hScrollPos[i] = 0;
|
|
vScrollPos[i] = 0;
|
|
}
|
|
|
|
selectedDirectory = "";
|
|
#ifdef _WIN32
|
|
wdMonitor = NULL;
|
|
checkCounter = 2;
|
|
g_timeout_add (CHECKTIME, _directoryUpdater, this);
|
|
#endif
|
|
}
|
|
|
|
bool FileCatalog::capture_event(GdkEventButton* event){
|
|
// need to record modifiers on the button press, because signal_toggled does not pass the event.
|
|
modifierKey = event->state;
|
|
return false;
|
|
}
|
|
|
|
void FileCatalog::exifInfoButtonToggled()
|
|
{
|
|
options.showFileNames = exifInfo->get_active();
|
|
fileBrowser->refreshThumbImages ();
|
|
}
|
|
|
|
void FileCatalog::on_realize() {
|
|
|
|
Gtk::VBox::on_realize();
|
|
Pango::FontDescription fontd = get_pango_context()->get_font_description ();
|
|
fileBrowser->get_pango_context()->set_font_description (fontd);
|
|
// batchQueue->get_pango_context()->set_font_description (fontd);
|
|
}
|
|
|
|
void FileCatalog::closeDir () {
|
|
|
|
if (filterPanel)
|
|
filterPanel->set_sensitive (false);
|
|
|
|
#ifndef _WIN32
|
|
if (dirMonitor)
|
|
dirMonitor->cancel ();
|
|
#else
|
|
if (wdMonitor) {
|
|
delete wdMonitor;
|
|
wdMonitor = NULL;
|
|
}
|
|
#endif
|
|
// terminate thumbnail preview loading
|
|
previewLoader.terminate ();
|
|
|
|
// terminate thumbnail updater
|
|
thumbImageUpdater.terminate ();
|
|
|
|
// remove entries
|
|
fileBrowser->close ();
|
|
fileNameList.clear ();
|
|
|
|
dirEFS.clear ();
|
|
hasValidCurrentEFS = false;
|
|
selectedDirectory = "";
|
|
redrawAll ();
|
|
}
|
|
|
|
std::vector<Glib::ustring> FileCatalog::getFileList () {
|
|
|
|
std::vector<Glib::ustring> names;
|
|
Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path (selectedDirectory);
|
|
safe_build_file_list (dir, names, selectedDirectory);
|
|
return names;
|
|
}
|
|
|
|
void FileCatalog::dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile) {
|
|
|
|
try {
|
|
Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path (dirname);
|
|
|
|
if (!dir)
|
|
return;
|
|
closeDir ();
|
|
previewsToLoad = 0;
|
|
previewsLoaded = 0;
|
|
// if openfile exists, we have to open it first (it is a command line argument)
|
|
if (openfile!="")
|
|
addAndOpenFile (openfile);
|
|
|
|
selectedDirectory = dir->get_parse_name();
|
|
fileNameList = getFileList ();
|
|
|
|
for (unsigned int i=0; i<fileNameList.size(); i++) {
|
|
Glib::RefPtr<Gio::File> f = Gio::File::create_for_path(fileNameList[i]);
|
|
if (f->get_parse_name() != openfile) // if we opened a file at the beginning don't add it again
|
|
checkAndAddFile (f);
|
|
}
|
|
|
|
_refreshProgressBar ();
|
|
previewLoader.process ();
|
|
|
|
#ifdef _WIN32
|
|
wdMonitor = new WinDirMonitor (selectedDirectory, this);
|
|
#elif defined __APPLE__
|
|
printf("TODO fix dir->monitor_directory () for OSX\n");
|
|
#else
|
|
dirMonitor = dir->monitor_directory ();
|
|
dirMonitor->signal_changed().connect (sigc::bind(sigc::mem_fun(*this, &FileCatalog::on_dir_changed), false));
|
|
#endif
|
|
}
|
|
catch (Glib::Exception& ex) {
|
|
std::cout << ex.what();
|
|
}
|
|
}
|
|
|
|
void FileCatalog::_refreshProgressBar () {
|
|
|
|
// check if progress bar is visible
|
|
/* Glib::ListHandle<Gtk::Widget*> list = buttonBar2->get_children ();
|
|
Glib::ListHandle<Gtk::Widget*>::iterator i = list.begin ();
|
|
for (; i!=list.end() && *i!=progressBar; i++);
|
|
if (i==list.end()) {
|
|
buttonBar2->pack_start (*progressBar, Gtk::PACK_SHRINK, 4);
|
|
buttonBar2->reorder_child (*progressBar, 2);
|
|
}
|
|
*/
|
|
progressBar->show ();
|
|
if (previewsToLoad>0)
|
|
progressBar->set_fraction ((double)previewsLoaded / previewsToLoad);
|
|
else
|
|
progressBar->set_fraction (1.0);
|
|
}
|
|
|
|
int refreshpb (void* data) {
|
|
|
|
gdk_threads_enter ();
|
|
((FileCatalog*)data)->_refreshProgressBar ();
|
|
gdk_threads_leave ();
|
|
return 0;
|
|
}
|
|
|
|
void FileCatalog::previewReady (FileBrowserEntry* fdn) {
|
|
|
|
// put it into the "full directory" browser
|
|
fdn->setImageAreaToolListener (iatlistener);
|
|
fileBrowser->addEntry (fdn);
|
|
|
|
// update exif filter settings (minimal & maximal values of exif tags, cameras, lenses, etc...)
|
|
const CacheImageData* cfs = fdn->thumbnail->getCacheImageData();
|
|
if (cfs->exifValid) {
|
|
if (cfs->fnumber < dirEFS.fnumberFrom)
|
|
dirEFS.fnumberFrom = cfs->fnumber;
|
|
if (cfs->fnumber > dirEFS.fnumberTo)
|
|
dirEFS.fnumberTo = cfs->fnumber;
|
|
if (cfs->shutter < dirEFS.shutterFrom)
|
|
dirEFS.shutterFrom = cfs->shutter;
|
|
if (cfs->shutter > dirEFS.shutterTo)
|
|
dirEFS.shutterTo = cfs->shutter;
|
|
if (cfs->iso>0 && (int)cfs->iso < dirEFS.isoFrom)
|
|
dirEFS.isoFrom = (int)cfs->iso;
|
|
if (cfs->iso>0 && (int)cfs->iso > dirEFS.isoTo)
|
|
dirEFS.isoTo = (int)cfs->iso;
|
|
if (cfs->focalLen < dirEFS.focalFrom)
|
|
dirEFS.focalFrom = cfs->focalLen;
|
|
if (cfs->focalLen > dirEFS.focalTo)
|
|
dirEFS.focalTo = cfs->focalLen;
|
|
}
|
|
dirEFS.cameras.insert (cfs->camera);
|
|
dirEFS.lenses.insert (cfs->lens);
|
|
previewsLoaded++;
|
|
g_idle_add (refreshpb, this);
|
|
}
|
|
|
|
int prevfinished (void* data) {
|
|
|
|
gdk_threads_enter();
|
|
((FileCatalog*)data)->_previewsFinished ();
|
|
gdk_threads_leave();
|
|
return 0;
|
|
}
|
|
|
|
void FileCatalog::_previewsFinished () {
|
|
|
|
redrawAll ();
|
|
previewsToLoad = 0;
|
|
previewsLoaded = 0;
|
|
// removeIfThere (buttonBar2, progressBar);
|
|
progressBar->hide ();
|
|
if (filterPanel) {
|
|
filterPanel->set_sensitive (true);
|
|
if ( !hasValidCurrentEFS ){
|
|
currentEFS = dirEFS;
|
|
filterPanel->setFilter ( dirEFS,true );
|
|
}else {
|
|
filterPanel->setFilter ( currentEFS,false );
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileCatalog::previewsFinished () {
|
|
|
|
if (!hasValidCurrentEFS)
|
|
currentEFS = dirEFS;
|
|
g_idle_add (prevfinished, this);
|
|
}
|
|
|
|
void PreviewLoader::remove (Glib::ustring fname) {
|
|
std::list<DirEntry>::iterator i;
|
|
for (i=jqueue.begin(); i!=jqueue.end(); i++)
|
|
if (i->fullName==fname)
|
|
break;
|
|
if (i!=jqueue.end())
|
|
jqueue.erase (i);
|
|
}
|
|
|
|
void PreviewLoader::start () {
|
|
|
|
jqueue.sort ();
|
|
}
|
|
|
|
void PreviewLoader::process (DirEntry& current) {
|
|
|
|
if (Glib::file_test (current.fullName, Glib::FILE_TEST_EXISTS)) {
|
|
Thumbnail* tmb = cacheMgr.getEntry (current.fullName);
|
|
if (tmb && pl)
|
|
pl->previewReady (new FileBrowserEntry (tmb, current.fullName));
|
|
}
|
|
}
|
|
|
|
void PreviewLoader::end () {
|
|
|
|
if (pl)
|
|
pl->previewsFinished ();
|
|
}
|
|
|
|
void FileCatalog::setEnabled (bool e) {
|
|
|
|
enabled = e;
|
|
}
|
|
|
|
void FileCatalog::redrawAll () {
|
|
|
|
fileBrowser->queue_draw ();
|
|
}
|
|
|
|
void FileCatalog::refreshAll () {
|
|
|
|
fileBrowser->refreshThumbImages ();
|
|
}
|
|
|
|
void FileCatalog::_openImage (std::vector<Thumbnail*> tmb) {
|
|
|
|
if (enabled && listener!=NULL) {
|
|
previewLoader.stop ();
|
|
thumbImageUpdater.stop ();
|
|
for (unsigned int i=0; i<tmb.size(); i++) {
|
|
if (editedFiles.find (tmb[i]->getFileName())==editedFiles.end())
|
|
listener->fileSelected (tmb[i]);
|
|
tmb[i]->decreaseRef ();
|
|
}
|
|
previewLoader.process ();
|
|
thumbImageUpdater.process ();
|
|
}
|
|
}
|
|
|
|
struct FCOIParams {
|
|
FileCatalog* catalog;
|
|
std::vector<Thumbnail*> tmb;
|
|
};
|
|
|
|
int fcopenimg (void* p) {
|
|
|
|
gdk_threads_enter ();
|
|
FCOIParams* params = (FCOIParams*)p;
|
|
params->catalog->_openImage (params->tmb);
|
|
delete params;
|
|
gdk_threads_leave ();
|
|
return 0;
|
|
}
|
|
|
|
void FileCatalog::openRequested (std::vector<Thumbnail*> tmb) {
|
|
|
|
FCOIParams* params = new FCOIParams;
|
|
params->catalog = this;
|
|
params->tmb = tmb;
|
|
for (unsigned int i=0; i<tmb.size(); i++)
|
|
tmb[i]->increaseRef ();
|
|
g_idle_add (fcopenimg, params);
|
|
}
|
|
|
|
void FileCatalog::deleteRequested (std::vector<FileBrowserEntry*> tbe) {
|
|
|
|
if (tbe.size()==0)
|
|
return;
|
|
|
|
Gtk::MessageDialog msd (M("FILEBROWSER_DELETEDLGLABEL"), false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true);
|
|
msd.set_secondary_text(Glib::ustring::compose (M("FILEBROWSER_DELETEDLGMSG"), tbe.size()));
|
|
|
|
if (msd.run()==Gtk::RESPONSE_YES) {
|
|
for (unsigned int i=0; i<tbe.size(); i++) {
|
|
Glib::ustring fname = tbe[i]->filename;
|
|
// remove from browser
|
|
FileBrowserEntry* t = fileBrowser->delEntry (fname);
|
|
// t->thumbnail->decreaseRef ();
|
|
delete t;
|
|
// remove from cache
|
|
cacheMgr.deleteEntry (fname);
|
|
// delete from file system
|
|
::g_remove (fname.c_str());
|
|
// delete paramfile if found
|
|
::g_remove (Glib::ustring(fname+paramFileExtension).c_str());
|
|
::g_remove (Glib::ustring(removeExtension(fname)+paramFileExtension).c_str());
|
|
// delete .thm file
|
|
::g_remove (Glib::ustring(removeExtension(fname)+".thm").c_str());
|
|
::g_remove (Glib::ustring(removeExtension(fname)+".THM").c_str());
|
|
}
|
|
redrawAll ();
|
|
}
|
|
}
|
|
|
|
void FileCatalog::developRequested (std::vector<FileBrowserEntry*> tbe) {
|
|
|
|
if (listener) {
|
|
thumbImageUpdater.stop ();
|
|
for (unsigned int i=0; i<tbe.size(); i++) {
|
|
rtengine::procparams::ProcParams params = tbe[i]->thumbnail->getProcParams();
|
|
rtengine::ProcessingJob* pjob = rtengine::ProcessingJob::create (tbe[i]->filename, tbe[i]->thumbnail->getType()==FT_Raw, params);
|
|
double tmpscale;
|
|
rtengine::IImage8* img = tbe[i]->thumbnail->processThumbImage (params, options.maxThumbnailHeight, tmpscale);
|
|
if (img) {
|
|
int pw = img->getWidth ();
|
|
int ph = img->getHeight ();
|
|
guint8* prev = new guint8 [pw*ph*3];
|
|
memcpy (prev, img->getData (), pw*ph*3);
|
|
img->free();
|
|
listener->addBatchQueueJob (new BatchQueueEntry (pjob, params, tbe[i]->filename, prev, pw, ph, tbe[i]->thumbnail));
|
|
}
|
|
else {
|
|
int pw, ph;
|
|
tbe[i]->thumbnail->getThumbnailSize (pw, ph);
|
|
listener->addBatchQueueJob (new BatchQueueEntry (pjob, params, tbe[i]->filename, NULL, pw, ph, tbe[i]->thumbnail));
|
|
}
|
|
}
|
|
thumbImageUpdater.process ();
|
|
}
|
|
}
|
|
|
|
void FileCatalog::renameRequested (std::vector<FileBrowserEntry*> tbe) {
|
|
|
|
RenameDialog* renameDlg = new RenameDialog ((Gtk::Window*)get_toplevel());
|
|
|
|
for (unsigned int i=0; i<tbe.size(); i++) {
|
|
renameDlg->initName (Glib::path_get_basename (tbe[i]->filename), tbe[i]->thumbnail->getCacheImageData());
|
|
|
|
Glib::ustring ofname = tbe[i]->filename;
|
|
Glib::ustring dirName = Glib::path_get_dirname (tbe[i]->filename);
|
|
Glib::ustring baseName = Glib::path_get_basename (tbe[i]->filename);
|
|
|
|
if (renameDlg->run ()== Gtk::RESPONSE_OK) {
|
|
Glib::ustring nBaseName = renameDlg->getNewName ();
|
|
// if path has directory components, exit
|
|
if (Glib::path_get_dirname (nBaseName) != ".")
|
|
continue;
|
|
// if no extension is given, concatenate the extension of the original file
|
|
Glib::ustring ext = getExtension (nBaseName);
|
|
if (ext=="")
|
|
nBaseName += "." + getExtension (baseName);
|
|
Glib::ustring nfname = Glib::build_filename (dirName, nBaseName);
|
|
if (!::g_rename (ofname.c_str(), nfname.c_str())) {
|
|
cacheMgr.renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname);
|
|
reparseDirectory ();
|
|
}
|
|
renameDlg->hide ();
|
|
}
|
|
}
|
|
delete renameDlg;
|
|
/* // ask for new file name
|
|
Gtk::Dialog dialog (M("FILEBROWSER_RENAMEDLGLABEL"), *((Gtk::Window*)get_toplevel()), true, true);
|
|
|
|
dialog.add_button (Gtk::Stock::OK, Gtk::RESPONSE_OK);
|
|
dialog.add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
|
|
|
|
Gtk::Label l;
|
|
dialog.get_vbox()->pack_start (l, Gtk::PACK_SHRINK);
|
|
|
|
Gtk::Entry nfentry;
|
|
|
|
dialog.get_vbox()->pack_start (nfentry, Gtk::PACK_SHRINK);
|
|
dialog.get_vbox()->show_all ();
|
|
|
|
nfentry.set_activates_default (true);
|
|
dialog.set_default_response (Gtk::RESPONSE_OK);
|
|
|
|
for (int i=0; i<tbe.size(); i++) {
|
|
|
|
Glib::ustring ofname = tbe[i]->filename;
|
|
Glib::ustring dirName = Glib::path_get_dirname (tbe[i]->filename);
|
|
Glib::ustring baseName = Glib::path_get_basename (tbe[i]->filename);
|
|
|
|
l.set_markup (Glib::ustring("<big><b>") + Glib::ustring::compose (M("FILEBROWSER_RENAMEDLGMSG"), baseName) + Glib::ustring("</b></big>"));
|
|
nfentry.set_text (baseName);
|
|
nfentry.select_region (0, baseName.size());
|
|
|
|
if (dialog.run ()== Gtk::RESPONSE_OK) {
|
|
Glib::ustring nBaseName = nfentry.get_text ();
|
|
// if path has directory components, exit
|
|
if (Glib::path_get_dirname (nBaseName) != ".")
|
|
continue;
|
|
// if no extension is given, concatenate the extension of the original file
|
|
if (nBaseName.find ('.')==nBaseName.npos) {
|
|
int lastdot = baseName.find_last_of ('.');
|
|
nBaseName += "." + (lastdot!=Glib::ustring::npos ? baseName.substr (lastdot+1) : "");
|
|
}
|
|
Glib::ustring nfname = Glib::build_filename (dirName, nBaseName);
|
|
if (!::g_rename (ofname.c_str(), nfname.c_str())) {
|
|
cacheMgr.renameEntry (ofname, tbe[i]->thumbnail->getMD5(), nfname);
|
|
// the remaining part (removing old and adding new entry) is done by the directory monitor
|
|
reparseDirectory ();
|
|
// on_dir_changed (Gio::File::create_for_path (nfname), Gio::File::create_for_path (nfname), Gio::FILE_MONITOR_EVENT_CHANGED, true);
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
void FileCatalog::categoryButtonToggled (Gtk::ToggleButton* b) {
|
|
|
|
//was control key pressed
|
|
bool control_down = modifierKey & GDK_CONTROL_MASK;
|
|
|
|
//was shift key pressed
|
|
bool shift_down = modifierKey & GDK_SHIFT_MASK;
|
|
|
|
for (int i=0; i<8; i++)
|
|
bCateg[i].block (true);
|
|
|
|
//button is already toggled when entering this function, so we switch it back to its initial state
|
|
b->set_active(!b->get_active());
|
|
|
|
//if both control and shift keys were pressed, do nothing
|
|
if (!(control_down && shift_down)) {
|
|
|
|
fileBrowser->getScrollPosition (hScrollPos[lastScrollPos], vScrollPos[lastScrollPos]);
|
|
|
|
//we look how many stars are already toggled on, if any
|
|
int toggled_stars_count=0, buttons=0, start_star=0, toggled_button=0;
|
|
|
|
for (int i=0; i<8; i++) {
|
|
if (categoryButtons[i]->get_active()) {
|
|
if (i>0 && i<7) {
|
|
toggled_stars_count ++;
|
|
start_star = i;
|
|
}
|
|
buttons |= (1 << i);
|
|
}
|
|
if (categoryButtons[i] == b) toggled_button = i;
|
|
}
|
|
|
|
//if no modifier key were pressed, we can switch-on the button, and clear all others
|
|
if (!(control_down || shift_down)) {
|
|
for (int i=0; i<8; i++) {
|
|
categoryButtons[i]->set_active (i==toggled_button);
|
|
}
|
|
}
|
|
//modifier key allowed only for stars
|
|
else if (toggled_button>0 && toggled_button<7) {
|
|
if (control_down) {
|
|
//control is pressed
|
|
if (toggled_stars_count == 1 && (buttons & (1 << toggled_button))) {
|
|
//we're deselecting the only star still active, so we activate the folder filter
|
|
categoryButtons[0]->set_active(true);
|
|
//and we deselect the toggled star
|
|
categoryButtons[toggled_button]->set_active (false);
|
|
}
|
|
else if (toggled_stars_count >= 1) {
|
|
//we toggle the state of a star (eventually another one than the only one selected)
|
|
categoryButtons[toggled_button]->set_active(!categoryButtons[toggled_button]->get_active());
|
|
}
|
|
else {
|
|
//no star selected
|
|
//we deselect the 2 non star filters
|
|
if (buttons & 1 ) categoryButtons[0]->set_active(false);
|
|
if (buttons & (1 << 7)) categoryButtons[7]->set_active(false);
|
|
//and we toggle on the star
|
|
categoryButtons[toggled_button]->set_active (true);
|
|
}
|
|
}
|
|
else {
|
|
//shift is pressed, only allowed if 0 or 1 star is selected
|
|
if (!toggled_stars_count) {
|
|
//we deselect the 2 non star filters
|
|
if (buttons & 1 ) categoryButtons[0]->set_active(false);
|
|
if (buttons & (1 << 7)) categoryButtons[7]->set_active(false);
|
|
//and we set the start star to 1 (unrated images)
|
|
start_star = 1;
|
|
//we act as if one star were selected
|
|
toggled_stars_count = 1;
|
|
}
|
|
if (toggled_stars_count == 1) {
|
|
int current_star=MIN(start_star,toggled_button);
|
|
int last_star =MAX(start_star,toggled_button);
|
|
//we permute the start and the end star for the next loop
|
|
for (; current_star <= last_star; current_star++) {
|
|
//we toggle on all the star in the range
|
|
if (!(buttons & (1 << current_star))) categoryButtons[current_star]->set_active(true);
|
|
}
|
|
}
|
|
//if more than one star selected, do nothing
|
|
}
|
|
}
|
|
|
|
//so set the right images
|
|
for (int i=0; i<5; i++) {
|
|
bool active_now, active_before;
|
|
active_now = bRank[i]->get_active();
|
|
active_before = buttons & (1 << (i+2));
|
|
if ( active_now && !active_before) bRank[i]->set_image (*iranked[i]);
|
|
else if (!active_now && active_before) bRank[i]->set_image (*igranked[i]);
|
|
}
|
|
|
|
fileBrowser->applyFilter (getFilter ());
|
|
|
|
//rearrange panels according to the selected filter
|
|
removeIfThere (hBox, trashButtonBox);
|
|
if (bTrash->get_active ())
|
|
hBox->pack_start (*trashButtonBox, Gtk::PACK_SHRINK, 4);
|
|
hBox->queue_draw ();
|
|
|
|
fileBrowser->setScrollPosition (hScrollPos[lastScrollPos], vScrollPos[lastScrollPos]);
|
|
}
|
|
|
|
for (int i=0; i<8; i++)
|
|
bCateg[i].block (false);
|
|
}
|
|
|
|
BrowserFilter FileCatalog::getFilter () {
|
|
|
|
BrowserFilter filter;
|
|
filter.showRanked[0] = bDir->get_active() || bUnRanked->get_active () || bTrash->get_active ();
|
|
for (int i=1; i<=5; i++)
|
|
filter.showRanked[i] = bDir->get_active() || bRank[i-1]->get_active () || bTrash->get_active ();
|
|
filter.showTrash = bDir->get_active() || bTrash->get_active ();
|
|
filter.showNotTrash = !bTrash->get_active ();
|
|
if (!filterPanel)
|
|
filter.exifFilterEnabled = false;
|
|
else {
|
|
if (!hasValidCurrentEFS)
|
|
filter.exifFilter = dirEFS;
|
|
else
|
|
filter.exifFilter = currentEFS;
|
|
filter.exifFilterEnabled = filterPanel->isEnabled ();
|
|
}
|
|
return filter;
|
|
}
|
|
|
|
void FileCatalog::filterChanged () {
|
|
|
|
fileBrowser->applyFilter (getFilter());
|
|
}
|
|
|
|
int FileCatalog::reparseDirectory () {
|
|
|
|
if (selectedDirectory=="")
|
|
return 0;
|
|
|
|
if (!Glib::file_test (selectedDirectory, Glib::FILE_TEST_IS_DIR)) {
|
|
closeDir ();
|
|
return 0;
|
|
}
|
|
|
|
std::vector<Glib::ustring> nfileNameList = getFileList ();
|
|
|
|
// check if a thumbnailed file has been deleted
|
|
const std::vector<ThumbBrowserEntryBase*>& t = fileBrowser->getEntries ();
|
|
std::vector<Glib::ustring> fileNamesToDel;
|
|
for (unsigned int i=0; i<t.size(); i++)
|
|
if (!Glib::file_test (t[i]->filename, Glib::FILE_TEST_EXISTS))
|
|
fileNamesToDel.push_back (t[i]->filename);
|
|
for (unsigned int i=0; i<fileNamesToDel.size(); i++) {
|
|
delete fileBrowser->delEntry (fileNamesToDel[i]);
|
|
cacheMgr.deleteEntry (fileNamesToDel[i]);
|
|
}
|
|
|
|
// check if a new file has been added
|
|
for (unsigned int i=0; i<nfileNameList.size(); i++) {
|
|
bool found = false;
|
|
for (int j=0; j<fileNameList.size(); j++)
|
|
if (nfileNameList[i]==fileNameList[j]) {
|
|
found = true;
|
|
break;
|
|
}
|
|
if (!found) {
|
|
previewLoader.stop ();
|
|
checkAndAddFile (Gio::File::create_for_parse_name (nfileNameList[i]));
|
|
_refreshProgressBar ();
|
|
previewLoader.process ();
|
|
}
|
|
}
|
|
|
|
fileNameList = nfileNameList;
|
|
return 1;
|
|
}
|
|
|
|
#ifdef _WIN32
|
|
void FileCatalog::winDirChanged () {
|
|
|
|
checkCounter = 0;
|
|
}
|
|
#endif
|
|
|
|
void FileCatalog::on_dir_changed (const Glib::RefPtr<Gio::File>& file, const Glib::RefPtr<Gio::File>& other_file, Gio::FileMonitorEvent event_type, bool internal) {
|
|
|
|
if (!internal)
|
|
gdk_threads_enter();
|
|
|
|
if (event_type == Gio::FILE_MONITOR_EVENT_CREATED || event_type == Gio::FILE_MONITOR_EVENT_DELETED || event_type == Gio::FILE_MONITOR_EVENT_CHANGED)
|
|
reparseDirectory ();
|
|
|
|
if (!internal)
|
|
gdk_threads_leave();
|
|
}
|
|
|
|
void FileCatalog::checkAndAddFile (Glib::RefPtr<Gio::File> file) {
|
|
|
|
if (!file )
|
|
return;
|
|
if( !file->query_exists())
|
|
return;
|
|
Glib::RefPtr<Gio::FileInfo> info = safe_query_file_info(file);
|
|
if (info && info->get_file_type() != Gio::FILE_TYPE_DIRECTORY && (!info->is_hidden() || !options.fbShowHidden)) {
|
|
int lastdot = info->get_name().find_last_of ('.');
|
|
if (options.is_extention_enabled(lastdot!=(int)Glib::ustring::npos ? info->get_name().substr (lastdot+1) : "")){
|
|
previewLoader.add (DirEntry (file->get_parse_name()));
|
|
previewsToLoad++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileCatalog::addAndOpenFile (const Glib::ustring& fname) {
|
|
|
|
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path (fname);
|
|
if (!file )
|
|
return;
|
|
if( !file->query_exists())
|
|
return;
|
|
Glib::RefPtr<Gio::FileInfo> info = safe_query_file_info(file);
|
|
if( !info )
|
|
return;
|
|
int lastdot = info->get_name().find_last_of ('.');
|
|
if (options.is_extention_enabled(lastdot!=(int)Glib::ustring::npos ? info->get_name().substr (lastdot+1) : "")){
|
|
// if supported, load thumbnail first
|
|
Thumbnail* tmb = cacheMgr.getEntry (file->get_parse_name());
|
|
if (tmb) {
|
|
FileBrowserEntry* entry = new FileBrowserEntry (tmb, file->get_parse_name());
|
|
previewReady (entry);
|
|
// open the file
|
|
FCOIParams* params = new FCOIParams;
|
|
params->catalog = this;
|
|
params->tmb.push_back (tmb);
|
|
tmb->increaseRef ();
|
|
g_idle_add (fcopenimg, params);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FileCatalog::emptyTrash () {
|
|
|
|
const std::vector<ThumbBrowserEntryBase*> t = fileBrowser->getEntries ();
|
|
std::vector<FileBrowserEntry*> toDel;
|
|
for (unsigned int i=0; i<t.size(); i++)
|
|
if (((FileBrowserEntry*)t[i])->thumbnail->getStage()==1)
|
|
toDel.push_back (((FileBrowserEntry*)t[i]));
|
|
deleteRequested (toDel);
|
|
trashChanged();
|
|
}
|
|
|
|
bool FileCatalog::trashIsEmpty () {
|
|
const std::vector<ThumbBrowserEntryBase*> t = fileBrowser->getEntries ();
|
|
for (unsigned int i=0; i<t.size(); i++)
|
|
if (((FileBrowserEntry*)t[i])->thumbnail->getStage()==1)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void FileCatalog::zoomIn () {
|
|
|
|
bool pLoad = previewLoader.runs();
|
|
if (pLoad)
|
|
previewLoader.stop ();
|
|
|
|
fileBrowser->zoomIn ();
|
|
|
|
if (pLoad)
|
|
previewLoader.process ();
|
|
}
|
|
void FileCatalog::zoomOut () {
|
|
|
|
bool pLoad = previewLoader.runs();
|
|
if (pLoad)
|
|
previewLoader.stop ();
|
|
|
|
fileBrowser->zoomOut ();
|
|
|
|
if (pLoad)
|
|
previewLoader.process ();
|
|
}
|
|
void FileCatalog::refreshEditedState (const std::set<Glib::ustring>& efiles) {
|
|
|
|
editedFiles = efiles;
|
|
fileBrowser->refreshEditedState (efiles);
|
|
}
|
|
|
|
void FileCatalog::selectionChanged (std::vector<Thumbnail*> tbe) {
|
|
|
|
if (fslistener)
|
|
fslistener->selectionChanged (tbe);
|
|
}
|
|
|
|
void FileCatalog::exifFilterChanged () {
|
|
|
|
currentEFS = filterPanel->getFilter ();
|
|
hasValidCurrentEFS = true;
|
|
fileBrowser->applyFilter (getFilter ());
|
|
}
|
|
|
|
void FileCatalog::setFilterPanel (FilterPanel* fpanel) {
|
|
|
|
filterPanel = fpanel;
|
|
filterPanel->set_sensitive (false);
|
|
filterPanel->setFilterPanelListener (this);
|
|
}
|
|
void FileCatalog::trashChanged () {
|
|
if (trashIsEmpty()) {
|
|
bTrash->set_image(*iTrashEmpty);
|
|
}
|
|
else {
|
|
bTrash->set_image(*iTrashFull);
|
|
}
|
|
}
|