Partially solving issue 1788: "Crashing during processing of queue"

It also correct a bug of wrong image orientation (was wrong in the File Browser while good in the Editor tab)

This patch also introduce several speed optimizations like:
   - switch between File Browser and Editor tab in Single Editor mode
   - asynchronous computation of the Batch Queue's thumbnails
   - quick start of RT even with a huge queue
   - less GUI overhead when applying a profile to multiple thumbs in the File Browser
This commit is contained in:
natureh 510
2013-06-16 15:49:47 +02:00
parent b907d54e0e
commit 54c6a6cbb9
50 changed files with 1810 additions and 783 deletions

View File

@@ -72,7 +72,9 @@ option (BUILD_SHARED "Build rawtherapee with shared libraries" OFF)
option (WITH_BZIP "Build with Bzip2 support" ON) option (WITH_BZIP "Build with Bzip2 support" ON)
option (WITH_MYFILE_MMAP "Build using memory mapped file" ON) option (WITH_MYFILE_MMAP "Build using memory mapped file" ON)
option (OPTION_OMP "Build with OpenMP support" ON) option (OPTION_OMP "Build with OpenMP support" ON)
option (BUILD_BUNDLE "Self-contained build" OFF) option (PROTECT_VECTORS "Protect critical vectors by custom R/W Mutex, recommanded even if your std::vector is thread safe" ON)
option (TRACE_MYRWMUTEX "Trace RT's custom R/W Mutex (Debug builds only); redirecting std::out to a file is strongly recommended!" OFF)
option (AUTO_GDK_FLUSH "Use gdk_flush on all gdk_thread_leave other than the GUI thread; set it ON if you experience X Server warning/errors" OFF)
# set install directories # set install directories
if (WIN32 OR APPLE) if (WIN32 OR APPLE)
@@ -160,6 +162,25 @@ if (NOT BUILD_BUNDLE AND
message (FATAL_ERROR "The paths has to be absolute or use -DBUILD_BUNDLE=ON") message (FATAL_ERROR "The paths has to be absolute or use -DBUILD_BUNDLE=ON")
endif () endif ()
# MyRWMutex
if (PROTECT_VECTORS)
add_definitions (-DPROTECT_VECTORS=1)
else (PROTECT_VECTORS)
add_definitions (-DPROTECT_VECTORS=0)
endif (PROTECT_VECTORS)
if (TRACE_MYRWMUTEX)
# Note: it will be set to 0 for Debug builds (rtgui/guiutils.h)
add_definitions (-DTRACE_MYRWMUTEX=1)
else (TRACE_MYRWMUTEX)
add_definitions (-DTRACE_MYRWMUTEX=0)
endif (TRACE_MYRWMUTEX)
if (AUTO_GDK_FLUSH)
add_definitions (-DAUTO_GDK_FLUSH=1)
else (AUTO_GDK_FLUSH)
add_definitions (-DAUTO_GDK_FLUSH=0)
endif (AUTO_GDK_FLUSH)
# check for libraries # check for libraries
find_package(PkgConfig) find_package(PkgConfig)
pkg_check_modules (GTK REQUIRED gtk+-2.0>=2.12) pkg_check_modules (GTK REQUIRED gtk+-2.0>=2.12)

View File

@@ -40,7 +40,7 @@
namespace rtengine { namespace rtengine {
Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, int deg) { Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh) {
StdImageSource imgSrc; StdImageSource imgSrc;
if (imgSrc.load(fname)) { if (imgSrc.load(fname)) {
@@ -48,10 +48,6 @@ Thumbnail* Thumbnail::loadFromImage (const Glib::ustring& fname, int &w, int &h,
} }
ImageIO* img = imgSrc.getImageIO(); ImageIO* img = imgSrc.getImageIO();
if (deg) {
img->rotate(deg);
}
Thumbnail* tpp = new Thumbnail (); Thumbnail* tpp = new Thumbnail ();
@@ -607,10 +603,10 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, int rhei
int rwidth; int rwidth;
if (params.coarse.rotate==90 || params.coarse.rotate==270) { if (params.coarse.rotate==90 || params.coarse.rotate==270) {
rwidth = rheight; rwidth = rheight;
rheight = thumbImg->height * rwidth / thumbImg->width; rheight = int(size_t(thumbImg->height) * size_t(rwidth) / size_t(thumbImg->width));
} }
else else
rwidth = thumbImg->width * rheight / thumbImg->height; rwidth = int(size_t(thumbImg->width) * size_t(rheight) / size_t(thumbImg->height));
Imagefloat* baseImg = resizeTo<Imagefloat>(rwidth, rheight, interp, thumbImg); Imagefloat* baseImg = resizeTo<Imagefloat>(rwidth, rheight, interp, thumbImg);

View File

@@ -81,7 +81,7 @@ namespace rtengine {
static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate); static Thumbnail* loadQuickFromRaw (const Glib::ustring& fname, rtengine::RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate);
static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate); static Thumbnail* loadFromRaw (const Glib::ustring& fname, RawMetaDataLocation& rml, int &w, int &h, int fixwh, bool rotate);
static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh, int deg=0); static Thumbnail* loadFromImage (const Glib::ustring& fname, int &w, int &h, int fixwh);
void getCamWB (double& temp, double& green); void getCamWB (double& temp, double& green);
void getAutoWB (double& temp, double& green); void getAutoWB (double& temp, double& green);

View File

@@ -65,6 +65,17 @@ BatchQueue::~BatchQueue ()
delete pmenu; delete pmenu;
} }
void BatchQueue::resizeLoadedQueue() {
// TODO: Check for Linux
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
for (size_t i=0; i<fd.size(); i++) {
fd.at(i)->resize(getThumbnailHeight());
}
}
// Reduce the max size of a thumb, since thumb is processed synchronously on adding to queue // Reduce the max size of a thumb, since thumb is processed synchronously on adding to queue
// leading to very long waiting when adding more images // leading to very long waiting when adding more images
int BatchQueue::calcMaxThumbnailHeight() { int BatchQueue::calcMaxThumbnailHeight() {
@@ -76,23 +87,37 @@ int BatchQueue::getMaxThumbnailHeight() const {
return calcMaxThumbnailHeight(); return calcMaxThumbnailHeight();
} }
void BatchQueue::saveThumbnailHeight (int height) {
options.thumbSizeQueue = height;
}
int BatchQueue::getThumbnailHeight () {
// The user could have manually forced the option to a too big value
return std::max(std::min(options.thumbSizeQueue, 200), 10);
}
void BatchQueue::rightClicked (ThumbBrowserEntryBase* entry) { void BatchQueue::rightClicked (ThumbBrowserEntryBase* entry) {
pmenu->popup (3, this->eventTime); pmenu->popup (3, this->eventTime);
} }
void BatchQueue::addEntries ( std::vector<BatchQueueEntry*> &entries, bool head) void BatchQueue::addEntries ( std::vector<BatchQueueEntry*> &entries, bool head, bool save)
{ {
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
for( std::vector<BatchQueueEntry*>::iterator entry = entries.begin(); entry != entries.end();entry++ ){ for( std::vector<BatchQueueEntry*>::iterator entry = entries.begin(); entry != entries.end();entry++ ){
(*entry)->setParent (this); (*entry)->setParent (this);
(*entry)->resize (std::min(options.thumbSize, getMaxThumbnailHeight())); // batch queue might have smaller, restricted size
// BatchQueueButtonSet HAVE TO be added before resizing to take them into account
BatchQueueButtonSet* bqbs = new BatchQueueButtonSet (*entry);
bqbs->setButtonListener (this);
(*entry)->addButtonSet (bqbs);
(*entry)->resize (getThumbnailHeight()); // batch queue might have smaller, restricted size
Glib::ustring tempFile = getTempFilenameForParams( (*entry)->filename ); Glib::ustring tempFile = getTempFilenameForParams( (*entry)->filename );
// recovery save // recovery save
@@ -114,13 +139,11 @@ void BatchQueue::addEntries ( std::vector<BatchQueueEntry*> &entries, bool head)
} }
if ((*entry)->thumbnail) if ((*entry)->thumbnail)
(*entry)->thumbnail->imageEnqueued (); (*entry)->thumbnail->imageEnqueued ();
}
}
BatchQueueButtonSet* bqbs = new BatchQueueButtonSet (*entry); if (save)
bqbs->setButtonListener (this); saveBatchQueue( );
(*entry)->addButtonSet (bqbs);
}
saveBatchQueue( );
}
redraw(); redraw();
notifyListener (false); notifyListener (false);
@@ -135,6 +158,12 @@ bool BatchQueue::saveBatchQueue( )
if (f==NULL) if (f==NULL)
return false; return false;
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (fd.size()) if (fd.size())
// The column's header is mandatory (the first line will be skipped when loaded) // The column's header is mandatory (the first line will be skipped when loaded)
fprintf(f,"input image full path|param file full path|output image full path|file format|jpeg quality|jpeg subsampling|" fprintf(f,"input image full path|param file full path|output image full path|file format|jpeg quality|jpeg subsampling|"
@@ -152,186 +181,186 @@ bool BatchQueue::saveBatchQueue( )
bqe->saveFormat.saveParams, bqe->forceFormatOpts bqe->saveFormat.saveParams, bqe->forceFormatOpts
); );
} }
}
fclose (f); fclose (f);
return true; return true;
} }
void BatchQueue::loadBatchQueue( ) bool BatchQueue::loadBatchQueue( )
{ {
{ Glib::ustring savedQueueFile;
savedQueueFile = options.rtdir+"/batch/queue.csv";
FILE *f = safe_g_fopen (savedQueueFile, "rt");
if (f!=NULL) {
char *buffer = new char[1024];
unsigned numLoaded=0;
// skipping the first line
bool firstLine=true;
// Yes, it's better to get the lock for the whole file reading,
// to update the list in one shot without any other concurrent access!
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
Glib::ustring savedQueueFile; while (fgets (buffer, 1024, f)){
savedQueueFile = options.rtdir+"/batch/queue.csv";
FILE *f = safe_g_fopen (savedQueueFile, "rt");
if (f!=NULL) { if (firstLine) {
char *buffer = new char[1024]; // skipping the column's title line
unsigned numLoaded=0; firstLine=false;
// skipping the first line continue;
bool firstLine=true; }
while (fgets (buffer, 1024, f)){
if (firstLine) { size_t pos;
// skipping the column's title line Glib::ustring source;
firstLine=false; Glib::ustring paramsFile;
continue; Glib::ustring outputFile;
} Glib::ustring saveFmt(options.saveFormat.format);
int jpegQuality=options.saveFormat.jpegQuality, jpegSubSamp =options.saveFormat.jpegSubSamp;
int pngBits =options.saveFormat.pngBits, pngCompression =options.saveFormat.pngCompression;
int tiffBits =options.saveFormat.tiffBits, tiffUncompressed=options.saveFormat.tiffUncompressed;
int saveParams =options.saveFormat.saveParams;
int forceFormatOpts =options.forceFormatOpts;
size_t pos; Glib::ustring currLine(buffer);
Glib::ustring source; int a = 0;
Glib::ustring paramsFile; if (currLine.rfind('\n') != Glib::ustring::npos) a++;
Glib::ustring outputFile; if (currLine.rfind('\r') != Glib::ustring::npos) a++;
Glib::ustring saveFmt(options.saveFormat.format); if (a)
int jpegQuality=options.saveFormat.jpegQuality, jpegSubSamp =options.saveFormat.jpegSubSamp; currLine = currLine.substr(0, currLine.length()-a);
int pngBits =options.saveFormat.pngBits, pngCompression =options.saveFormat.pngCompression;
int tiffBits =options.saveFormat.tiffBits, tiffUncompressed=options.saveFormat.tiffUncompressed;
int saveParams =options.saveFormat.saveParams;
int forceFormatOpts =options.forceFormatOpts;
Glib::ustring currLine(buffer); // Looking for the image's full path
int a = 0; pos = currLine.find('|');
if (currLine.rfind('\n') != Glib::ustring::npos) a++; if (pos != Glib::ustring::npos) {
if (currLine.rfind('\r') != Glib::ustring::npos) a++; source = currLine.substr(0, pos);
if (a) currLine = currLine.substr(pos+1);
currLine = currLine.substr(0, currLine.length()-a);
// Looking for the image's full path // Looking for the procparams' full path
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
source = currLine.substr(0, pos); paramsFile = currLine.substr(0, pos);
currLine = currLine.substr(pos+1);
// Looking for the full output path; if empty, it'll use the template string
pos = currLine.find('|');
if (pos != Glib::ustring::npos) {
outputFile = currLine.substr(0, pos);
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the procparams' full path // No need to bother reading the last options, they will be ignored if outputFile is empty!
pos = currLine.find('|'); if (!outputFile.empty()) {
if (pos != Glib::ustring::npos) {
paramsFile = currLine.substr(0, pos);
currLine = currLine.substr(pos+1);
// Looking for the full output path; if empty, it'll use the template string // Looking for the saving format
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
outputFile = currLine.substr(0, pos); saveFmt = currLine.substr(0, pos);
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// No need to bother reading the last options, they will be ignored if outputFile is empty! // Looking for the jpeg quality
if (!outputFile.empty()) { pos = currLine.find('|');
if (pos != Glib::ustring::npos) {
jpegQuality = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1);
// Looking for the saving format // Looking for the jpeg subsampling
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
saveFmt = currLine.substr(0, pos); jpegSubSamp = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the jpeg quality // Looking for the png bit depth
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
jpegQuality = atoi(currLine.substr(0, pos).c_str()); pngBits = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the jpeg subsampling // Looking for the png compression
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
jpegSubSamp = atoi(currLine.substr(0, pos).c_str()); pngCompression = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the png bit depth // Looking for the tiff bit depth
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
pngBits = atoi(currLine.substr(0, pos).c_str()); tiffBits = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the png compression // Looking for the tiff uncompression
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
pngCompression = atoi(currLine.substr(0, pos).c_str()); tiffUncompressed = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the tiff bit depth // Looking out if we have to save the procparams
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
tiffBits = atoi(currLine.substr(0, pos).c_str()); saveParams = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); currLine = currLine.substr(pos+1);
// Looking for the tiff uncompression // Looking out if we have to to use the format options
pos = currLine.find('|'); pos = currLine.find('|');
if (pos != Glib::ustring::npos) { if (pos != Glib::ustring::npos) {
tiffUncompressed = atoi(currLine.substr(0, pos).c_str()); forceFormatOpts = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1); // currLine = currLine.substr(pos+1);
// Looking out if we have to save the procparams }}}}}}}}}}}}}
pos = currLine.find('|');
if (pos != Glib::ustring::npos) {
saveParams = atoi(currLine.substr(0, pos).c_str());
currLine = currLine.substr(pos+1);
// Looking out if we have to to use the format options if( !source.empty() && !paramsFile.empty() ){
pos = currLine.find('|'); rtengine::procparams::ProcParams pparams;
if (pos != Glib::ustring::npos) { if( pparams.load( paramsFile ) )
forceFormatOpts = atoi(currLine.substr(0, pos).c_str()); continue;
// currLine = currLine.substr(pos+1);
}}}}}}}}}}}}} ::Thumbnail *thumb = cacheMgr->getEntry( source, &pparams );
if( thumb ){
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create(source, thumb->getType() == FT_Raw, pparams);
if( !source.empty() && !paramsFile.empty() ){ int prevh = getMaxThumbnailHeight();
rtengine::procparams::ProcParams pparams; int prevw = prevh;
if( pparams.load( paramsFile ) ) thumb->getThumbnailSize (prevw, prevh);
continue;
::Thumbnail *thumb = cacheMgr->getEntry( source ); BatchQueueEntry *entry = new BatchQueueEntry(job, pparams,source, prevw, prevh, thumb);
if( thumb ){ entry->setParent(this);
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create(source, thumb->getType() == FT_Raw, pparams);
int prevh = getMaxThumbnailHeight(); // BatchQueueButtonSet HAVE TO be added before resizing to take them into account
int prevw = prevh; BatchQueueButtonSet* bqbs = new BatchQueueButtonSet(entry);
guint8* prev = NULL; bqbs->setButtonListener(this);
double tmpscale; entry->addButtonSet(bqbs);
rtengine::IImage8* img = thumb->processThumbImage(pparams, prevh, tmpscale);
if (img) {
prevw = img->getWidth();
prevh = img->getHeight();
prev = new guint8[prevw * prevh * 3];
memcpy(prev, img->getData(), prevw * prevh * 3);
img->free();
}
BatchQueueEntry *entry = new BatchQueueEntry(job, pparams,source, prev, prevw, prevh, thumb);
entry->setParent(this);
entry->resize(options.thumbSize);
entry->savedParamsFile = paramsFile;
entry->selected = false;
entry->outFileName = outputFile;
if (!outputFile.empty()) {
entry->saveFormat.format = saveFmt;
entry->saveFormat.jpegQuality = jpegQuality;
entry->saveFormat.jpegSubSamp = jpegSubSamp;
entry->saveFormat.pngBits = pngBits;
entry->saveFormat.pngCompression = pngCompression;
entry->saveFormat.tiffBits = tiffBits;
entry->saveFormat.tiffUncompressed = tiffUncompressed!=0;
entry->saveFormat.saveParams = saveParams!=0;
entry->forceFormatOpts = forceFormatOpts!=0;
}
else
entry->forceFormatOpts = false;
fd.push_back(entry);
BatchQueueButtonSet* bqbs = new BatchQueueButtonSet(entry); //entry->resize(getThumbnailHeight());
bqbs->setButtonListener(this); entry->savedParamsFile = paramsFile;
entry->addButtonSet(bqbs); entry->selected = false;
numLoaded++; entry->outFileName = outputFile;
if (!outputFile.empty()) {
entry->saveFormat.format = saveFmt;
entry->saveFormat.jpegQuality = jpegQuality;
entry->saveFormat.jpegSubSamp = jpegSubSamp;
entry->saveFormat.pngBits = pngBits;
entry->saveFormat.pngCompression = pngCompression;
entry->saveFormat.tiffBits = tiffBits;
entry->saveFormat.tiffUncompressed = tiffUncompressed!=0;
entry->saveFormat.saveParams = saveParams!=0;
entry->forceFormatOpts = forceFormatOpts!=0;
} }
else
entry->forceFormatOpts = false;
fd.push_back(entry);
numLoaded++;
} }
} }
delete [] buffer;
fclose(f);
} }
delete [] buffer;
fclose(f);
} }
redraw(); redraw();
notifyListener(false); notifyListener(false);
return !fd.empty();
} }
Glib::ustring BatchQueue::getTempFilenameForParams( const Glib::ustring filename ) Glib::ustring BatchQueue::getTempFilenameForParams( const Glib::ustring filename )
@@ -359,13 +388,13 @@ int cancelItemUI (void* data)
} }
void BatchQueue::cancelItems (std::vector<ThumbBrowserEntryBase*>* items) { void BatchQueue::cancelItems (std::vector<ThumbBrowserEntryBase*>* items) {
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
for (size_t i=0; i<items->size(); i++) { for (size_t i=0; i<items->size(); i++) {
BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i]; BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i];
if (entry->processing) if (entry->processing)
continue; continue;
@@ -382,59 +411,61 @@ void BatchQueue::cancelItems (std::vector<ThumbBrowserEntryBase*>* items) {
fd[i]->selected = false; fd[i]->selected = false;
lastClicked = NULL; lastClicked = NULL;
selected.clear (); selected.clear ();
saveBatchQueue( );
} }
saveBatchQueue( );
redraw (); redraw ();
notifyListener (false); notifyListener (false);
} }
void BatchQueue::headItems (std::vector<ThumbBrowserEntryBase*>* items) { void BatchQueue::headItems (std::vector<ThumbBrowserEntryBase*>* items) {
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
for (int i=items->size()-1; i>=0; i--) {
BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i]; for (int i=items->size()-1; i>=0; i--) {
if (entry->processing) BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i];
continue; if (entry->processing)
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry); continue;
if (pos!=fd.end() && pos!=fd.begin()) { std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry);
fd.erase (pos); if (pos!=fd.end() && pos!=fd.begin()) {
// find the first item that is not under processing fd.erase (pos);
for (pos=fd.begin(); pos!=fd.end(); pos++) // find the first item that is not under processing
if (!(*pos)->processing) { for (pos=fd.begin(); pos!=fd.end(); pos++)
fd.insert (pos, entry); if (!(*pos)->processing) {
break; fd.insert (pos, entry);
} break;
} }
} }
saveBatchQueue( );
} }
}
saveBatchQueue( );
redraw (); redraw ();
} }
void BatchQueue::tailItems (std::vector<ThumbBrowserEntryBase*>* items) { void BatchQueue::tailItems (std::vector<ThumbBrowserEntryBase*>* items) {
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
for (size_t i=0; i<items->size(); i++) {
BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i]; for (size_t i=0; i<items->size(); i++) {
if (entry->processing) BatchQueueEntry* entry = (BatchQueueEntry*)(*items)[i];
continue; if (entry->processing)
std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry); continue;
if (pos!=fd.end()) { std::vector<ThumbBrowserEntryBase*>::iterator pos = std::find (fd.begin(), fd.end(), entry);
fd.erase (pos); if (pos!=fd.end()) {
fd.push_back (entry); fd.erase (pos);
} fd.push_back (entry);
} }
saveBatchQueue( );
} }
}
saveBatchQueue( );
redraw (); redraw ();
} }
@@ -442,13 +473,13 @@ void BatchQueue::tailItems (std::vector<ThumbBrowserEntryBase*>* items) {
void BatchQueue::selectAll () { void BatchQueue::selectAll () {
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::ReaderLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
lastClicked = NULL; lastClicked = NULL;
selected.clear (); selected.clear ();
for (size_t i=0; i<fd.size(); i++) { for (size_t i=0; i<fd.size(); i++) {
if (fd[i]->processing) if (fd[i]->processing)
continue; continue;
fd[i]->selected = true; fd[i]->selected = true;
@@ -459,17 +490,18 @@ void BatchQueue::selectAll () {
} }
void BatchQueue::startProcessing () { void BatchQueue::startProcessing () {
if (!processing && !fd.empty()) {
BatchQueueEntry* next;
{ if (!processing) {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
next = static_cast<BatchQueueEntry*>(fd[0]); if (!fd.empty()) {
// tag it as processing BatchQueueEntry* next;
next = static_cast<BatchQueueEntry*>(fd[0]);
// tag it as processing
next->processing = true; next->processing = true;
processing = next; processing = next;
// remove from selection // remove from selection
@@ -480,13 +512,17 @@ void BatchQueue::startProcessing () {
processing->selected = false; processing->selected = false;
} }
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// remove button set // remove button set
next->removeButtonSet (); next->removeButtonSet ();
}
// start batch processing // start batch processing
rtengine::startBatchProcessing (next->job, this, options.tunnelMetaData); rtengine::startBatchProcessing (next->job, this, options.tunnelMetaData);
queue_draw (); queue_draw ();
}
} }
} }
@@ -522,11 +558,11 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) {
err = img->saveAsJPEG (fname, saveFormat.jpegQuality, saveFormat.jpegSubSamp); err = img->saveAsJPEG (fname, saveFormat.jpegQuality, saveFormat.jpegSubSamp);
img->free (); img->free ();
if (err) throw "Unable to save output file"; if (err) throw "Unable to save output file";
if (saveFormat.saveParams) { if (saveFormat.saveParams) {
// We keep the extension to avoid overwriting the profile when we have // We keep the extension to avoid overwriting the profile when we have
// the same output filename with different extension // the same output filename with different extension
//processing->params.save (removeExtension(fname) + paramFileExtension); //processing->params.save (removeExtension(fname) + paramFileExtension);
processing->params.save (fname + ".out" + paramFileExtension); processing->params.save (fname + ".out" + paramFileExtension);
} }
@@ -542,11 +578,11 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) {
// delete from the queue // delete from the queue
delete processing; processing = NULL; delete processing; processing = NULL;
bool queueEmptied=false; bool queueEmptied=false;
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
fd.erase (fd.begin()); fd.erase (fd.begin());
@@ -555,8 +591,8 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) {
queueEmptied=true; queueEmptied=true;
} }
else if (listener && listener->canStartNext ()) { else if (listener && listener->canStartNext ()) {
BatchQueueEntry* next = static_cast<BatchQueueEntry*>(fd[0]); BatchQueueEntry* next = static_cast<BatchQueueEntry*>(fd[0]);
// tag it as selected // tag it as selected
next->processing = true; next->processing = true;
processing = next; processing = next;
// remove from selection // remove from selection
@@ -567,21 +603,34 @@ rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) {
processing->selected = false; processing->selected = false;
} }
// remove button set // remove button set
{
// ButtonSet have Cairo::Surface which might be rendered while we're trying to delete them
GThreadLock lock;
next->removeButtonSet (); next->removeButtonSet ();
}
if (saveBatchQueue( )) {
safe_g_remove( processedParams );
// Delete all files in directory \batch when finished, just to be sure to remove zombies
if( fd.empty() ){
std::vector<Glib::ustring> names;
Glib::ustring batchdir = options.rtdir+"/batch/";
Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path (batchdir);
safe_build_file_list (dir, names, batchdir);
for(std::vector<Glib::ustring>::iterator iter=names.begin(); iter != names.end();iter++ )
safe_g_remove( *iter );
} }
} }
} }
if (saveBatchQueue( )) {
safe_g_remove( processedParams );
// Delete all files in directory \batch when finished, just to be sure to remove zombies
// Not sure that locking is necessary, but it should be safer
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if( fd.empty() ){
#if PROTECT_VECTORS
MYREADERLOCK_RELEASE(l);
#endif
std::vector<Glib::ustring> names;
Glib::ustring batchdir = options.rtdir+"/batch/";
Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path (batchdir);
safe_build_file_list (dir, names, batchdir);
for(std::vector<Glib::ustring>::iterator iter=names.begin(); iter != names.end();iter++ )
safe_g_remove( *iter );
}
}
redraw (); redraw ();
notifyListener (queueEmptied); notifyListener (queueEmptied);
@@ -728,6 +777,7 @@ void BatchQueue::setProgress (double p) {
if (processing) if (processing)
processing->progress = p; processing->progress = p;
// No need to acquire the GUI, setProgressUI will do it
g_idle_add (setProgressUI, this); g_idle_add (setProgressUI, this);
} }
@@ -751,6 +801,7 @@ struct NLParams {
}; };
int bqnotifylistenerUI (void* data) { int bqnotifylistenerUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
NLParams* params = static_cast<NLParams*>(data); NLParams* params = static_cast<NLParams*>(data);
params->listener->queueSizeChanged (params->qsize, params->queueEmptied); params->listener->queueSizeChanged (params->qsize, params->queueEmptied);
delete params; delete params;
@@ -762,13 +813,19 @@ void BatchQueue::notifyListener (bool queueEmptied) {
if (listener) { if (listener) {
NLParams* params = new NLParams; NLParams* params = new NLParams;
params->listener = listener; params->listener = listener;
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
params->qsize = fd.size(); params->qsize = fd.size();
}
params->queueEmptied = queueEmptied; params->queueEmptied = queueEmptied;
g_idle_add (bqnotifylistenerUI, params); g_idle_add (bqnotifylistenerUI, params);
} }
} }
void BatchQueue::redrawNeeded (LWButton* button) { void BatchQueue::redrawNeeded (LWButton* button) {
GThreadLock lock;
queue_draw (); queue_draw ();
} }

View File

@@ -28,6 +28,7 @@
class BatchQueueListener { class BatchQueueListener {
public: public:
virtual ~BatchQueueListener () {}
virtual void queueSizeChanged (int qsize, bool queueEmptied) =0; virtual void queueSizeChanged (int qsize, bool queueEmptied) =0;
virtual bool canStartNext () =0; virtual bool canStartNext () =0;
}; };
@@ -39,6 +40,8 @@ class BatchQueue : public ThumbBrowserBase,
protected: protected:
int getMaxThumbnailHeight() const; int getMaxThumbnailHeight() const;
void saveThumbnailHeight (int height);
int getThumbnailHeight ();
BatchQueueEntry* processing; // holds the currently processed image BatchQueueEntry* processing; // holds the currently processed image
@@ -61,15 +64,22 @@ class BatchQueue : public ThumbBrowserBase,
BatchQueue (); BatchQueue ();
~BatchQueue (); ~BatchQueue ();
void addEntries (std::vector<BatchQueueEntry*> &entries, bool head=false); void addEntries (std::vector<BatchQueueEntry*> &entries, bool head=false, bool save=true);
void cancelItems (std::vector<ThumbBrowserEntryBase*>* items); void cancelItems (std::vector<ThumbBrowserEntryBase*>* items);
void headItems (std::vector<ThumbBrowserEntryBase*>* items); void headItems (std::vector<ThumbBrowserEntryBase*>* items);
void tailItems (std::vector<ThumbBrowserEntryBase*>* items); void tailItems (std::vector<ThumbBrowserEntryBase*>* items);
void selectAll (); void selectAll ();
void startProcessing (); void startProcessing ();
bool hasJobs () { return (!fd.empty()); } bool hasJobs () {
// not sure that this lock is necessary, but it's safer to keep it...
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
return (!fd.empty());
}
rtengine::ProcessingJob* imageReady (rtengine::IImage16* img); rtengine::ProcessingJob* imageReady (rtengine::IImage16* img);
void setProgress (double p); void setProgress (double p);
@@ -79,7 +89,8 @@ class BatchQueue : public ThumbBrowserBase,
void setBatchQueueListener (BatchQueueListener* l) { listener = l; } void setBatchQueueListener (BatchQueueListener* l) { listener = l; }
void loadBatchQueue (); bool loadBatchQueue ();
void resizeLoadedQueue();
static Glib::ustring calcAutoFileNameBase (const Glib::ustring& origFileName); static Glib::ustring calcAutoFileNameBase (const Glib::ustring& origFileName);
static int calcMaxThumbnailHeight(); static int calcMaxThumbnailHeight();

View File

@@ -27,16 +27,16 @@
bool BatchQueueEntry::iconsLoaded(false); bool BatchQueueEntry::iconsLoaded(false);
Glib::RefPtr<Gdk::Pixbuf> BatchQueueEntry::savedAsIcon; Glib::RefPtr<Gdk::Pixbuf> BatchQueueEntry::savedAsIcon;
BatchQueueEntry::BatchQueueEntry (rtengine::ProcessingJob* pjob, const rtengine::procparams::ProcParams& pparams, Glib::ustring fname, guint8* previmg, int prevw, int prevh, Thumbnail* thm) BatchQueueEntry::BatchQueueEntry (rtengine::ProcessingJob* pjob, const rtengine::procparams::ProcParams& pparams, Glib::ustring fname, int prevw, int prevh, Thumbnail* thm)
: ThumbBrowserEntryBase(fname), : ThumbBrowserEntryBase(fname),
opreview(previmg), origpw(prevw), origph(prevh), opreview(NULL), origpw(prevw), origph(prevh), opreviewDone(false),
job(pjob), progress(0), outFileName(""), forceFormatOpts(false) { job(pjob), progress(0), outFileName(""), forceFormatOpts(false) {
thumbnail=thm; thumbnail=thm;
params = pparams; params = pparams;
#ifndef WIN32 #if 1 //ndef WIN32
// The BatchQueueEntryIdleHelper tracks if an entry has been deleted while it was sitting wating for "idle" // The BatchQueueEntryIdleHelper tracks if an entry has been deleted while it was sitting waiting for "idle"
bqih = new BatchQueueEntryIdleHelper; bqih = new BatchQueueEntryIdleHelper;
bqih->bqentry = this; bqih->bqentry = this;
bqih->destroyed = false; bqih->destroyed = false;
@@ -55,24 +55,28 @@ BatchQueueEntry::BatchQueueEntry (rtengine::ProcessingJob* pjob, const rtengine:
BatchQueueEntry::~BatchQueueEntry () { BatchQueueEntry::~BatchQueueEntry () {
batchQueueEntryUpdater.removeJobs (this); batchQueueEntryUpdater.removeJobs (this);
delete [] opreview; opreview=NULL; if (opreview) delete [] opreview; opreview=NULL;
if (thumbnail) if (thumbnail)
thumbnail->decreaseRef (); thumbnail->decreaseRef ();
#ifndef WIN32
if (bqih->pending) if (bqih->pending)
bqih->destroyed = true; bqih->destroyed = true;
else else
delete bqih; delete bqih;
#endif
} }
void BatchQueueEntry::refreshThumbnailImage () { void BatchQueueEntry::refreshThumbnailImage () {
if (!opreview) if (!opreviewDone) {
return; // creating the image buffer first
//if (!opreview) opreview = new guint8[(origpw+1) * origph * 3];
batchQueueEntryUpdater.process (opreview, origpw, origph, preh, this); // this will asynchronously land at this.updateImage // this will asynchronously compute the original preview and land at this.updateImage
batchQueueEntryUpdater.process (NULL, origpw, origph, preh, this, &params, thumbnail);
}
else {
// this will asynchronously land at this.updateImage
batchQueueEntryUpdater.process (opreview, origpw, origph, preh, this);
}
} }
void BatchQueueEntry::calcThumbnailSize () { void BatchQueueEntry::calcThumbnailSize () {
@@ -166,8 +170,6 @@ Glib::ustring BatchQueueEntry::getToolTip (int x, int y) {
} }
#ifndef WIN32
struct BQUpdateParam { struct BQUpdateParam {
BatchQueueEntryIdleHelper* bqih; BatchQueueEntryIdleHelper* bqih;
guint8* img; guint8* img;
@@ -180,11 +182,13 @@ int updateImageUIThread (void* data) {
BatchQueueEntryIdleHelper* bqih = params->bqih; BatchQueueEntryIdleHelper* bqih = params->bqih;
// If the BQEntry was destroyed meanwhile, remove all the IdleHelper if all entries came through GThreadLock tLock; // Acquire the GUI
// If the BQEntry was destroyed meanwhile, remove all the IdleHelper if all entries came through
if (bqih->destroyed) { if (bqih->destroyed) {
if (bqih->pending == 1) if (bqih->pending == 1)
delete bqih; delete bqih;
else else
bqih->pending--; bqih->pending--;
delete [] params->img; delete [] params->img;
delete params; delete params;
@@ -198,35 +202,29 @@ int updateImageUIThread (void* data) {
delete params; delete params;
return 0; return 0;
} }
#endif
// Starts a copy of img->preview via GTK thread // Starts a copy of img->preview via GTK thread
void BatchQueueEntry::updateImage (guint8* img, int w, int h) { void BatchQueueEntry::updateImage (guint8* img, int w, int h, int origw, int origh, guint8* newOPreview) {
// TODO: Check for Linux/Mac
#ifdef WIN32
// since the update itself is already called in an async thread and there are problem with accessing opreview in thumbbrowserbase, // since the update itself is already called in an async thread and there are problem with accessing opreview in thumbbrowserbase,
// it's safer to do this synchrously // it's safer to do this synchronously
{ {
GThreadLock lock; GThreadLock lock;
_updateImage(img,w,h); _updateImage(img,w,h);
} }
#else
bqih->pending++;
BQUpdateParam* param = new BQUpdateParam ();
param->bqih = bqih;
param->img = img;
param->w = w;
param->h = h;
g_idle_add (updateImageUIThread, param);
#endif
} }
void BatchQueueEntry::_updateImage (guint8* img, int w, int h) { void BatchQueueEntry::_updateImage (guint8* img, int w, int h) {
if (preh == h) { if (preh == h) {
#if PROTECT_VECTORS
MYWRITERLOCK(l, lockRW);
#endif
prew = w; prew = w;
assert (preview == NULL);
preview = new guint8 [prew*preh*3]; preview = new guint8 [prew*preh*3];
memcpy (preview, img, prew*preh*3); memcpy (preview, img, prew*preh*3);
if (parent) if (parent)

View File

@@ -37,6 +37,7 @@ class BatchQueueEntry : public ThumbBrowserEntryBase, public BQEntryUpdateListen
guint8* opreview; guint8* opreview;
int origpw, origph; int origpw, origph;
BatchQueueEntryIdleHelper* bqih; BatchQueueEntryIdleHelper* bqih;
bool opreviewDone;
static bool iconsLoaded; static bool iconsLoaded;
public: public:
@@ -51,7 +52,7 @@ public:
SaveFormat saveFormat; SaveFormat saveFormat;
bool forceFormatOpts; bool forceFormatOpts;
BatchQueueEntry (rtengine::ProcessingJob* job, const rtengine::procparams::ProcParams& pparams, Glib::ustring fname, guint8* previmg, int prevw, int prevh, Thumbnail* thm=NULL); BatchQueueEntry (rtengine::ProcessingJob* job, const rtengine::procparams::ProcParams& pparams, Glib::ustring fname, int prevw, int prevh, Thumbnail* thm=NULL);
~BatchQueueEntry (); ~BatchQueueEntry ();
void refreshThumbnailImage (); void refreshThumbnailImage ();
@@ -66,7 +67,7 @@ public:
virtual Glib::ustring getToolTip (int x, int y); virtual Glib::ustring getToolTip (int x, int y);
// bqentryupdatelistener interface // bqentryupdatelistener interface
void updateImage (guint8* img, int w, int h); void updateImage (guint8* img, int w, int h, int origw, int origh, guint8* newOPreview);
void _updateImage (guint8* img, int w, int h); // inside gtk thread void _updateImage (guint8* img, int w, int h); // inside gtk thread
}; };

View File

@@ -25,6 +25,16 @@
#include "../rtengine/safegtk.h" #include "../rtengine/safegtk.h"
#include "rtimage.h" #include "rtimage.h"
struct BQProcessLoaded {
BatchQueue* bq;
};
int processLoadedBatchQueueUIThread (void* data) {
BatchQueue* bq = static_cast<BatchQueue*>(data);
bq->resizeLoadedQueue();
return 0;
}
BatchQueuePanel::BatchQueuePanel () { BatchQueuePanel::BatchQueuePanel () {
@@ -132,7 +142,9 @@ BatchQueuePanel::BatchQueuePanel () {
batchQueue->setBatchQueueListener (this); batchQueue->setBatchQueueListener (this);
show_all (); show_all ();
batchQueue->loadBatchQueue (); if (batchQueue->loadBatchQueue ()) {
g_idle_add_full (G_PRIORITY_LOW, processLoadedBatchQueueUIThread, batchQueue, NULL);
}
} }

View File

@@ -27,6 +27,7 @@ using namespace rtengine::procparams;
BatchToolPanelCoordinator::BatchToolPanelCoordinator (FilePanel* parent) : ToolPanelCoordinator(), parent(parent) { BatchToolPanelCoordinator::BatchToolPanelCoordinator (FilePanel* parent) : ToolPanelCoordinator(), parent(parent) {
blockedUpdate = false;
// remove exif panel and iptc panel // remove exif panel and iptc panel
std::vector<ToolPanel*>::iterator epi = std::find (toolPanels.begin(), toolPanels.end(), exifpanel); std::vector<ToolPanel*>::iterator epi = std::find (toolPanels.begin(), toolPanels.end(), exifpanel);
if (epi!=toolPanels.end()) if (epi!=toolPanels.end())
@@ -56,13 +57,13 @@ void BatchToolPanelCoordinator::selectionChanged (const std::vector<Thumbnail*>&
void BatchToolPanelCoordinator::closeSession (bool save) { void BatchToolPanelCoordinator::closeSession (bool save) {
pparamsEdited.set (false); pparamsEdited.set (false);
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
selected[i]->removeThumbnailListener (this); selected[i]->removeThumbnailListener (this);
if (somethingChanged && save) { if (somethingChanged && save) {
// read new values from the gui // read new values from the gui
for (size_t i=0; i<toolPanels.size(); i++) for (size_t i=0; i<toolPanels.size(); i++)
toolPanels[i]->write (&pparams, &pparamsEdited); toolPanels[i]->write (&pparams, &pparamsEdited);
@@ -73,11 +74,11 @@ void BatchToolPanelCoordinator::closeSession (bool save) {
newParams = initialPP[i]; newParams = initialPP[i];
pparamsEdited.combine (newParams, pparams, selected.size()==1); pparamsEdited.combine (newParams, pparams, selected.size()==1);
// trim new adjuster's values to the adjuster's limits // trim new adjuster's values to the adjuster's limits
for (unsigned int j=0; j<toolPanels.size(); j++) for (unsigned int j=0; j<toolPanels.size(); j++)
toolPanels[j]->trimValues (&newParams); toolPanels[j]->trimValues (&newParams);
selected[i]->setProcParams (newParams, NULL, BATCHEDITOR, true); selected[i]->setProcParams (newParams, NULL, BATCHEDITOR, true);
} }
} }
for (size_t i=0; i<paramcListeners.size(); i++) for (size_t i=0; i<paramcListeners.size(); i++)
@@ -333,12 +334,28 @@ void BatchToolPanelCoordinator::optionsChanged () {
void BatchToolPanelCoordinator::procParamsChanged (Thumbnail* thm, int whoChangedIt) { void BatchToolPanelCoordinator::procParamsChanged (Thumbnail* thm, int whoChangedIt) {
if (whoChangedIt!=BATCHEDITOR) { if (whoChangedIt!=BATCHEDITOR && !blockedUpdate) {
closeSession (false); closeSession (false);
initSession (); initSession ();
} }
} }
void BatchToolPanelCoordinator::beginBatchPParamsChange(int numberOfEntries) {
blockedUpdate = true;
if (numberOfEntries > 50) // Arbitrary amount
parent->set_sensitive(false);
}
// The end of a batch pparams change triggers a close/initsession
void BatchToolPanelCoordinator::endBatchPParamsChange() {
//printf("BatchToolPanelCoordinator::endBatchPParamsChange / Nouvelle session!\n");
closeSession (false);
initSession ();
blockedUpdate = false;
parent->set_sensitive(true);
}
/* /*
* WARNING: profileChange is actually called by the History only. * WARNING: profileChange is actually called by the History only.
* Using a Profile panel in the batch tool panel editor is actually * Using a Profile panel in the batch tool panel editor is actually

View File

@@ -30,6 +30,7 @@ class FilePanel;
class BatchToolPanelCoordinator : class BatchToolPanelCoordinator :
public ToolPanelCoordinator, public ToolPanelCoordinator,
public FileSelectionChangeListener, public FileSelectionChangeListener,
public BatchPParamsChangeListener,
public ThumbnailListener public ThumbnailListener
{ {
protected: protected:
@@ -39,6 +40,7 @@ class BatchToolPanelCoordinator :
std::vector<Glib::ustring> selFileNames; std::vector<Glib::ustring> selFileNames;
std::vector<rtengine::procparams::ProcParams> initialPP; std::vector<rtengine::procparams::ProcParams> initialPP;
bool somethingChanged; bool somethingChanged;
bool blockedUpdate;
FilePanel* parent; FilePanel* parent;
void closeSession (bool save=true); void closeSession (bool save=true);
@@ -63,7 +65,11 @@ class BatchToolPanelCoordinator :
// thumbnaillistener interface // thumbnaillistener interface
void procParamsChanged (Thumbnail* thm, int whoChangedIt); void procParamsChanged (Thumbnail* thm, int whoChangedIt);
// batchpparamschangelistener interface
void beginBatchPParamsChange(int numberOfEntries);
void endBatchPParamsChange();
// imageareatoollistener interface // imageareatoollistener interface
void spotWBselected (int x, int y, Thumbnail* thm=NULL); void spotWBselected (int x, int y, Thumbnail* thm=NULL);
void cropSelectionReady (); void cropSelectionReady ();

View File

@@ -23,12 +23,17 @@
BatchQueueEntryUpdater batchQueueEntryUpdater; BatchQueueEntryUpdater batchQueueEntryUpdater;
BatchQueueEntryUpdater::BatchQueueEntryUpdater () BatchQueueEntryUpdater::BatchQueueEntryUpdater ()
: tostop(false), stopped(true), qMutex(NULL) { : tostop(false), stopped(true), thread(NULL), qMutex(NULL) {
} }
void BatchQueueEntryUpdater::process (guint8* oimg, int ow, int oh, int newh, BQEntryUpdateListener* listener) { void BatchQueueEntryUpdater::process (guint8* oimg, int ow, int oh, int newh, BQEntryUpdateListener* listener, rtengine::ProcParams* pparams, Thumbnail* thumbnail) {
if (!oimg && (!pparams || !thumbnail)) {
//printf("WARNING! !oimg && (!pparams || !thumbnail)\n");
return;
}
if (!qMutex) if (!qMutex)
qMutex = new Glib::Mutex (); qMutex = new Glib::Mutex ();
qMutex->lock (); qMutex->lock ();
// look up if an older version is in the queue // look up if an older version is in the queue
@@ -39,6 +44,8 @@ void BatchQueueEntryUpdater::process (guint8* oimg, int ow, int oh, int newh, BQ
i->oh = oh; i->oh = oh;
i->newh = newh; i->newh = newh;
i->listener = listener; i->listener = listener;
i->pparams = pparams;
i->thumbnail = thumbnail;
break; break;
} }
@@ -50,6 +57,8 @@ void BatchQueueEntryUpdater::process (guint8* oimg, int ow, int oh, int newh, BQ
j.oh = oh; j.oh = oh;
j.newh = newh; j.newh = newh;
j.listener = listener; j.listener = listener;
j.pparams = pparams;
j.thumbnail = thumbnail;
jqueue.push_back (j); jqueue.push_back (j);
} }
qMutex->unlock (); qMutex->unlock ();
@@ -79,11 +88,37 @@ void BatchQueueEntryUpdater::processThread () {
} }
qMutex->unlock (); qMutex->unlock ();
if (!isEmpty && current.listener) { rtengine::IImage8* img = NULL;
bool newBuffer = false;
if (current.thumbnail && current.pparams) {
// the thumbnail and the pparams are provided, it means that we have to build the original preview image
double tmpscale;
img = current.thumbnail->processThumbImage (*current.pparams, current.oh, tmpscale);
//current.thumbnail->decreaseRef (); // WARNING: decreasing refcount (and maybe deleting) thumbnail, with or without processed image
if (img) {
int prevw = img->getWidth();
int prevh = img->getHeight();
#ifndef NDEBUG
if (current.ow != img->getW() || current.oh != img->getH())
printf("WARNING! Expected image size: %dx%d ; image size is: %dx%d\n", current.ow, current.oh, img->getW(), img->getH());
assert ((current.ow+1)*current.oh >= img->getW()*img->getH());
#endif
current.ow = prevw;
current.oh = prevh;
if (!current.oimg) {
current.oimg = new guint8[prevw*prevh*3];
newBuffer = true;
}
memcpy(current.oimg, img->getData(), prevw * prevh * 3);
img->free();
}
}
if (current.oimg && !isEmpty && current.listener) {
int neww = current.newh * current.ow / current.oh; int neww = current.newh * current.ow / current.oh;
guint8* img = new guint8 [current.newh*neww*3]; guint8* img = new guint8 [current.newh*neww*3];
thumbInterp (current.oimg, current.ow, current.oh, img, neww, current.newh); thumbInterp (current.oimg, current.ow, current.oh, img, neww, current.newh);
current.listener->updateImage (img, neww, current.newh); current.listener->updateImage (img, neww, current.newh, current.ow, current.oh, newBuffer?current.oimg:NULL);
} }
} }
@@ -114,12 +149,11 @@ void BatchQueueEntryUpdater::terminate () {
if (!qMutex || stopped) return; if (!qMutex || stopped) return;
if (!stopped) { if (!stopped) {
// Yield to currenly running thread and wait till it's finished // Yield to currently running thread and wait till it's finished
gdk_threads_leave(); GThreadUnLock();
tostop = true; tostop = true;
Glib::Thread::self()->yield(); Glib::Thread::self()->yield();
if (!stopped) thread->join (); if (!stopped) thread->join ();
gdk_threads_enter();
} }
// Remove remaining jobs // Remove remaining jobs

View File

@@ -26,7 +26,8 @@
class BQEntryUpdateListener { class BQEntryUpdateListener {
public: public:
virtual void updateImage (guint8* img, int w, int h) {} virtual ~BQEntryUpdateListener () {}
virtual void updateImage (guint8* img, int w, int h, int origw, int origh, guint8* newOPreview) {}
}; };
class BatchQueueEntryUpdater { class BatchQueueEntryUpdater {
@@ -35,6 +36,8 @@ class BatchQueueEntryUpdater {
guint8* oimg; guint8* oimg;
int ow, oh, newh; int ow, oh, newh;
BQEntryUpdateListener* listener; BQEntryUpdateListener* listener;
rtengine::ProcParams* pparams;
Thumbnail* thumbnail;
}; };
protected: protected:
@@ -47,7 +50,7 @@ class BatchQueueEntryUpdater {
public: public:
BatchQueueEntryUpdater (); BatchQueueEntryUpdater ();
void process (guint8* oimg, int ow, int oh, int newh, BQEntryUpdateListener* listener); void process (guint8* oimg, int ow, int oh, int newh, BQEntryUpdateListener* listener, rtengine::ProcParams* pparams=NULL, Thumbnail* thumbnail=NULL);
void removeJobs (BQEntryUpdateListener* listener); void removeJobs (BQEntryUpdateListener* listener);
void terminate (); void terminate ();

View File

@@ -33,8 +33,13 @@ int CacheImageData::load (const Glib::ustring& fname) {
rtengine::SafeKeyFile keyFile; rtengine::SafeKeyFile keyFile;
try { try {
if (!keyFile.load_from_file (fname)) bool loaded = keyFile.load_from_file (fname);
if (!loaded) {
#ifndef NDEBUG
printf("Failed to load_from_file(%s)\n", fname.c_str());
#endif
return 1; return 1;
}
if (keyFile.has_group ("General")) { if (keyFile.has_group ("General")) {
if (keyFile.has_key ("General", "MD5")) md5 = keyFile.get_string ("General", "MD5"); if (keyFile.has_key ("General", "MD5")) md5 = keyFile.get_string ("General", "MD5");
@@ -58,32 +63,31 @@ int CacheImageData::load (const Glib::ustring& fname) {
if (keyFile.has_key ("DateTime", "MSec")) msec = keyFile.get_integer ("DateTime", "MSec"); if (keyFile.has_key ("DateTime", "MSec")) msec = keyFile.get_integer ("DateTime", "MSec");
} }
exifValid = false; exifValid = false;
if (keyFile.has_group ("ExifInfo")) {
exifValid = true;
if (keyFile.has_key ("ExifInfo", "Valid")) exifValid = keyFile.get_boolean ("ExifInfo", "Valid");
if (exifValid) { if (keyFile.has_group ("ExifInfo")) {
if (keyFile.has_key ("ExifInfo", "FNumber")) fnumber = keyFile.get_double ("ExifInfo", "FNumber"); exifValid = true;
if (keyFile.has_key ("ExifInfo", "Shutter")) shutter = keyFile.get_double ("ExifInfo", "Shutter"); if (keyFile.has_key ("ExifInfo", "Valid")) exifValid = keyFile.get_boolean ("ExifInfo", "Valid");
if (keyFile.has_key ("ExifInfo", "FocalLen")) focalLen = keyFile.get_double ("ExifInfo", "FocalLen");
if (exifValid) {
if (keyFile.has_key ("ExifInfo", "FNumber")) fnumber = keyFile.get_double ("ExifInfo", "FNumber");
if (keyFile.has_key ("ExifInfo", "Shutter")) shutter = keyFile.get_double ("ExifInfo", "Shutter");
if (keyFile.has_key ("ExifInfo", "FocalLen")) focalLen = keyFile.get_double ("ExifInfo", "FocalLen");
if (keyFile.has_key ("ExifInfo", "FocalLen35mm")) focalLen35mm = keyFile.get_double ("ExifInfo", "FocalLen35mm"); if (keyFile.has_key ("ExifInfo", "FocalLen35mm")) focalLen35mm = keyFile.get_double ("ExifInfo", "FocalLen35mm");
else focalLen35mm=focalLen; // prevent crashes on old files else focalLen35mm=focalLen; // prevent crashes on old files
if (keyFile.has_key ("ExifInfo", "FocusDist")) focusDist = keyFile.get_double ("ExifInfo", "FocusDist"); if (keyFile.has_key ("ExifInfo", "FocusDist")) focusDist = keyFile.get_double ("ExifInfo", "FocusDist");
else focusDist=0; else focusDist=0;
if (keyFile.has_key ("ExifInfo", "ISO")) iso = keyFile.get_integer ("ExifInfo", "ISO"); if (keyFile.has_key ("ExifInfo", "ISO")) iso = keyFile.get_integer ("ExifInfo", "ISO");
if (keyFile.has_key ("ExifInfo", "ExpComp")) expcomp = keyFile.get_string ("ExifInfo", "ExpComp"); if (keyFile.has_key ("ExifInfo", "ExpComp")) expcomp = keyFile.get_string ("ExifInfo", "ExpComp");
} }
if (keyFile.has_key ("ExifInfo", "Lens")) lens = keyFile.get_string ("ExifInfo", "Lens"); if (keyFile.has_key ("ExifInfo", "Lens")) lens = keyFile.get_string ("ExifInfo", "Lens");
if (keyFile.has_key ("ExifInfo", "Camera")) camera = keyFile.get_string ("ExifInfo", "Camera"); if (keyFile.has_key ("ExifInfo", "Camera")) camera = keyFile.get_string ("ExifInfo", "Camera");
} }
if (keyFile.has_group ("FileInfo")) { if (keyFile.has_group ("FileInfo")) {
if (keyFile.has_key ("FileInfo", "Filetype")) filetype = keyFile.get_string ("FileInfo", "Filetype"); if (keyFile.has_key ("FileInfo", "Filetype")) filetype = keyFile.get_string ("FileInfo", "Filetype");
} }
if (format==FT_Raw && keyFile.has_group ("ExtraRawInfo")) { if (format==FT_Raw && keyFile.has_group ("ExtraRawInfo")) {
if (keyFile.has_key ("ExtraRawInfo", "ThumbImageType")) thumbImgType = keyFile.get_integer ("ExtraRawInfo", "ThumbImageType"); if (keyFile.has_key ("ExtraRawInfo", "ThumbImageType")) thumbImgType = keyFile.get_integer ("ExtraRawInfo", "ThumbImageType");
if (keyFile.has_key ("ExtraRawInfo", "ThumbImageOffset")) thumbOffset = keyFile.get_integer ("ExtraRawInfo", "ThumbImageOffset"); if (keyFile.has_key ("ExtraRawInfo", "ThumbImageOffset")) thumbOffset = keyFile.get_integer ("ExtraRawInfo", "ThumbImageOffset");
@@ -94,9 +98,10 @@ int CacheImageData::load (const Glib::ustring& fname) {
} }
return 0; return 0;
} }
catch (Glib::Error) { catch (Glib::Error &err) {
return 1; printf("Error code %d while reading values from \"%s\":\n%s\n", err.code(), fname.c_str(), err.what().c_str());
} }
return 1;
} }
int CacheImageData::save (const Glib::ustring& fname) { int CacheImageData::save (const Glib::ustring& fname) {
@@ -151,5 +156,6 @@ int CacheImageData::save (const Glib::ustring& fname) {
fprintf (f, "%s", keyFile.to_data().c_str()); fprintf (f, "%s", keyFile.to_data().c_str());
fclose (f); fclose (f);
return 0; return 0;
}} }
}

View File

@@ -64,7 +64,11 @@ void CacheManager::init () {
safe_g_mkdir_with_parents (Glib::ustring(Glib::build_filename (baseDir, "data")), 511); safe_g_mkdir_with_parents (Glib::ustring(Glib::build_filename (baseDir, "data")), 511);
} }
Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) { /*
* if pparams is NULL (default), get the ProcParams from the cache;
* if pparams is non NULL, compute the thumbnails with the provided pparams
*/
Thumbnail* CacheManager::getEntry (const Glib::ustring& fname, const rtengine::procparams::ProcParams *pparams) {
Thumbnail* res = NULL; Thumbnail* res = NULL;
@@ -93,8 +97,8 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) {
if (safe_file_test (cfname, Glib::FILE_TEST_EXISTS)) { if (safe_file_test (cfname, Glib::FILE_TEST_EXISTS)) {
CacheImageData* cfs = new CacheImageData (); CacheImageData* cfs = new CacheImageData ();
int e = cfs->load (cfname); int e = cfs->load (cfname);
if (!e && cfs->supported==true) if (!e && cfs->supported==true)
res = new Thumbnail (this, fname, cfs); res = new Thumbnail (this, fname, cfs, pparams);
if (res && !res->isSupported ()) { if (res && !res->isSupported ()) {
delete res; delete res;
res = NULL; res = NULL;
@@ -104,7 +108,7 @@ Thumbnail* CacheManager::getEntry (const Glib::ustring& fname) {
// if not, create a new one // if not, create a new one
if (!res) { if (!res) {
res = new Thumbnail (this, fname, md5); res = new Thumbnail (this, fname, md5, pparams);
if (!res->isSupported ()) { if (!res->isSupported ()) {
delete res; delete res;
res = NULL; res = NULL;

View File

@@ -24,6 +24,7 @@
#include <glibmm.h> #include <glibmm.h>
#include "thumbnail.h" #include "thumbnail.h"
#include <cstdio> #include <cstdio>
#include "../rtengine/procparams.h"
class Thumbnail; class Thumbnail;
@@ -45,7 +46,7 @@ class CacheManager {
static CacheManager* getInstance(void); static CacheManager* getInstance(void);
void init (); void init ();
Thumbnail* getEntry (const Glib::ustring& fname); Thumbnail* getEntry (const Glib::ustring& fname, const rtengine::procparams::ProcParams *pparams=NULL);
void deleteEntry (const Glib::ustring& fname); void deleteEntry (const Glib::ustring& fname);
void renameEntry (const std::string& oldfilename, const std::string& oldmd5, const std::string& newfilename); void renameEntry (const std::string& oldfilename, const std::string& oldmd5, const std::string& newfilename);

View File

@@ -893,14 +893,14 @@ void ColorAppearance::setDefaults (const ProcParams* defParams, const ParamsEdit
} }
} }
int autoCamChangedUI (void* data) { int autoCamChangedUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
(static_cast<ColorAppearance*>(data))->autoCamComputed_ (); (static_cast<ColorAppearance*>(data))->autoCamComputed_ ();
return 0; return 0;
} }
void ColorAppearance::autoCamChanged (double ccam) void ColorAppearance::autoCamChanged (double ccam)
{ {
nextCcam = ccam; nextCcam = ccam;
g_idle_add (autoCamChangedUI, this); g_idle_add (autoCamChangedUI, this);
// Glib::signal_idle().connect (sigc::mem_fun(*this, &ColorAppearance::autoCamComputed_));
} }
bool ColorAppearance::autoCamComputed_ () { bool ColorAppearance::autoCamComputed_ () {
@@ -913,14 +913,14 @@ bool ColorAppearance::autoCamComputed_ () {
return false; return false;
} }
int adapCamChangedUI (void* data) { int adapCamChangedUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
(static_cast<ColorAppearance*>(data))->adapCamComputed_ (); (static_cast<ColorAppearance*>(data))->adapCamComputed_ ();
return 0; return 0;
} }
void ColorAppearance::adapCamChanged (double cadap) void ColorAppearance::adapCamChanged (double cadap)
{ {
nextCadap = cadap; nextCadap = cadap;
g_idle_add (adapCamChangedUI, this); g_idle_add (adapCamChangedUI, this);
// Glib::signal_idle().connect (sigc::mem_fun(*this, &ColorAppearance::autoCamComputed_));
} }
bool ColorAppearance::adapCamComputed_ () { bool ColorAppearance::adapCamComputed_ () {

View File

@@ -469,11 +469,13 @@ void Crop::enabledChanged () {
} }
int notifyListenerUI (void* data) { int notifyListenerUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
(static_cast<Crop*>(data))->notifyListener (); (static_cast<Crop*>(data))->notifyListener ();
return 0; return 0;
} }
int refreshSpinsUI (void* data) { int refreshSpinsUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
RefreshSpinHelper* rsh = static_cast<RefreshSpinHelper*>(data); RefreshSpinHelper* rsh = static_cast<RefreshSpinHelper*>(data);
rsh->crop->refreshSpins (rsh->notify); rsh->crop->refreshSpins (rsh->notify);
delete rsh; delete rsh;
@@ -587,23 +589,23 @@ void Crop::ratioChanged () {
// Correct current crop if it doesn't fit // Correct current crop if it doesn't fit
void Crop::adjustCropToRatio() { void Crop::adjustCropToRatio() {
if (fixr->get_active() && !fixr->get_inconsistent()) { if (fixr->get_active() && !fixr->get_inconsistent()) {
// int W = w->get_value (); // int W = w->get_value ();
// int H = h->get_value (); // int H = h->get_value ();
int W = nw; int W = nw;
int H = nh; int H = nh;
int X = nx; int X = nx;
int Y = ny; int Y = ny;
if (W>=H) if (W>=H)
cropWidth2Resized (X, Y, W, H); cropWidth2Resized (X, Y, W, H);
else else
cropHeight2Resized (X, Y, W, H); cropHeight2Resized (X, Y, W, H);
} }
// This will safe the options // This will save the options
g_idle_add (refreshSpinsUI, new RefreshSpinHelper (this, true)); g_idle_add (refreshSpinsUI, new RefreshSpinHelper (this, true));
} }
void Crop::refreshSize () { void Crop::refreshSize () {
@@ -667,6 +669,7 @@ struct setdimparams {
}; };
int sizeChangedUI (void* data) { int sizeChangedUI (void* data) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
setdimparams* params = static_cast<setdimparams*>(data); setdimparams* params = static_cast<setdimparams*>(data);
params->crop->setDimensions (params->x, params->y); params->crop->setDimensions (params->x, params->y);
delete params; delete params;
@@ -958,12 +961,11 @@ void Crop::cropResized (int &x, int &y, int& x2, int& y2) {
nh = H; nh = H;
g_idle_add (refreshSpinsUI, new RefreshSpinHelper (this, false)); g_idle_add (refreshSpinsUI, new RefreshSpinHelper (this, false));
// Glib::signal_idle().connect (sigc::mem_fun(*this, &Crop::refreshSpins));
} }
void Crop::cropManipReady () { void Crop::cropManipReady () {
g_idle_add (notifyListenerUI, this); g_idle_add (notifyListenerUI, this);
} }
double Crop::getRatio () { double Crop::getRatio () {

View File

@@ -186,11 +186,11 @@ int createpixbufs (void* data) {
if (!ch->enabled) { if (!ch->enabled) {
delete [] ch->cropimg; delete [] ch->cropimg;
ch->cropimg = NULL; ch->cropimg = NULL;
delete [] ch->cropimgtrue; delete [] ch->cropimgtrue;
ch->cropimgtrue = NULL; ch->cropimgtrue = NULL;
ch->cimg.unlock (); ch->cimg.unlock ();
return 0; return 0;
} }
if (ch->cropimg) { if (ch->cropimg) {
if (ch->cix==ch->cropX && ch->ciy==ch->cropY && ch->ciw==ch->cropW && ch->cih==ch->cropH && ch->cis==(ch->zoom>=1000?1:ch->zoom)) { if (ch->cix==ch->cropX && ch->ciy==ch->cropY && ch->ciw==ch->cropW && ch->cih==ch->cropH && ch->cis==(ch->zoom>=1000?1:ch->zoom)) {
@@ -207,15 +207,15 @@ int createpixbufs (void* data) {
ch->cropPixbuf = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); ch->cropPixbuf = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh);
tmpPixbuf->scale (ch->cropPixbuf, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST); tmpPixbuf->scale (ch->cropPixbuf, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST);
tmpPixbuf.clear (); tmpPixbuf.clear ();
Glib::RefPtr<Gdk::Pixbuf> tmpPixbuftrue = Gdk::Pixbuf::create_from_data (ch->cropimgtrue, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, 2*ch->cropimg_height, 3*ch->cropimg_width); Glib::RefPtr<Gdk::Pixbuf> tmpPixbuftrue = Gdk::Pixbuf::create_from_data (ch->cropimgtrue, Gdk::COLORSPACE_RGB, false, 8, ch->cropimg_width, 2*ch->cropimg_height, 3*ch->cropimg_width);
ch->cropPixbuftrue = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh); ch->cropPixbuftrue = Gdk::Pixbuf::create (Gdk::COLORSPACE_RGB, false, 8, imw, imh);
tmpPixbuftrue->scale (ch->cropPixbuftrue, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST); tmpPixbuftrue->scale (ch->cropPixbuftrue, 0, 0, imw, imh, 0, 0, czoom/1000.0, czoom/1000.0, Gdk::INTERP_NEAREST);
tmpPixbuftrue.clear (); tmpPixbuftrue.clear ();
} }
delete [] ch->cropimg; delete [] ch->cropimg;
ch->cropimg = NULL; ch->cropimg = NULL;
delete [] ch->cropimgtrue; delete [] ch->cropimgtrue;
ch->cropimgtrue = NULL; ch->cropimgtrue = NULL;
} }
ch->cimg.unlock (); ch->cimg.unlock ();
@@ -233,7 +233,7 @@ int createpixbufs (void* data) {
} }
void CropHandler::setDetailedCrop (IImage8* im, IImage8* imtrue, rtengine::procparams::ColorManagementParams cmp, void CropHandler::setDetailedCrop (IImage8* im, IImage8* imtrue, rtengine::procparams::ColorManagementParams cmp,
rtengine::procparams::CropParams cp, int ax, int ay, int aw, int ah, int askip) { rtengine::procparams::CropParams cp, int ax, int ay, int aw, int ah, int askip) {
if (!enabled) if (!enabled)
return; return;
@@ -255,9 +255,9 @@ void CropHandler::setDetailedCrop (IImage8* im, IImage8* imtrue, rtengine::procp
cropimg_width = im->getWidth (); cropimg_width = im->getWidth ();
cropimg_height = im->getHeight (); cropimg_height = im->getHeight ();
cropimg = new unsigned char [3*cropimg_width*cropimg_height]; cropimg = new unsigned char [3*cropimg_width*cropimg_height];
cropimgtrue = new unsigned char [3*cropimg_width*cropimg_height]; cropimgtrue = new unsigned char [3*cropimg_width*cropimg_height];
memcpy (cropimg, im->getData(), 3*cropimg_width*cropimg_height); memcpy (cropimg, im->getData(), 3*cropimg_width*cropimg_height);
memcpy (cropimgtrue, imtrue->getData(), 3*cropimg_width*cropimg_height); memcpy (cropimgtrue, imtrue->getData(), 3*cropimg_width*cropimg_height);
cix = ax; cix = ax;
ciy = ay; ciy = ay;
ciw = aw; ciw = aw;

View File

@@ -29,9 +29,12 @@
#include "rtimage.h" #include "rtimage.h"
#define CHECKTIME 5000 #define CHECKTIME 5000
extern Glib::ustring argv0;
DirBrowser::DirBrowser () { DirBrowser::DirBrowser () : expandSuccess(false)
#ifdef WIN32
, volumes(0)
#endif
{
dirtree = Gtk::manage ( new Gtk::TreeView() ); dirtree = Gtk::manage ( new Gtk::TreeView() );
scrolledwindow4 = Gtk::manage ( new Gtk::ScrolledWindow() ); scrolledwindow4 = Gtk::manage ( new Gtk::ScrolledWindow() );
@@ -144,7 +147,7 @@ void DirBrowser::updateVolumes () {
int nvolumes = GetLogicalDrives (); int nvolumes = GetLogicalDrives ();
if (nvolumes!=volumes) { if (nvolumes!=volumes) {
GThreadLock lock; GThreadLock lock;
for (int i=0; i<32; i++) for (int i=0; i<32; i++)
if (((volumes >> i) & 1) && !((nvolumes >> i) & 1)) { // volume i has been deleted if (((volumes >> i) & 1) && !((nvolumes >> i) & 1)) { // volume i has been deleted
@@ -237,23 +240,26 @@ void DirBrowser::updateDir (const Gtk::TreeModel::iterator& iter) {
for (Gtk::TreeModel::iterator it=iter->children().begin(); it!=iter->children().end(); it++) for (Gtk::TreeModel::iterator it=iter->children().begin(); it!=iter->children().end(); it++)
if (!safe_file_test (it->get_value (dtColumns.dirname), Glib::FILE_TEST_EXISTS) if (!safe_file_test (it->get_value (dtColumns.dirname), Glib::FILE_TEST_EXISTS)
|| !safe_file_test (it->get_value (dtColumns.dirname), Glib::FILE_TEST_IS_DIR)) { || !safe_file_test (it->get_value (dtColumns.dirname), Glib::FILE_TEST_IS_DIR)) {
GThreadLock lock;
dirTreeModel->erase (it); dirTreeModel->erase (it);
change = true; change = true;
break; break;
} }
} }
// test if new files are created // test if new files are created
std::vector<Glib::ustring> subDirs; std::vector<Glib::ustring> subDirs;
Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path (iter->get_value (dtColumns.dirname)); Glib::RefPtr<Gio::File> dir = Gio::File::create_for_path (iter->get_value (dtColumns.dirname));
safe_build_subdir_list (dir, subDirs, options.fbShowHidden); safe_build_subdir_list (dir, subDirs, options.fbShowHidden);
for (int i=0; i<subDirs.size(); i++) { for (int i=0; i<subDirs.size(); i++) {
bool found = false; bool found = false;
for (Gtk::TreeModel::iterator it=iter->children().begin(); it!=iter->children().end() && !found ; it++) for (Gtk::TreeModel::iterator it=iter->children().begin(); it!=iter->children().end() && !found ; it++)
found = (it->get_value (dtColumns.filename)==subDirs[i]); found = (it->get_value (dtColumns.filename)==subDirs[i]);
if (!found) if (!found) {
GThreadLock lock;
addDir (iter, subDirs[i]); addDir (iter, subDirs[i]);
}
} }
} }
@@ -353,9 +359,7 @@ void DirBrowser::file_changed (const Glib::RefPtr<Gio::File>& file, const Glib::
if (!file || !safe_file_test (dirName, Glib::FILE_TEST_IS_DIR) || event_type==Gio::FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) if (!file || !safe_file_test (dirName, Glib::FILE_TEST_IS_DIR) || event_type==Gio::FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED)
return; return;
gdk_threads_enter();
updateDir (iter); updateDir (iter);
gdk_threads_leave();
} }
void DirBrowser::selectDir (Glib::ustring dir) { void DirBrowser::selectDir (Glib::ustring dir) {

View File

@@ -24,6 +24,7 @@
class DirSelectionListener { class DirSelectionListener {
public: public:
virtual ~DirSelectionListener () {}
virtual void dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile="") {} virtual void dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile="") {}
}; };

View File

@@ -597,6 +597,7 @@ struct spparams {
int setprogressStrUI( void *p ) int setprogressStrUI( void *p )
{ {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
spparams *s= static_cast<spparams*>(p); spparams *s= static_cast<spparams*>(p);
if( ! s->str.empty() ) if( ! s->str.empty() )
@@ -662,6 +663,7 @@ void EditorPanel::refreshProcessingState (bool inProcessingP) {
s->val = 1.0; s->val = 1.0;
#ifdef WIN32 #ifdef WIN32
// Maybe accessing "parent", which is a Gtk object, can justify to get the Gtk lock...
if (!firstProcessingDone && static_cast<RTWindow*>(parent)->getIsFullscreen()) { parent->fullscreen(); } if (!firstProcessingDone && static_cast<RTWindow*>(parent)->getIsFullscreen()) { parent->fullscreen(); }
#endif #endif
firstProcessingDone = true; firstProcessingDone = true;
@@ -1108,19 +1110,11 @@ BatchQueueEntry* EditorPanel::createBatchQueueEntry () {
ipc->getParams (&pparams); ipc->getParams (&pparams);
//rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); //rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams);
rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (openThm->getFileName (), openThm->getType()==FT_Raw, pparams); rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (openThm->getFileName (), openThm->getType()==FT_Raw, pparams);
int prevh = options.maxThumbnailHeight; int fullW=0, fullH=0;
int prevw = prevh; isrc->getImageSource()->getFullSize(fullW, fullH, pparams.coarse.rotate==90 || pparams.coarse.rotate==270 ? TR_R90 : TR_NONE);
guint8* prev = NULL;//(guint8*) previewHandler->getImagePreview (prevw, prevh); int prevh = BatchQueue::calcMaxThumbnailHeight();
double tmpscale; int prevw = int((size_t)fullW * (size_t)prevh / (size_t)fullH);
rtengine::IImage8* img = openThm->processThumbImage (pparams, options.maxThumbnailHeight, tmpscale); return new BatchQueueEntry (job, pparams, openThm->getFileName(), prevw, prevh, openThm);
if (img) {
prevw = img->getWidth ();
prevh = img->getHeight ();
prev = new guint8 [prevw*prevh*3];
memcpy (prev, img->getData (), prevw*prevh*3);
img->free();
}
return new BatchQueueEntry (job, pparams, openThm->getFileName(), prev, prevw, prevh, openThm);
} }

View File

@@ -29,6 +29,7 @@
#include "../rtengine/dfmanager.h" #include "../rtengine/dfmanager.h"
#include "../rtengine/ffmanager.h" #include "../rtengine/ffmanager.h"
#include "rtimage.h" #include "rtimage.h"
#include "guiutils.h"
extern Options options; extern Options options;
@@ -106,7 +107,7 @@ FileBrowser::FileBrowser ()
/*********************** /***********************
* external programs * external programs
* *********************/ * *********************/
#ifdef WIN32 #if PROTECT_VECTORS
Gtk::manage(miOpenDefaultViewer=new Gtk::MenuItem (M("FILEBROWSER_OPENDEFAULTVIEWER"))); Gtk::manage(miOpenDefaultViewer=new Gtk::MenuItem (M("FILEBROWSER_OPENDEFAULTVIEWER")));
#else #else
miOpenDefaultViewer=NULL; miOpenDefaultViewer=NULL;
@@ -301,6 +302,12 @@ FileBrowser::~FileBrowser ()
void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) { void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) {
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
trash->set_sensitive (false); trash->set_sensitive (false);
untrash->set_sensitive (false); untrash->set_sensitive (false);
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
@@ -318,6 +325,7 @@ void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) {
partpasteprof->set_sensitive (clipboard.hasProcParams()); partpasteprof->set_sensitive (clipboard.hasProcParams());
copyprof->set_sensitive (selected.size()==1); copyprof->set_sensitive (selected.size()==1);
clearprof->set_sensitive (!selected.empty()); clearprof->set_sensitive (!selected.empty());
}
// submenu applmenu // submenu applmenu
int p = 0; int p = 0;
@@ -336,10 +344,10 @@ void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) {
Gtk::Menu* applpartmenu = Gtk::manage (new Gtk::Menu ()); Gtk::Menu* applpartmenu = Gtk::manage (new Gtk::Menu ());
//std::vector<Glib::ustring> profnames = profileStore.getProfileNames (); // this is already created for submenu applmenu above //std::vector<Glib::ustring> profnames = profileStore.getProfileNames (); // this is already created for submenu applmenu above
for (size_t i=0; i<profnames.size(); i++) { for (size_t i=0; i<profnames.size(); i++) {
Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (profnames[i])); Gtk::MenuItem* mi = Gtk::manage (new Gtk::MenuItem (profnames[i]));
applpartmenu->attach (*mi, 0, 1, p, p+1); p++; applpartmenu->attach (*mi, 0, 1, p, p+1); p++;
mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyPartialMenuItemActivated), profnames[i])); mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyPartialMenuItemActivated), profnames[i]));
mi->show (); mi->show ();
} }
applypartprof->set_submenu (*applpartmenu); applypartprof->set_submenu (*applpartmenu);
@@ -428,7 +436,7 @@ void FileBrowser::addEntry (FileBrowserEntry* entry) {
} }
void FileBrowser::addEntry_ (FileBrowserEntry* entry) { 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->selected = false;
entry->drawable = false; entry->drawable = false;
entry->framed = editedFiles.find (entry->filename)!=editedFiles.end(); entry->framed = editedFiles.find (entry->filename)!=editedFiles.end();
@@ -439,14 +447,14 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry) {
entry->getThumbButtonSet()->setColorLabel (entry->thumbnail->getColorLabel()); entry->getThumbButtonSet()->setColorLabel (entry->thumbnail->getColorLabel());
entry->getThumbButtonSet()->setInTrash (entry->thumbnail->getStage()==1); entry->getThumbButtonSet()->setInTrash (entry->thumbnail->getStage()==1);
entry->getThumbButtonSet()->setButtonListener (this); entry->getThumbButtonSet()->setButtonListener (this);
entry->resize (getCurrentThumbSize()); entry->resize (getThumbnailHeight());
// find place in abc order // find place in abc order
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
std::vector<ThumbBrowserEntryBase*>::iterator i = fd.begin(); std::vector<ThumbBrowserEntryBase*>::iterator i = fd.begin();
while (i!=fd.end() && *entry < *((FileBrowserEntry*)*i)) while (i!=fd.end() && *entry < *((FileBrowserEntry*)*i))
@@ -455,15 +463,15 @@ void FileBrowser::addEntry_ (FileBrowserEntry* entry) {
fd.insert (i, entry); fd.insert (i, entry);
initEntry (entry); initEntry (entry);
} }
redraw (); redraw ();
} }
FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) { FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
for (std::vector<ThumbBrowserEntryBase*>::iterator i=fd.begin(); i!=fd.end(); i++) for (std::vector<ThumbBrowserEntryBase*>::iterator i=fd.begin(); i!=fd.end(); i++)
if ((*i)->filename==fname) { if ((*i)->filename==fname) {
@@ -472,8 +480,8 @@ FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) {
fd.erase (i); fd.erase (i);
std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), entry); std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), entry);
#ifdef WIN32 #if PROTECT_VECTORS
l.release(); MYWRITERLOCK_RELEASE(l);
#endif #endif
if (j!=selected.end()) { if (j!=selected.end()) {
@@ -486,7 +494,7 @@ FileBrowserEntry* FileBrowser::delEntry (const Glib::ustring& fname) {
lastClicked = NULL; lastClicked = NULL;
redraw (); redraw ();
return (static_cast<FileBrowserEntry*>(entry)); return (static_cast<FileBrowserEntry*>(entry));
} }
return NULL; return NULL;
} }
@@ -502,22 +510,33 @@ void FileBrowser::close () {
fbih->destroyed = false; fbih->destroyed = false;
fbih->pending = 0; fbih->pending = 0;
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::WriterLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
selected.clear (); selected.clear ();
notifySelectionListener ();
// TODO: Check for Linux
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l); // notifySelectionListener will need read access!
#endif
notifySelectionListener ();
// TODO: Check for Linux
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// The listener merges parameters with old values, so delete afterwards // The listener merges parameters with old values, so delete afterwards
for (size_t i=0; i<fd.size(); i++) for (size_t i=0; i<fd.size(); i++)
{ {
delete fd[i]; delete fd.at(i);
} }
fd.clear (); fd.clear ();
} }
lastClicked = NULL; lastClicked = NULL;
} }
@@ -537,8 +556,17 @@ void FileBrowser::menuColorlabelActivated (Gtk::MenuItem* m) {
void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
std::vector<FileBrowserEntry*> mselected; std::vector<FileBrowserEntry*> mselected;
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i])); mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
}
if (!tbl || (m!=selall && mselected.empty()) ) if (!tbl || (m!=selall && mselected.empty()) )
return; return;
@@ -560,8 +588,8 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
// Build vector of all file names // Build vector of all file names
std::vector<Glib::ustring> selFileNames; std::vector<Glib::ustring> selFileNames;
for (int i=0; i<selected.size(); i++) { for (int i=0; i<mselected.size(); i++) {
Glib::ustring fn=selected[i]->thumbnail->getFileName(); Glib::ustring fn=mselected[i]->thumbnail->getFileName();
// Maybe batch processed version // Maybe batch processed version
if (pAct->target==2) fn = Glib::ustring::compose ("%1.%2", BatchQueue::calcAutoFileNameBase(fn), options.saveFormatBatch.format); if (pAct->target==2) fn = Glib::ustring::compose ("%1.%2", BatchQueue::calcAutoFileNameBase(fn), options.saveFormatBatch.format);
@@ -576,7 +604,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
if (m==open) { if (m==open) {
std::vector<Thumbnail*> entries; std::vector<Thumbnail*> entries;
for (size_t i=0; i<mselected.size(); i++) for (size_t i=0; i<mselected.size(); i++)
entries.push_back (mselected[i]->thumbnail); entries.push_back (mselected[i]->thumbnail);
tbl->openRequested (entries); tbl->openRequested (entries);
} }
@@ -598,19 +626,19 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
tbl->renameRequested (mselected); tbl->renameRequested (mselected);
else if (m==selall) { else if (m==selall) {
lastClicked = NULL; lastClicked = NULL;
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::ReaderLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
selected.clear (); selected.clear ();
for (size_t i=0; i<fd.size(); i++) for (size_t i=0; i<fd.size(); i++)
if (checkFilter (fd[i])) { if (checkFilter (fd[i])) {
fd[i]->selected = true; fd[i]->selected = true;
selected.push_back (fd[i]); selected.push_back (fd[i]);
} }
} }
queue_draw (); queue_draw ();
notifySelectionListener (); notifySelectionListener ();
} }
@@ -623,86 +651,103 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
} }
else if (m==autoDF){ else if (m==autoDF){
for (size_t i=0; i<mselected.size(); i++){ if (!mselected.empty() && bppcl)
rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams(); bppcl->beginBatchPParamsChange(mselected.size());
pp.raw.df_autoselect= true; for (size_t i=0; i<mselected.size(); i++){
pp.raw.dark_frame.clear(); rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
mselected[i]->thumbnail->setProcParams(pp,NULL,FILEBROWSER,false); 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){ }else if (m==selectDF){
if( !mselected.empty() ){ if( !mselected.empty() ){
rtengine::procparams::ProcParams pp=mselected[0]->thumbnail->getProcParams(); rtengine::procparams::ProcParams pp=mselected[0]->thumbnail->getProcParams();
Gtk::FileChooserDialog fc("Dark Frame",Gtk::FILE_CHOOSER_ACTION_OPEN ); Gtk::FileChooserDialog fc("Dark Frame",Gtk::FILE_CHOOSER_ACTION_OPEN );
FileChooserLastFolderPersister persister(&fc, options.lastDarkframeDir); FileChooserLastFolderPersister persister(&fc, options.lastDarkframeDir);
fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL); fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
fc.add_button( Gtk::StockID("gtk-apply"), Gtk::RESPONSE_APPLY); fc.add_button( Gtk::StockID("gtk-apply"), Gtk::RESPONSE_APPLY);
if(!pp.raw.dark_frame.empty()) if(!pp.raw.dark_frame.empty())
fc.set_filename( pp.raw.dark_frame ); fc.set_filename( pp.raw.dark_frame );
if( fc.run() == Gtk::RESPONSE_APPLY ){ if( fc.run() == Gtk::RESPONSE_APPLY ) {
for (size_t i=0; i<mselected.size(); i++){ if (bppcl)
rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams(); bppcl->beginBatchPParamsChange(mselected.size());
pp.raw.dark_frame= fc.get_filename(); for (size_t i=0; i<mselected.size(); i++){
pp.raw.df_autoselect= false; rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
mselected[i]->thumbnail->setProcParams(pp,NULL,FILEBROWSER,false); 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){ }else if( m==thisIsDF){
if( !options.rtSettings.darkFramesPath.empty()) { if( !options.rtSettings.darkFramesPath.empty()) {
if (Gio::File::create_for_path(options.rtSettings.darkFramesPath)->query_exists() ){ if (Gio::File::create_for_path(options.rtSettings.darkFramesPath)->query_exists() ){
for (size_t i=0; i<mselected.size(); i++){ for (size_t i=0; i<mselected.size(); i++){
Glib::RefPtr<Gio::File> file = Gio::File::create_for_path ( mselected[i]->filename ); Glib::RefPtr<Gio::File> file = Gio::File::create_for_path ( mselected[i]->filename );
if( !file )continue; if( !file )continue;
Glib::ustring destName = options.rtSettings.darkFramesPath+ "/" + file->get_basename(); Glib::ustring destName = options.rtSettings.darkFramesPath+ "/" + file->get_basename();
Glib::RefPtr<Gio::File> dest = Gio::File::create_for_path ( destName ); Glib::RefPtr<Gio::File> dest = Gio::File::create_for_path ( destName );
file->move( dest ); file->move( dest );
} }
// Reinit cache // Reinit cache
rtengine::dfm.init( options.rtSettings.darkFramesPath ); rtengine::dfm.init( options.rtSettings.darkFramesPath );
} }
else { else {
// Target directory creation failed, we clear the darkFramesPath setting // Target directory creation failed, we clear the darkFramesPath setting
options.rtSettings.darkFramesPath.clear(); options.rtSettings.darkFramesPath.clear();
Glib::ustring msg_ = Glib::ustring::compose (M("MAIN_MSG_PATHDOESNTEXIST"), options.rtSettings.darkFramesPath) Glib::ustring msg_ = Glib::ustring::compose (M("MAIN_MSG_PATHDOESNTEXIST"), options.rtSettings.darkFramesPath)
+"\n\n"+M("MAIN_MSG_OPERATIONCANCELLED"); +"\n\n"+M("MAIN_MSG_OPERATIONCANCELLED");
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.set_title(M("TP_DARKFRAME_LABEL")); msgd.set_title(M("TP_DARKFRAME_LABEL"));
msgd.run (); msgd.run ();
} }
} }
else { else {
Glib::ustring msg_ = M("MAIN_MSG_SETPATHFIRST")+"\n\n"+M("MAIN_MSG_OPERATIONCANCELLED"); 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); Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.set_title(M("TP_DARKFRAME_LABEL")); msgd.set_title(M("TP_DARKFRAME_LABEL"));
msgd.run (); msgd.run ();
} }
} }
else if (m==autoFF){ else if (m==autoFF){
for (size_t i=0; i<mselected.size(); i++){ if (!mselected.empty() && bppcl)
rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams(); bppcl->beginBatchPParamsChange(mselected.size());
pp.raw.ff_AutoSelect= true; for (size_t i=0; i<mselected.size(); i++){
pp.raw.ff_file.clear(); rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
mselected[i]->thumbnail->setProcParams(pp,NULL,FILEBROWSER,false); 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){ else if (m==selectFF){
if( !mselected.empty() ){ if( !mselected.empty() ){
rtengine::procparams::ProcParams pp=mselected[0]->thumbnail->getProcParams(); rtengine::procparams::ProcParams pp=mselected[0]->thumbnail->getProcParams();
Gtk::FileChooserDialog fc("Flat Field",Gtk::FILE_CHOOSER_ACTION_OPEN ); Gtk::FileChooserDialog fc("Flat Field",Gtk::FILE_CHOOSER_ACTION_OPEN );
FileChooserLastFolderPersister persister(&fc, options.lastFlatfieldDir); FileChooserLastFolderPersister persister(&fc, options.lastFlatfieldDir);
fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL); fc.add_button( Gtk::StockID("gtk-cancel"), Gtk::RESPONSE_CANCEL);
fc.add_button( Gtk::StockID("gtk-apply"), Gtk::RESPONSE_APPLY); fc.add_button( Gtk::StockID("gtk-apply"), Gtk::RESPONSE_APPLY);
if(!pp.raw.ff_file.empty()) if(!pp.raw.ff_file.empty())
fc.set_filename( pp.raw.ff_file ); fc.set_filename( pp.raw.ff_file );
if( fc.run() == Gtk::RESPONSE_APPLY ){ if( fc.run() == Gtk::RESPONSE_APPLY ) {
for (size_t i=0; i<mselected.size(); i++){ if (bppcl)
rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams(); bppcl->beginBatchPParamsChange(mselected.size());
pp.raw.ff_file= fc.get_filename(); for (size_t i=0; i<mselected.size(); i++){
pp.raw.ff_AutoSelect= false; rtengine::procparams::ProcParams pp=mselected[i]->thumbnail->getProcParams();
mselected[i]->thumbnail->setProcParams(pp,NULL,FILEBROWSER,false); 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){ else if( m==thisIsFF){
if( !options.rtSettings.flatFieldsPath.empty()) { if( !options.rtSettings.flatFieldsPath.empty()) {
@@ -745,13 +790,17 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
mselected[i]->thumbnail->clearProcParams (FILEBROWSER); mselected[i]->thumbnail->clearProcParams (FILEBROWSER);
queue_draw (); queue_draw ();
} else if (m==execcustprof) { } else if (m==execcustprof) {
for (size_t i=0; i<mselected.size(); i++) { if (!mselected.empty() && bppcl)
bppcl->beginBatchPParamsChange(mselected.size());
for (size_t i=0; i<mselected.size(); i++) {
mselected[i]->thumbnail->createProcParamsForUpdate (false, true); mselected[i]->thumbnail->createProcParamsForUpdate (false, true);
// Empty run to update the thumb // Empty run to update the thumb
rtengine::procparams::ProcParams params = mselected[i]->thumbnail->getProcParams (); rtengine::procparams::ProcParams params = mselected[i]->thumbnail->getProcParams ();
mselected[i]->thumbnail->setProcParams (params, NULL, FILEBROWSER); mselected[i]->thumbnail->setProcParams (params, NULL, FILEBROWSER);
} }
if (!mselected.empty() && bppcl)
bppcl->endBatchPParamsChange();
} else if (m==clearFromCache) { } else if (m==clearFromCache) {
for (size_t i=0; i<mselected.size(); i++) for (size_t i=0; i<mselected.size(); i++)
tbl->clearFromCacheRequested (mselected, false); tbl->clearFromCacheRequested (mselected, false);
@@ -767,6 +816,10 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) {
} }
void FileBrowser::copyProfile () { void FileBrowser::copyProfile () {
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (selected.size()==1) if (selected.size()==1)
clipboard.setProcParams ((static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->getProcParams()); clipboard.setProcParams ((static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->getProcParams());
@@ -776,12 +829,21 @@ void FileBrowser::pasteProfile () {
if (clipboard.hasProcParams()) { if (clipboard.hasProcParams()) {
std::vector<FileBrowserEntry*> mselected; std::vector<FileBrowserEntry*> mselected;
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
for (unsigned int i=0; i<selected.size(); i++) for (unsigned int i=0; i<selected.size(); i++)
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i])); mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
}
if (!tbl || mselected.empty()) if (!tbl || mselected.empty())
return; return;
if (!mselected.empty() && bppcl)
bppcl->beginBatchPParamsChange(mselected.size());
for (unsigned int i=0; i<mselected.size(); i++) { for (unsigned int i=0; i<mselected.size(); i++) {
// copying read only clipboard PartialProfile to a temporary one // copying read only clipboard PartialProfile to a temporary one
rtengine::procparams::PartialProfile cbPartProf = clipboard.getPartialProfile(); rtengine::procparams::PartialProfile cbPartProf = clipboard.getPartialProfile();
@@ -791,6 +853,8 @@ void FileBrowser::pasteProfile () {
mselected[i]->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER); mselected[i]->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER);
pastedPartProf.deleteInstance(); pastedPartProf.deleteInstance();
} }
if (!mselected.empty() && bppcl)
bppcl->endBatchPParamsChange();
queue_draw (); queue_draw ();
} }
@@ -801,8 +865,15 @@ void FileBrowser::partPasteProfile () {
if (clipboard.hasProcParams()) { if (clipboard.hasProcParams()) {
std::vector<FileBrowserEntry*> mselected; std::vector<FileBrowserEntry*> mselected;
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
for (unsigned int i=0; i<selected.size(); i++) for (unsigned int i=0; i<selected.size(); i++)
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i])); mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
}
if (!tbl || mselected.empty()) if (!tbl || mselected.empty())
return; return;
@@ -810,6 +881,8 @@ void FileBrowser::partPasteProfile () {
int i = partialPasteDlg.run (); int i = partialPasteDlg.run ();
if (i == Gtk::RESPONSE_OK) { if (i == Gtk::RESPONSE_OK) {
if (!mselected.empty() && bppcl)
bppcl->beginBatchPParamsChange(mselected.size());
for (unsigned int i=0; i<mselected.size(); i++) { for (unsigned int i=0; i<mselected.size(); i++) {
// copying read only clipboard PartialProfile to a temporary one, initialized to the thumb's ProcParams // 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 mselected[i]->thumbnail->createProcParamsForUpdate(false,false); // this can execute customprofilebuilder to generate param file
@@ -823,6 +896,8 @@ void FileBrowser::partPasteProfile () {
mselected[i]->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER); mselected[i]->thumbnail->setProcParams (*pastedPartProf.pparams, pastedPartProf.pedited, FILEBROWSER);
pastedPartProf.deleteInstance(); pastedPartProf.deleteInstance();
} }
if (!mselected.empty() && bppcl)
bppcl->endBatchPParamsChange();
queue_draw (); queue_draw ();
} }
@@ -832,9 +907,17 @@ void FileBrowser::partPasteProfile () {
void FileBrowser::openDefaultViewer (int destination) { void FileBrowser::openDefaultViewer (int destination) {
bool success=true; bool success=true;
{
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (selected.size()==1) if (selected.size()==1)
success=(static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->openDefaultViewer(destination); success=(static_cast<FileBrowserEntry*>(selected[0]))->thumbnail->openDefaultViewer(destination);
}
if (!success) { if (!success) {
Gtk::MessageDialog msgd (M("MAIN_MSG_IMAGEUNPROCESSED"), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); Gtk::MessageDialog msgd (M("MAIN_MSG_IMAGEUNPROCESSED"), true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.run (); msgd.run ();
@@ -956,18 +1039,47 @@ bool FileBrowser::keyPressed (GdkEventKey* event) {
return false; 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, 200), 10);
else
return std::max(std::min(options.thumbSize, 200), 10);
}
void FileBrowser::applyMenuItemActivated (Glib::ustring ppname) { void FileBrowser::applyMenuItemActivated (Glib::ustring ppname) {
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
const rtengine::procparams::PartialProfile* partProfile = profileStore.getProfile (ppname); const rtengine::procparams::PartialProfile* partProfile = profileStore.getProfile (ppname);
if (partProfile->pparams && !selected.empty()) { if (partProfile->pparams && !selected.empty()) {
for (size_t i=0; i<selected.size(); i++) 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); (static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->setProcParams (*partProfile->pparams, partProfile->pedited, FILEBROWSER);
if (bppcl)
bppcl->endBatchPParamsChange();
queue_draw (); queue_draw ();
} }
} }
void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) { void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) {
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (!tbl || selected.empty()) if (!tbl || selected.empty())
return; return;
@@ -976,6 +1088,8 @@ void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) {
if (srcProfiles->pparams) { if (srcProfiles->pparams) {
if (partialPasteDlg.run()==Gtk::RESPONSE_OK) { if (partialPasteDlg.run()==Gtk::RESPONSE_OK) {
if (bppcl)
bppcl->beginBatchPParamsChange(selected.size());
for (size_t i=0; i<selected.size(); i++) { for (size_t i=0; i<selected.size(); i++) {
selected[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file selected[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
@@ -986,6 +1100,8 @@ void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) {
(static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->setProcParams (*dstProfile.pparams, dstProfile.pedited, FILEBROWSER); (static_cast<FileBrowserEntry*>(selected[i]))->thumbnail->setProcParams (*dstProfile.pparams, dstProfile.pedited, FILEBROWSER);
dstProfile.deleteInstance(); dstProfile.deleteInstance();
} }
if (bppcl)
bppcl->endBatchPParamsChange();
queue_draw (); queue_draw ();
} }
partialPasteDlg.hide (); partialPasteDlg.hide ();
@@ -999,16 +1115,16 @@ void FileBrowser::applyFilter (const BrowserFilter& filter) {
// remove items not complying the filter from the selection // remove items not complying the filter from the selection
bool selchanged = false; bool selchanged = false;
numFiltered=0; numFiltered=0;
{ {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::ReaderLock l(entryRW); // Don't make this a writer lock! MYWRITERLOCK(l, entryRW); // Don't make this a writer lock! HOMBRE: Why? 'selected' is modified here
#endif #endif
for (size_t i=0; i<fd.size(); i++) { for (size_t i=0; i<fd.size(); i++) {
if (checkFilter (fd[i])) if (checkFilter (fd[i]))
numFiltered++; numFiltered++;
else if (fd[i]->selected ) { else if (fd[i]->selected ) {
fd[i]->selected = false; fd[i]->selected = false;
std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), fd[i]); std::vector<ThumbBrowserEntryBase*>::iterator j = std::find (selected.begin(), selected.end(), fd[i]);
selected.erase (j); selected.erase (j);
@@ -1017,7 +1133,7 @@ void FileBrowser::applyFilter (const BrowserFilter& filter) {
selchanged = true; selchanged = true;
} }
} }
} }
if (selchanged) if (selchanged)
notifySelectionListener (); notifySelectionListener ();
@@ -1138,42 +1254,59 @@ void FileBrowser::fromTrashRequested (std::vector<FileBrowserEntry*> tbe) {
void FileBrowser::rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank) { void FileBrowser::rankingRequested (std::vector<FileBrowserEntry*> tbe, int rank) {
for (size_t i=0; i<tbe.size(); i++) { if (!tbe.empty() && bppcl)
// try to load the last saved parameters from the cache or from the paramfile file bppcl->beginBatchPParamsChange(tbe.size());
tbe[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
// notify listeners TODO: should do this ONLY when params changed by customprofilebuilder? for (size_t i=0; i<tbe.size(); i++) {
tbe[i]->thumbnail->notifylisterners_procParamsChanged(FILEBROWSER);
// try to load the last saved parameters from the cache or from the paramfile file
tbe[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
// 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->setRank (rank);
tbe[i]->thumbnail->updateCache (); // needed to save the colorlabel to disk in the procparam file(s) and the cache image data file 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? //TODO? - should update pparams instead?
if (tbe[i]->getThumbButtonSet()) if (tbe[i]->getThumbButtonSet())
tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank()); tbe[i]->getThumbButtonSet()->setRank (tbe[i]->thumbnail->getRank());
} }
applyFilter (filter); applyFilter (filter);
if (!tbe.empty() && bppcl)
bppcl->endBatchPParamsChange();
} }
void FileBrowser::colorlabelRequested (std::vector<FileBrowserEntry*> tbe, int colorlabel) { void FileBrowser::colorlabelRequested (std::vector<FileBrowserEntry*> tbe, int colorlabel) {
for (size_t i=0; i<tbe.size(); i++) { if (!tbe.empty() && bppcl)
// try to load the last saved parameters from the cache or from the paramfile file bppcl->beginBatchPParamsChange(tbe.size());
tbe[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
// notify listeners TODO: should do this ONLY when params changed by customprofilebuilder? for (size_t i=0; i<tbe.size(); i++) {
tbe[i]->thumbnail->notifylisterners_procParamsChanged(FILEBROWSER); // try to load the last saved parameters from the cache or from the paramfile file
tbe[i]->thumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file
// 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->setColorLabel (colorlabel);
tbe[i]->thumbnail->updateCache(); // needed to save the colorlabel to disk in the procparam file(s) and the cache image data file 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? //TODO? - should update pparams instead?
if (tbe[i]->getThumbButtonSet()) if (tbe[i]->getThumbButtonSet())
tbe[i]->getThumbButtonSet()->setColorLabel (tbe[i]->thumbnail->getColorLabel()); tbe[i]->getThumbButtonSet()->setColorLabel (tbe[i]->thumbnail->getColorLabel());
} }
applyFilter (filter); applyFilter (filter);
if (!tbe.empty() && bppcl)
bppcl->endBatchPParamsChange();
} }
void FileBrowser::requestRanking(int rank){ void FileBrowser::requestRanking(int rank){
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
std::vector<FileBrowserEntry*> mselected; std::vector<FileBrowserEntry*> mselected;
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i])); mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
@@ -1182,6 +1315,10 @@ void FileBrowser::requestRanking(int rank){
} }
void FileBrowser::requestColorLabel(int colorlabel){ void FileBrowser::requestColorLabel(int colorlabel){
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
std::vector<FileBrowserEntry*> mselected; std::vector<FileBrowserEntry*> mselected;
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
mselected.push_back (static_cast<FileBrowserEntry*>(selected[i])); mselected.push_back (static_cast<FileBrowserEntry*>(selected[i]));
@@ -1219,8 +1356,8 @@ void FileBrowser::buttonPressed (LWButton* button, int actionCode, void* actionD
void FileBrowser::openNextImage () { void FileBrowser::openNextImage () {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::ReaderLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
if (!fd.empty() && selected.size()>0 && !options.tabbedUI) { if (!fd.empty() && selected.size()>0 && !options.tabbedUI) {
@@ -1240,8 +1377,18 @@ void FileBrowser::openNextImage () {
fd[k]->selected = true; fd[k]->selected = true;
selected.push_back (fd[k]); selected.push_back (fd[k]);
//queue_draw (); //queue_draw ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// this will require a read access
notifySelectionListener (); notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// scroll to the selected position // scroll to the selected position
double h1, v1; double h1, v1;
getScrollPosition(h1,v1); getScrollPosition(h1,v1);
@@ -1249,15 +1396,22 @@ void FileBrowser::openNextImage () {
double h2=selected[0]->getStartX(); double h2=selected[0]->getStartX();
double v2=selected[0]->getStartY(); 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 // scroll only when selected[0] is outside of the displayed bounds
if (h2+fd[k]->getMinimalWidth()-h1 > get_width()) if (h2+minWidth-h1 > get_width())
setScrollPosition(h2-(get_width()-fd[k]->getMinimalWidth()),v2); setScrollPosition(h2-minWidth,v2);
if (h1>h2) if (h1>h2)
setScrollPosition(h2,v2); setScrollPosition(h2,v2);
// open the selected image // open the selected image
std::vector<Thumbnail*> entries; std::vector<Thumbnail*> entries;
entries.push_back ((static_cast<FileBrowserEntry*>(fd[k]))->thumbnail); entries.push_back (thumb);
tbl->openRequested (entries); tbl->openRequested (entries);
return; return;
} }
@@ -1270,8 +1424,8 @@ void FileBrowser::openNextImage () {
void FileBrowser::openPrevImage () { void FileBrowser::openPrevImage () {
// TODO: Check for Linux // TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::ReaderLock l(entryRW); MYWRITERLOCK(l, entryRW);
#endif #endif
if (!fd.empty() && selected.size()>0 && !options.tabbedUI) { if (!fd.empty() && selected.size()>0 && !options.tabbedUI) {
@@ -1291,8 +1445,18 @@ void FileBrowser::openPrevImage () {
fd[k]->selected = true; fd[k]->selected = true;
selected.push_back (fd[k]); selected.push_back (fd[k]);
//queue_draw (); //queue_draw ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// this will require a read access
notifySelectionListener (); notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// scroll to the selected position // scroll to the selected position
double h1, v1; double h1, v1;
getScrollPosition(h1,v1); getScrollPosition(h1,v1);
@@ -1300,15 +1464,22 @@ void FileBrowser::openPrevImage () {
double h2=selected[0]->getStartX(); double h2=selected[0]->getStartX();
double v2=selected[0]->getStartY(); 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 // scroll only when selected[0] is outside of the displayed bounds
if (h2+fd[k]->getMinimalWidth()-h1 > get_width()) if (h2+minWidth-h1 > get_width())
setScrollPosition(h2-(get_width()-fd[k]->getMinimalWidth()),v2); setScrollPosition(h2-minWidth,v2);
if (h1>h2) if (h1>h2)
setScrollPosition(h2,v2); setScrollPosition(h2,v2);
// open the selected image // open the selected image
std::vector<Thumbnail*> entries; std::vector<Thumbnail*> entries;
entries.push_back ((static_cast<FileBrowserEntry*>(fd[k]))->thumbnail); entries.push_back (thumb);
tbl->openRequested (entries); tbl->openRequested (entries);
return; return;
} }
@@ -1324,6 +1495,11 @@ void FileBrowser::selectImage (Glib::ustring fname) {
// need to clear the filter in filecatalog // need to clear the filter in filecatalog
// TODO: Check for Linux
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
if (!fd.empty() && !options.tabbedUI) { if (!fd.empty() && !options.tabbedUI) {
for (size_t i=0; i<fd.size(); i++){ for (size_t i=0; i<fd.size(); i++){
if (fname==fd[i]->filename && !fd[i]->filtered) { if (fname==fd[i]->filename && !fd[i]->filtered) {
@@ -1338,11 +1514,26 @@ void FileBrowser::selectImage (Glib::ustring fname) {
fd[i]->selected = true; fd[i]->selected = true;
selected.push_back (fd[i]); selected.push_back (fd[i]);
queue_draw (); queue_draw ();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
// this will require a read access
notifySelectionListener (); notifySelectionListener ();
#if PROTECT_VECTORS
MYWRITERLOCK_ACQUIRE(l);
#endif
// scroll to the selected position // scroll to the selected position
double h=selected[0]->getStartX(); double h=selected[0]->getStartX();
double v=selected[0]->getStartY(); double v=selected[0]->getStartY();
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
setScrollPosition(h,v); setScrollPosition(h,v);
return; return;
@@ -1373,6 +1564,7 @@ void FileBrowser::_thumbRearrangementNeeded () {
} }
void FileBrowser::thumbRearrangementNeeded () { void FileBrowser::thumbRearrangementNeeded () {
// refreshThumbImagesUI will handle thread safety itself
g_idle_add (refreshThumbImagesUI, this); g_idle_add (refreshThumbImagesUI, this);
} }
@@ -1384,15 +1576,20 @@ void FileBrowser::selectionChanged () {
void FileBrowser::notifySelectionListener () { void FileBrowser::notifySelectionListener () {
if (tbl) { if (tbl) {
// TODO: Check for Linux
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
std::vector<Thumbnail*> thm; std::vector<Thumbnail*> thm;
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
thm.push_back ((static_cast<FileBrowserEntry*>(selected[i]))->thumbnail); thm.push_back ((static_cast<FileBrowserEntry*>(selected[i]))->thumbnail);
tbl->selectionChanged (thm); tbl->selectionChanged (thm);
} }
} }
void FileBrowser::redrawNeeded (LWButton* button) { void FileBrowser::redrawNeeded (LWButton* button) {
GThreadLock lock;
queue_draw (); queue_draw ();
} }
FileBrowser::type_trash_changed FileBrowser::trash_changed () { FileBrowser::type_trash_changed FileBrowser::trash_changed () {

View File

@@ -25,6 +25,7 @@
#include "exiffiltersettings.h" #include "exiffiltersettings.h"
#include "filebrowserentry.h" #include "filebrowserentry.h"
#include "browserfilter.h" #include "browserfilter.h"
#include "pparamschangelistener.h"
#include "partialpastedlg.h" #include "partialpastedlg.h"
#include "exportpanel.h" #include "exportpanel.h"
#include "extprog.h" #include "extprog.h"
@@ -34,6 +35,7 @@ class FileBrowserEntry;
class FileBrowserListener { class FileBrowserListener {
public: public:
virtual ~FileBrowserListener () {}
virtual void openRequested (std::vector<Thumbnail*> tbe) {} virtual void openRequested (std::vector<Thumbnail*> tbe) {}
virtual void developRequested (std::vector<FileBrowserEntry*> tbe, bool fastmode) {} virtual void developRequested (std::vector<FileBrowserEntry*> tbe, bool fastmode) {}
virtual void renameRequested (std::vector<FileBrowserEntry*> tbe) {} virtual void renameRequested (std::vector<FileBrowserEntry*> tbe) {}
@@ -112,6 +114,7 @@ class FileBrowser : public ThumbBrowserBase,
Glib::RefPtr<Gtk::AccelGroup> pmaccelgroup; Glib::RefPtr<Gtk::AccelGroup> pmaccelgroup;
BatchPParamsChangeListener* bppcl;
FileBrowserListener* tbl; FileBrowserListener* tbl;
BrowserFilter filter; BrowserFilter filter;
int numFiltered; int numFiltered;
@@ -140,6 +143,7 @@ class FileBrowser : public ThumbBrowserBase,
FileBrowserEntry* delEntry (const Glib::ustring& fname); // return the entry if found here return NULL otherwise FileBrowserEntry* delEntry (const Glib::ustring& fname); // return the entry if found here return NULL otherwise
void close (); void close ();
void setBatchPParamsChangeListener (BatchPParamsChangeListener* l) { bppcl = l; }
void setFileBrowserListener (FileBrowserListener* l) { tbl = l; } void setFileBrowserListener (FileBrowserListener* l) { tbl = l; }
void menuItemActivated (Gtk::MenuItem* m); void menuItemActivated (Gtk::MenuItem* m);
@@ -156,6 +160,9 @@ class FileBrowser : public ThumbBrowserBase,
void doubleClicked (ThumbBrowserEntryBase* entry); void doubleClicked (ThumbBrowserEntryBase* entry);
bool keyPressed (GdkEventKey* event); bool keyPressed (GdkEventKey* event);
void saveThumbnailHeight (int height);
int getThumbnailHeight ();
void openNextImage (); void openNextImage ();
void openPrevImage (); void openPrevImage ();
void copyProfile (); void copyProfile ();

View File

@@ -203,11 +203,14 @@ void FileBrowserEntry::updateImage (rtengine::IImage8* img, double scale, rtengi
param->img = img; param->img = img;
param->scale = scale; param->scale = scale;
param->cropParams = cropParams; param->cropParams = cropParams;
g_idle_add (updateImageUI, param); g_idle_add_full (G_PRIORITY_LOW, updateImageUI, param, NULL);
} }
void FileBrowserEntry::_updateImage (rtengine::IImage8* img, double s, rtengine::procparams::CropParams cropParams) { void FileBrowserEntry::_updateImage (rtengine::IImage8* img, double s, rtengine::procparams::CropParams cropParams) {
Glib::RWLock::WriterLock l(lockRW);
#if PROTECT_VECTORS
MYWRITERLOCK(l, lockRW);
#endif
redrawRequests--; redrawRequests--;
scale = s; scale = s;
@@ -219,6 +222,8 @@ void FileBrowserEntry::_updateImage (rtengine::IImage8* img, double s, rtengine:
if (preh == img->getHeight ()) { if (preh == img->getHeight ()) {
prew = img->getWidth (); prew = img->getWidth ();
GThreadLock lock;
// Check if image has been rotated since last time // Check if image has been rotated since last time
rotated = preview!=NULL && newLandscape!=landscape; rotated = preview!=NULL && newLandscape!=landscape;

View File

@@ -38,8 +38,6 @@ using namespace std;
#define CHECKTIME 2000 #define CHECKTIME 2000
extern Glib::ustring argv0;
FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb, FilePanel* filepanel) : FileCatalog::FileCatalog (CoarsePanel* cp, ToolBar* tb, FilePanel* filepanel) :
filepanel(filepanel), filepanel(filepanel),
selectedDirectoryId(1), selectedDirectoryId(1),
@@ -462,9 +460,12 @@ void FileCatalog::closeDir () {
// remove entries // remove entries
selectedDirectory = ""; selectedDirectory = "";
fileBrowser->close (); fileBrowser->close ();
fileNameList.clear (); fileNameList.clear ();
{
Glib::Mutex::Lock lock(filterMutex);
dirEFS.clear (); dirEFS.clear ();
}
hasValidCurrentEFS = false; hasValidCurrentEFS = false;
redrawAll (); redrawAll ();
} }
@@ -537,13 +538,15 @@ void FileCatalog::_refreshProgressBar () {
// Also mention that this progress bar only measures the FIRST pass (quick thumbnails) // Also mention that this progress bar only measures the FIRST pass (quick thumbnails)
// The second, usually longer pass is done multithreaded down in the single entries and is NOT measured by this // The second, usually longer pass is done multithreaded down in the single entries and is NOT measured by this
if (!inTabMode) { if (!inTabMode) {
Gtk::Notebook *nb =(Gtk::Notebook *)(filepanel->get_parent()); GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
Gtk::Box* hbb=NULL;
Gtk::Label *label=NULL; Gtk::Notebook *nb =(Gtk::Notebook *)(filepanel->get_parent());
if( options.mainNBVertical ) Gtk::Box* hbb=NULL;
hbb = Gtk::manage (new Gtk::VBox ()); Gtk::Label *label=NULL;
else if( options.mainNBVertical )
hbb = Gtk::manage (new Gtk::HBox ()); hbb = Gtk::manage (new Gtk::VBox ());
else
hbb = Gtk::manage (new Gtk::HBox ());
if (!previewsToLoad ) { if (!previewsToLoad ) {
hbb->pack_start (*Gtk::manage (new Gtk::Image (Gtk::Stock::DIRECTORY, Gtk::ICON_SIZE_MENU))); hbb->pack_start (*Gtk::manage (new Gtk::Image (Gtk::Stock::DIRECTORY, Gtk::ICON_SIZE_MENU)));
int filteredCount=fileBrowser->getNumFiltered(); int filteredCount=fileBrowser->getNumFiltered();
@@ -573,17 +576,9 @@ int refreshProgressBarUI (void* data) {
} }
void FileCatalog::previewReady (int dir_id, FileBrowserEntry* fdn) { void FileCatalog::previewReady (int dir_id, FileBrowserEntry* fdn) {
GThreadLock lock;
previewReadyUI (dir_id,fdn);
}
// Called WITHIN gtk thread if ( dir_id != selectedDirectoryId )
void FileCatalog::previewReadyUI (int dir_id, FileBrowserEntry* fdn) { return;
if ( dir_id != selectedDirectoryId )
{
return;
}
// put it into the "full directory" browser // put it into the "full directory" browser
fdn->setImageAreaToolListener (iatlistener); fdn->setImageAreaToolListener (iatlistener);
@@ -591,6 +586,9 @@ void FileCatalog::previewReadyUI (int dir_id, FileBrowserEntry* fdn) {
// update exif filter settings (minimal & maximal values of exif tags, cameras, lenses, etc...) // update exif filter settings (minimal & maximal values of exif tags, cameras, lenses, etc...)
const CacheImageData* cfs = fdn->thumbnail->getCacheImageData(); const CacheImageData* cfs = fdn->thumbnail->getCacheImageData();
{
Glib::Mutex::Lock lock(filterMutex);
if (cfs->exifValid) { if (cfs->exifValid) {
if (cfs->fnumber < dirEFS.fnumberFrom) if (cfs->fnumber < dirEFS.fnumberFrom)
dirEFS.fnumberFrom = cfs->fnumber; dirEFS.fnumberFrom = cfs->fnumber;
@@ -613,13 +611,14 @@ void FileCatalog::previewReadyUI (int dir_id, FileBrowserEntry* fdn) {
dirEFS.cameras.insert (cfs->camera); dirEFS.cameras.insert (cfs->camera);
dirEFS.lenses.insert (cfs->lens); dirEFS.lenses.insert (cfs->lens);
dirEFS.expcomp.insert (cfs->expcomp); dirEFS.expcomp.insert (cfs->expcomp);
}
previewsLoaded++; previewsLoaded++;
g_idle_add (refreshProgressBarUI, this); g_idle_add (refreshProgressBarUI, this);
} }
int prevfinished (void* data) { int prevfinished (void* data) {
GThreadLock lock;
(static_cast<FileCatalog*>(data))->previewsFinishedUI (); (static_cast<FileCatalog*>(data))->previewsFinishedUI ();
return 0; return 0;
} }
@@ -627,26 +626,30 @@ int prevfinished (void* data) {
// Called within GTK UI thread // Called within GTK UI thread
void FileCatalog::previewsFinishedUI () { void FileCatalog::previewsFinishedUI () {
redrawAll (); {
previewsToLoad = 0; GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
redrawAll ();
previewsToLoad = 0;
if (filterPanel) { if (filterPanel) {
filterPanel->set_sensitive (true); filterPanel->set_sensitive (true);
if ( !hasValidCurrentEFS ){ Glib::Mutex::Lock lock(filterMutex);
currentEFS = dirEFS; if ( !hasValidCurrentEFS ){
filterPanel->setFilter ( dirEFS,true ); currentEFS = dirEFS;
}else { filterPanel->setFilter ( dirEFS,true );
filterPanel->setFilter ( currentEFS,false ); }else {
} filterPanel->setFilter ( currentEFS,false );
} }
}
if (exportPanel)
exportPanel->set_sensitive (true);
// restart anything that might have been loaded low quality if (exportPanel)
fileBrowser->refreshQuickThumbImages(); exportPanel->set_sensitive (true);
fileBrowser->applyFilter (getFilter()); // refresh total image count
_refreshProgressBar(); // restart anything that might have been loaded low quality
fileBrowser->refreshQuickThumbImages();
fileBrowser->applyFilter (getFilter()); // refresh total image count
_refreshProgressBar();
}
filepanel->loadingThumbs(M("PROGRESSBAR_READY"),0); filepanel->loadingThumbs(M("PROGRESSBAR_READY"),0);
if (!imageToSelect_fname.empty()){ if (!imageToSelect_fname.empty()){
@@ -668,8 +671,10 @@ void FileCatalog::previewsFinished (int dir_id) {
return; return;
} }
if (!hasValidCurrentEFS) if (!hasValidCurrentEFS) {
Glib::Mutex::Lock lock(filterMutex);
currentEFS = dirEFS; currentEFS = dirEFS;
}
g_idle_add (prevfinished, this); g_idle_add (prevfinished, this);
} }
@@ -689,9 +694,9 @@ void FileCatalog::refreshThumbImages () {
void FileCatalog::refreshHeight () { void FileCatalog::refreshHeight () {
int newHeight=fileBrowser->getEffectiveHeight() + buttonBar->get_height(); int newHeight=fileBrowser->getEffectiveHeight() + buttonBar->get_height();
if (!options.FileBrowserToolbarSingleRow) { if (!options.FileBrowserToolbarSingleRow) {
newHeight += hbToolBar1->get_height(); newHeight += hbToolBar1->get_height();
} }
set_size_request(0, newHeight); set_size_request(0, newHeight+2); // HOMBRE: yeah, +2, there's always 2 pixels missing... sorry for this dirty hack O:)
} }
void FileCatalog::_openImage (std::vector<Thumbnail*> tmb) { void FileCatalog::_openImage (std::vector<Thumbnail*> tmb) {
@@ -879,11 +884,14 @@ void FileCatalog::developRequested (std::vector<FileBrowserEntry*> tbe, bool fas
if (listener) { if (listener) {
std::vector<BatchQueueEntry*> entries; std::vector<BatchQueueEntry*> entries;
#pragma omp parallel for ordered // TODO: (HOMBRE) should we still use parallelization here, now that thumbnails are processed asynchronously...?
//#pragma omp parallel for ordered
for (size_t i=0; i<tbe.size(); i++) { for (size_t i=0; i<tbe.size(); i++) {
rtengine::procparams::ProcParams params = tbe[i]->thumbnail->getProcParams(); FileBrowserEntry* fbe = tbe[i];
Thumbnail* th = fbe->thumbnail;
rtengine::procparams::ProcParams params = th->getProcParams();
// if fast mode is selected, override (disable) prams // if fast mode is selected, override (disable) params
// controlling time and resource consuming tasks // controlling time and resource consuming tasks
// and also those which effect is not pronounced after reducing the image size // and also those which effect is not pronounced after reducing the image size
// TODO!!! could expose selections below via preferences // TODO!!! could expose selections below via preferences
@@ -921,30 +929,19 @@ void FileCatalog::developRequested (std::vector<FileBrowserEntry*> tbe, bool fas
params.resize.height = options.fastexport_resize_height ; params.resize.height = options.fastexport_resize_height ;
} }
rtengine::ProcessingJob* pjob = rtengine::ProcessingJob::create (tbe[i]->filename, tbe[i]->thumbnail->getType()==FT_Raw, params); rtengine::ProcessingJob* pjob = rtengine::ProcessingJob::create (fbe->filename, th->getType()==FT_Raw, params);
double tmpscale;
rtengine::IImage8* img = tbe[i]->thumbnail->processThumbImage (params, BatchQueue::calcMaxThumbnailHeight(), tmpscale);
int pw, ph; int pw;
guint8* prev=NULL; int ph = BatchQueue::calcMaxThumbnailHeight();
th->getThumbnailSize (pw, ph);
if (img) {
pw = img->getWidth ();
ph = img->getHeight ();
prev = new guint8 [pw*ph*3];
memcpy (prev, img->getData (), pw*ph*3);
img->free();
} else {
tbe[i]->thumbnail->getThumbnailSize (pw, ph);
}
// processThumbImage is the processing intensive part, but adding to queue must be ordered // processThumbImage is the processing intensive part, but adding to queue must be ordered
#pragma omp ordered //#pragma omp ordered
{ //{
entries.push_back(new BatchQueueEntry (pjob, params, tbe[i]->filename, prev, pw, ph, tbe[i]->thumbnail)); BatchQueueEntry* bqh = new BatchQueueEntry (pjob, params, fbe->filename, pw, ph, th);
} entries.push_back(bqh);
} //}
}
listener->addBatchQueueJobs( entries ); listener->addBatchQueueJobs( entries );
} }
@@ -1324,8 +1321,10 @@ BrowserFilter FileCatalog::getFilter () {
if (!filterPanel) if (!filterPanel)
filter.exifFilterEnabled = false; filter.exifFilterEnabled = false;
else { else {
if (!hasValidCurrentEFS) Glib::Mutex::Lock lock(filterMutex);
if (!hasValidCurrentEFS) {
filter.exifFilter = dirEFS; filter.exifFilter = dirEFS;
}
else else
filter.exifFilter = currentEFS; filter.exifFilter = currentEFS;
filter.exifFilterEnabled = filterPanel->isEnabled (); filter.exifFilterEnabled = filterPanel->isEnabled ();
@@ -1408,15 +1407,14 @@ void FileCatalog::winDirChanged () {
void FileCatalog::on_dir_changed (const Glib::RefPtr<Gio::File>& file, const Glib::RefPtr<Gio::File>& other_file, Gio::FileMonitorEvent event_type, bool internal) { 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 (options.has_retained_extention(file->get_parse_name())) { if (options.has_retained_extention(file->get_parse_name())
if (!internal) && (event_type == Gio::FILE_MONITOR_EVENT_CREATED || event_type == Gio::FILE_MONITOR_EVENT_DELETED || event_type == Gio::FILE_MONITOR_EVENT_CHANGED)) {
gdk_threads_enter(); if (!internal) {
GThreadLock lock;
if (event_type == Gio::FILE_MONITOR_EVENT_CREATED || event_type == Gio::FILE_MONITOR_EVENT_DELETED || event_type == Gio::FILE_MONITOR_EVENT_CHANGED) reparseDirectory ();
reparseDirectory (); }
else
if (!internal) reparseDirectory ();
gdk_threads_leave();
} }
} }
@@ -1454,7 +1452,7 @@ void FileCatalog::addAndOpenFile (const Glib::ustring& fname) {
Thumbnail* tmb = cacheMgr->getEntry (file->get_parse_name()); Thumbnail* tmb = cacheMgr->getEntry (file->get_parse_name());
if (tmb) { if (tmb) {
FileBrowserEntry* entry = new FileBrowserEntry (tmb, file->get_parse_name()); FileBrowserEntry* entry = new FileBrowserEntry (tmb, file->get_parse_name());
previewReadyUI (selectedDirectoryId,entry); previewReady (selectedDirectoryId,entry);
// open the file // open the file
FCOIParams* params = new FCOIParams; FCOIParams* params = new FCOIParams;
params->catalog = this; params->catalog = this;
@@ -1511,7 +1509,9 @@ void FileCatalog::selectionChanged (std::vector<Thumbnail*> tbe) {
void FileCatalog::exifFilterChanged () { void FileCatalog::exifFilterChanged () {
currentEFS = filterPanel->getFilter (); // not sure that locking is necessary here...
Glib::Mutex::Lock lock(filterMutex);
currentEFS = filterPanel->getFilter ();
hasValidCurrentEFS = true; hasValidCurrentEFS = true;
fileBrowser->applyFilter (getFilter ()); fileBrowser->applyFilter (getFilter ());
_refreshProgressBar(); _refreshProgressBar();
@@ -1557,9 +1557,11 @@ bool FileCatalog::Query_key_pressed (GdkEventKey *event){
FileCatalog::buttonQueryClearPressed (); FileCatalog::buttonQueryClearPressed ();
return true; return true;
} }
break;
default: default:
return false; break;
} }
return false;
} }
void FileCatalog::updateFBQueryTB (bool singleRow) { void FileCatalog::updateFBQueryTB (bool singleRow) {
@@ -1625,9 +1627,11 @@ bool FileCatalog::BrowsePath_key_pressed (GdkEventKey *event){
BrowsePath->select_region(BrowsePath->get_text_length(), BrowsePath->get_text_length()); BrowsePath->select_region(BrowsePath->get_text_length(), BrowsePath->get_text_length());
return true; return true;
} }
break;
default: default:
return false; break;
} }
return false;
} }
void FileCatalog::tbLeftPanel_1_visible (bool visible){ void FileCatalog::tbLeftPanel_1_visible (bool visible){
@@ -1806,6 +1810,7 @@ bool FileCatalog::handleShortcutKey (GdkEventKey* event) {
FileCatalog::buttonBrowsePathPressed (); FileCatalog::buttonBrowsePathPressed ();
return true; return true;
} }
break;
} }
} }

View File

@@ -133,6 +133,7 @@ class FileCatalog : public Gtk::VBox,
Gtk::Button* zoomInButton; Gtk::Button* zoomInButton;
Gtk::Button* zoomOutButton; Gtk::Button* zoomOutButton;
Glib::Mutex filterMutex;
ExifFilterSettings dirEFS; ExifFilterSettings dirEFS;
ExifFilterSettings currentEFS; ExifFilterSettings currentEFS;
bool hasValidCurrentEFS; bool hasValidCurrentEFS;
@@ -172,21 +173,20 @@ class FileCatalog : public Gtk::VBox,
void dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile=""); void dirSelected (const Glib::ustring& dirname, const Glib::ustring& openfile="");
void closeDir (); void closeDir ();
void refreshEditedState (const std::set<Glib::ustring>& efiles); void refreshEditedState (const std::set<Glib::ustring>& efiles);
// previewloaderlistener interface // previewloaderlistener interface
void previewReadyUI (int dir_id, FileBrowserEntry* fdn); void previewReady (int dir_id, FileBrowserEntry* fdn);
void previewReady (int dir_id, FileBrowserEntry* fdn); void previewsFinished (int dir_id);
void previewsFinished (int dir_id);
void previewsFinishedUI (); void previewsFinishedUI ();
void _refreshProgressBar (); void _refreshProgressBar ();
// filterpanel interface // filterpanel interface
void exifFilterChanged (); void exifFilterChanged ();
// exportpanel interface // exportpanel interface
void exportRequested(); void exportRequested();
Glib::ustring lastSelectedDir () { return selectedDirectory; } Glib::ustring lastSelectedDir () { return selectedDirectory; }
void setEnabled (bool e); // if not enabled, it does not open image void setEnabled (bool e); // if not enabled, it does not open image
void enableTabMode(bool enable); // sets progress bar void enableTabMode(bool enable); // sets progress bar
@@ -210,18 +210,18 @@ class FileCatalog : public Gtk::VBox,
void setImageAreaToolListener (ImageAreaToolListener* l) { iatlistener = l; } void setImageAreaToolListener (ImageAreaToolListener* l) { iatlistener = l; }
void setDirBrowserRemoteInterface (DirBrowserRemoteInterface* l) { dirlistener = l; } void setDirBrowserRemoteInterface (DirBrowserRemoteInterface* l) { dirlistener = l; }
void setFilterPanel (FilterPanel* fpanel); void setFilterPanel (FilterPanel* fpanel);
void setExportPanel (ExportPanel* expanel); void setExportPanel (ExportPanel* expanel);
void exifInfoButtonToggled(); void exifInfoButtonToggled();
void categoryButtonToggled (Gtk::ToggleButton* b, bool isMouseClick); void categoryButtonToggled (Gtk::ToggleButton* b, bool isMouseClick);
bool capture_event(GdkEventButton* event); bool capture_event(GdkEventButton* event);
void filterChanged (); void filterChanged ();
void runFilterDialog (); void runFilterDialog ();
void on_realize(); void on_realize();
void reparseDirectory (); void reparseDirectory ();
void _openImage (std::vector<Thumbnail*> tmb); void _openImage (std::vector<Thumbnail*> tmb);
void zoomIn (); void zoomIn ();
void zoomOut (); void zoomOut ();

View File

@@ -87,6 +87,7 @@ FilePanel::FilePanel () : parent(NULL) {
fileCatalog->setFilterPanel (filterPanel); fileCatalog->setFilterPanel (filterPanel);
fileCatalog->setExportPanel (exportPanel); fileCatalog->setExportPanel (exportPanel);
fileCatalog->setImageAreaToolListener (tpc); fileCatalog->setImageAreaToolListener (tpc);
fileCatalog->fileBrowser->setBatchPParamsChangeListener (tpc);
//------------------ //------------------
@@ -134,6 +135,7 @@ void FilePanel::setAspect () {
void FilePanel::init () { void FilePanel::init () {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
dirBrowser->fillDirTree (); dirBrowser->fillDirTree ();
placesBrowser->refreshPlacesList (); placesBrowser->refreshPlacesList ();
@@ -170,40 +172,49 @@ bool FilePanel::fileSelected (Thumbnail* thm) {
ProgressConnector<rtengine::InitialImage*> *ld = new ProgressConnector<rtengine::InitialImage*>(); ProgressConnector<rtengine::InitialImage*> *ld = new ProgressConnector<rtengine::InitialImage*>();
ld->startFunc (sigc::bind(sigc::ptr_fun(&rtengine::InitialImage::load), thm->getFileName (), thm->getType()==FT_Raw, &error, parent->getProgressListener()), ld->startFunc (sigc::bind(sigc::ptr_fun(&rtengine::InitialImage::load), thm->getFileName (), thm->getType()==FT_Raw, &error, parent->getProgressListener()),
sigc::bind(sigc::mem_fun(*this,&FilePanel::imageLoaded), thm, ld) ); sigc::bind(sigc::mem_fun(*this,&FilePanel::imageLoaded), thm, ld) );
return true; return true;
} }
bool FilePanel::imageLoaded( Thumbnail* thm, ProgressConnector<rtengine::InitialImage*> *pc ){ bool FilePanel::imageLoaded( Thumbnail* thm, ProgressConnector<rtengine::InitialImage*> *pc ){
if (pc->returnValue() && thm) { if (pc->returnValue() && thm) {
if (options.tabbedUI) { if (options.tabbedUI) {
EditorPanel* epanel = Gtk::manage (new EditorPanel ()); EditorPanel* epanel;
{
GThreadLock lock; // Acquiring the GUI... not sure that it's necessary, but it shouldn't harm
epanel = Gtk::manage (new EditorPanel ());
parent->addEditorPanel (epanel,Glib::path_get_basename (thm->getFileName())); parent->addEditorPanel (epanel,Glib::path_get_basename (thm->getFileName()));
}
epanel->open(thm, pc->returnValue() ); epanel->open(thm, pc->returnValue() );
} else { } else {
{
GThreadLock lock; // Acquiring the GUI... not sure that it's necessary, but it shouldn't harm
parent->SetEditorCurrent(); parent->SetEditorCurrent();
parent->epanel->open(thm, pc->returnValue() ); }
parent->epanel->open(thm, pc->returnValue() );
} }
} else {
Glib::ustring msg_ = Glib::ustring("<b>") + M("MAIN_MSG_CANNOTLOAD") + " \"" + thm->getFileName() + "\" .\n</b>";
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true);
msgd.run ();
}
delete pc;
} else { {
Glib::ustring msg_ = Glib::ustring("<b>") + M("MAIN_MSG_CANNOTLOAD") + " \"" + thm->getFileName() + "\" .\n</b>"; GThreadLock lock; // Acquiring the GUI... not sure that it's necessary, but it shouldn't harm
Gtk::MessageDialog msgd (msg_, true, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); parent->setProgress(0.);
msgd.run (); parent->setProgressStr("");
} }
delete pc; thm->imageLoad( false );
parent->setProgress(0.);
parent->setProgressStr("");
thm->imageLoad( false );
return false; // MUST return false from idle function return false; // MUST return false from idle function
} }
void FilePanel::saveOptions () { void FilePanel::saveOptions () {
int winW, winH; int winW, winH;
parent->get_size(winW, winH); parent->get_size(winW, winH);
options.dirBrowserWidth = dirpaned->get_position (); options.dirBrowserWidth = dirpaned->get_position ();
options.dirBrowserHeight = placespaned->get_position (); options.dirBrowserHeight = placespaned->get_position ();
options.browserToolPanelWidth = winW - get_position(); options.browserToolPanelWidth = winW - get_position();
@@ -262,6 +273,7 @@ bool FilePanel::handleShortcutKey (GdkEventKey* event) {
void FilePanel::loadingThumbs(Glib::ustring str, double rate) void FilePanel::loadingThumbs(Glib::ustring str, double rate)
{ {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
if( !str.empty()) if( !str.empty())
parent->setProgressStr(str); parent->setProgressStr(str);
parent->setProgress( rate ); parent->setProgress( rate );

View File

@@ -28,6 +28,11 @@
using namespace std; using namespace std;
#if TRACE_MYRWMUTEX==1 && !defined NDEBUG
unsigned int MyReaderLock::readerLockCounter = 0;
unsigned int MyWriterLock::writerLockCounter = 0;
#endif
Glib::ustring escapeHtmlChars(const Glib::ustring &src) { Glib::ustring escapeHtmlChars(const Glib::ustring &src) {
// Sources chars to be escaped // Sources chars to be escaped

View File

@@ -20,7 +20,10 @@
#define __GUI_UTILS_ #define __GUI_UTILS_
#include <gtkmm.h> #include <gtkmm.h>
#include <glibmm.h>
#include "../rtengine/rtengine.h" #include "../rtengine/rtengine.h"
#include <sstream>
#include <iostream>
Glib::ustring escapeHtmlChars(const Glib::ustring &src); Glib::ustring escapeHtmlChars(const Glib::ustring &src);
bool removeIfThere (Gtk::Container* cont, Gtk::Widget* w, bool increference=true); bool removeIfThere (Gtk::Container* cont, Gtk::Widget* w, bool increference=true);
@@ -74,6 +77,455 @@ public:
} }
}; };
#ifdef NDEBUG
// We don't trace mutex
#undef TRACE_MYRWMUTEX
#define TRACE_MYRWMUTEX 0
#endif
// Uncomment this if you want to bypass the CMakeList options and force the values
// Of course, DO NOT COMMIT! :)
//#undef PROTECT_VECTORS
//#define PROTECT_VECTORS 1
//#undef TRACE_MYRWMUTEX
//#define TRACE_MYRWMUTEX 1
/**
* @brief Custom RWLock with debugging feature, to replace the buggy Glib::RWLock (can have negative reader_count value!)
*
* It may be slower, but thread safe!
*/
class MyRWMutex {
public:
Glib::Mutex handlerMutex;
Glib::Cond access;
size_t writerCount;
size_t readerCount;
#if TRACE_MYRWMUTEX
Glib::ustring lastWriterFile;
int lastWriterLine;
// Unfortunately, ownerThread may not be the culprit of a deadlock, it can be another concurrent Reader...
void* ownerThread;
MyRWMutex() : writerCount(0), readerCount(0), lastWriterLine(0), ownerThread(NULL) {}
#else
MyRWMutex() : writerCount(0), readerCount(0) {}
#endif
};
/**
* @brief Custom ReaderLock with debugging feature, to replace the buggy Glib::RWLock (can have negative reader_count value!)
*
*/
class MyReaderLock {
MyRWMutex& rwMutex;
bool locked;
#if TRACE_MYRWMUTEX
static unsigned int readerLockCounter;
int locknumber;
public:
inline MyReaderLock(MyRWMutex& mutex, const char* name, const char* file, const int line) : rwMutex(mutex), locked(false), locknumber(0)
#else
public:
inline MyReaderLock(MyRWMutex& mutex) : rwMutex(mutex)
#endif
{
// to operate safely
rwMutex.handlerMutex.lock();
#if TRACE_MYRWMUTEX
locknumber = readerLockCounter++;
void* thread = Glib::Thread::self();
std::cout << thread << "/" << locknumber << ":" << name << " / " << file << " : " << line << " - locking - R";
#endif
if (!rwMutex.writerCount) {
// There's no writer operating, we can increment the writer count which will lock writers
++rwMutex.writerCount;
#if TRACE_MYRWMUTEX
std::cout << " ++ new owner";
#endif
}
else {
// The writer count is non null, but we can be the owner of the writer lock
// It will be the case if the reader count is non null too.
if (!rwMutex.readerCount) {
// the mutex is in real write mode, we're waiting to see it null
#if TRACE_MYRWMUTEX
std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl;
#endif
while (rwMutex.writerCount)
rwMutex.access.wait(rwMutex.handlerMutex);
++rwMutex.writerCount;
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = file;
rwMutex.lastWriterLine = line;
rwMutex.ownerThread = thread;
std::cout << thread << "/" << locknumber << ":" << name << " / " << file << " : " << line << " - locking - R ++ new owner";
#endif
}
}
// then we can increment the reader count
++rwMutex.readerCount;
#if TRACE_MYRWMUTEX
std::cout << " - ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl;
#endif
rwMutex.handlerMutex.unlock();
locked = true;
}
#if TRACE_MYRWMUTEX
// locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely
inline void acquire(const char* file, const int line)
#else
// locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely
inline void acquire()
#endif
{
#if TRACE_MYRWMUTEX
void* thread = Glib::Thread::self();
#endif
if (!locked) {
// to operate safely
rwMutex.handlerMutex.lock();
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - R (lock)";
#endif
if (!rwMutex.writerCount) {
// There's no writer operating, we can increment the writer count which will lock writers
++rwMutex.writerCount;
#if TRACE_MYRWMUTEX
std::cout << " ++ new owner";
#endif
}
else {
// The writer count is non null, but a reader can be the owner of the writer lock,
// it will be the case if the reader count is non null too.
if (!rwMutex.readerCount) {
// the mutex is in real write mode, we're waiting to see it null
#if TRACE_MYRWMUTEX
std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl;
#endif
while (rwMutex.writerCount)
rwMutex.access.wait(rwMutex.handlerMutex);
++rwMutex.writerCount;
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = file;
rwMutex.lastWriterLine = line;
rwMutex.ownerThread = thread;
std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - R (lock) ++ new owner";
#endif
}
}
// then we can increment the reader count
++rwMutex.readerCount;
#if TRACE_MYRWMUTEX
std::cout << " - ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl;
#endif
rwMutex.handlerMutex.unlock();
locked = true;
}
#if TRACE_MYRWMUTEX
else std::cout << thread << "/" << locknumber << " / already locked by this object - R (lock)" << std::endl;
#endif
}
inline ~MyReaderLock() {
#if TRACE_MYRWMUTEX
void* thread = Glib::Thread::self();
#endif
if (locked) {
// to operate safely
rwMutex.handlerMutex.lock();
// decrement the writer number first
--rwMutex.readerCount;
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << " / unlocking - R - ReaderCount: " << rwMutex.readerCount;
#endif
if (!rwMutex.readerCount) {
// no more reader, so we decrement the writer count
--rwMutex.writerCount;
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = "";
rwMutex.lastWriterLine = 0;
rwMutex.ownerThread = NULL;
std::cout << " -- new owner possible!" << " >>> ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount;
#endif
// and signal the next waiting reader/writer that it's free
rwMutex.access.broadcast();
}
#if TRACE_MYRWMUTEX
std::cout << std::endl;
#endif
rwMutex.handlerMutex.unlock();
}
#if TRACE_MYRWMUTEX
else std::cout << thread << "/" << locknumber << " / already unlocked by this object - R" << std::endl;
#endif
}
#if TRACE_MYRWMUTEX
// releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely
inline void release(const char* file, const int line)
#else
// releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely
inline void release()
#endif
{
#if TRACE_MYRWMUTEX
void* thread = Glib::Thread::self();
#endif
if (locked) {
// to operate safely
rwMutex.handlerMutex.lock();
// decrement the writer number first
--rwMutex.readerCount;
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << " / unlocking - R (release) - ReaderCount: " << rwMutex.readerCount;
#endif
if (!rwMutex.readerCount) {
// no more reader, so we decrement the writer count
--rwMutex.writerCount;
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = "";
rwMutex.lastWriterLine = 0;
rwMutex.ownerThread = NULL;
std::cout << " -- new owner possible!" << " >>> ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount;
#endif
// and signal the next waiting reader/writer that it's free
rwMutex.access.broadcast();
}
#if TRACE_MYRWMUTEX
std::cout << std::endl;
#endif
rwMutex.handlerMutex.unlock();
locked = false;
}
#if TRACE_MYRWMUTEX
else std::cout << thread << "/" << locknumber << " / already unlocked - R (release)" << std::endl;
#endif
}
};
/**
* @brief Custom WriterLock with debugging feature, to replace the buggy Glib::RWLock (can have negative reader_count value!)
*
*/
class MyWriterLock {
MyRWMutex& rwMutex;
bool locked;
#if TRACE_MYRWMUTEX
static unsigned int writerLockCounter;
int locknumber;
public:
inline MyWriterLock(MyRWMutex& mutex, const char* name, const char* file, const int line) : rwMutex(mutex), locked(false), locknumber(0)
#else
public:
inline MyWriterLock(MyRWMutex& mutex) : rwMutex(mutex)
#endif
{
// to operate safely
rwMutex.handlerMutex.lock();
#if TRACE_MYRWMUTEX
locknumber = writerLockCounter++;
void* thread = Glib::Thread::self();
std::cout << thread << "/" << locknumber << ":" << name << " / " << file << " : " << line << " - locking - W";
#endif
if (rwMutex.writerCount) {
// The writer count is non null, so we have to wait for it to be null again
#if TRACE_MYRWMUTEX
std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl;
#endif
while (rwMutex.writerCount)
rwMutex.access.wait(rwMutex.handlerMutex);
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - W";
#endif
}
// then we can increment the writer count
++rwMutex.writerCount;
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = file;
rwMutex.lastWriterLine = line;
rwMutex.ownerThread = thread;
std::cout << " ++ new owner <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl;
#endif
rwMutex.handlerMutex.unlock();
locked = true;
}
#if TRACE_MYRWMUTEX
// locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely
inline void acquire(const char* file, const int line)
#else
// locks the MyRWMutex with Read access if this MyReaderLock has not already locked it, otherwise return safely
inline void acquire()
#endif
{
#if TRACE_MYRWMUTEX
void* thread = Glib::Thread::self();
#endif
if (!locked) {
// to operate safely
rwMutex.handlerMutex.lock();
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - W (lock)";
#endif
if (rwMutex.writerCount) {
// The writer count is non null, so we have to wait for it to be null again
#if TRACE_MYRWMUTEX
std::cout << " waiting..." << std::endl << "Current writer owner: " << rwMutex.lastWriterFile << " : " << rwMutex.lastWriterLine << std::endl;
#endif
while (rwMutex.writerCount)
rwMutex.access.wait(rwMutex.handlerMutex);
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << ":" << file << " : " << line << " - locking - W (lock)";
#endif
}
// then we can increment the reader count
++rwMutex.writerCount;
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = file;
rwMutex.lastWriterLine = line;
rwMutex.ownerThread = thread;
std::cout << " ++ new owner <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl;
#endif
rwMutex.handlerMutex.unlock();
locked = true;
}
#if TRACE_MYRWMUTEX
else std::cout << thread << "/" << locknumber << " / already locked by this object - W (lock)" << std::endl;
#endif
}
inline ~MyWriterLock() {
#if TRACE_MYRWMUTEX
void* thread = Glib::Thread::self();
#endif
if (locked) {
// to operate safely
rwMutex.handlerMutex.lock();
// decrement the writer number first
--rwMutex.writerCount;
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << " / unlocking - W";
#endif
if (!rwMutex.writerCount) {
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = "";
rwMutex.lastWriterLine = 0;
rwMutex.ownerThread = NULL;
std::cout << " -- new owner possible!";
#endif
// The writer count is null again, so we can wake up the next writer or reader
rwMutex.access.broadcast();
}
#if TRACE_MYRWMUTEX
std::cout << " <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl;
#endif
rwMutex.handlerMutex.unlock();
}
#if TRACE_MYRWMUTEX
else std::cout << thread << "/" << locknumber << " / already unlocked by this object - W" << std::endl;
#endif
}
#if TRACE_MYRWMUTEX
// releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely
inline void release(const char* file, const int line)
#else
// releases the MyRWMutex with Write access if this MyWriterLock has already locked it, otherwise return safely
inline void release()
#endif
{
#if TRACE_MYRWMUTEX
void* thread = Glib::Thread::self();
#endif
if (locked) {
// to operate safely
rwMutex.handlerMutex.lock();
// decrement the writer number first
--rwMutex.writerCount;
#if TRACE_MYRWMUTEX
std::cout << thread << "/" << locknumber << " / unlocking - W (release)";
#endif
if (!rwMutex.writerCount) {
#if TRACE_MYRWMUTEX
rwMutex.lastWriterFile = "";
rwMutex.lastWriterLine = 0;
rwMutex.ownerThread = NULL;
std::cout << " -- new owner possible!";
#endif
// The writer count is null again, so we can wake up the next writer or reader
rwMutex.access.broadcast();
}
#if TRACE_MYRWMUTEX
std::cout << " <<< ReaderCount: " << rwMutex.readerCount << " - WriterCount: " << rwMutex.writerCount << std::endl;
#endif
rwMutex.handlerMutex.unlock();
locked = false;
}
#if TRACE_MYRWMUTEX
else std::cout << thread << "/" << locknumber << " / already unlocked by this object - W (release)" << std::endl;
#endif
}
};
#if TRACE_MYRWMUTEX
#define MYREADERLOCK(ln, e) MyReaderLock ln(e, #e, __FILE__, __LINE__);
#define MYWRITERLOCK(ln, e) MyWriterLock ln(e, #e, __FILE__, __LINE__);
#define MYREADERLOCK_ACQUIRE(ln) ln.acquire(__FILE__, __LINE__);
#define MYWRITERLOCK_ACQUIRE(ln) ln.acquire(__FILE__, __LINE__);
#define MYREADERLOCK_RELEASE(ln) ln.release(__FILE__, __LINE__);
#define MYWRITERLOCK_RELEASE(ln) ln.release(__FILE__, __LINE__);
#else
#define MYREADERLOCK(ln, e) MyReaderLock ln(e);
#define MYWRITERLOCK(ln, e) MyWriterLock ln(e);
#define MYREADERLOCK_ACQUIRE(ln) ln.acquire();
#define MYWRITERLOCK_ACQUIRE(ln) ln.acquire();
#define MYREADERLOCK_RELEASE(ln) ln.release();
#define MYWRITERLOCK_RELEASE(ln) ln.release();
#endif
/** /**
* @brief subclass of Gtk::ScrolledWindow in order to handle the scrollwheel * @brief subclass of Gtk::ScrolledWindow in order to handle the scrollwheel
*/ */

View File

@@ -353,6 +353,9 @@ void HistogramRGBArea::renderRGBMarks (int r, int g, int b) {
return; return;
} }
// Mostly not necessary, but should be in some case
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
Glib::RefPtr<Gdk::Window> window = get_window(); Glib::RefPtr<Gdk::Window> window = get_window();
int winx, winy, winw, winh, wind; int winx, winy, winw, winh, wind;
window->get_geometry(winx, winy, winw, winh, wind); window->get_geometry(winx, winy, winw, winh, wind);
@@ -420,8 +423,6 @@ void HistogramRGBArea::renderRGBMarks (int r, int g, int b) {
int histrgbupdate (void* data) { int histrgbupdate (void* data) {
gdk_threads_enter ();
HistogramRGBAreaIdleHelper* harih = static_cast<HistogramRGBAreaIdleHelper*>(data); HistogramRGBAreaIdleHelper* harih = static_cast<HistogramRGBAreaIdleHelper*>(data);
if (harih->destroyed) { if (harih->destroyed) {
@@ -429,7 +430,6 @@ int histrgbupdate (void* data) {
delete harih; delete harih;
else else
harih->pending--; harih->pending--;
gdk_threads_leave ();
return 0; return 0;
} }
@@ -437,7 +437,6 @@ int histrgbupdate (void* data) {
harih->harea->queue_draw (); harih->harea->queue_draw ();
harih->pending--; harih->pending--;
gdk_threads_leave ();
return 0; return 0;
} }
@@ -453,7 +452,7 @@ void HistogramRGBArea::update (int valh, int rh, int gh, int bh) {
} }
else else
valid = false; valid = false;
harih->pending++; harih->pending++;
g_idle_add (histrgbupdate, harih); g_idle_add (histrgbupdate, harih);
} }
@@ -618,8 +617,9 @@ void HistogramArea::update (LUTu &histRed, LUTu &histGreen, LUTu &histBlue, LUTu
} }
else else
valid = false; valid = false;
haih->pending++; haih->pending++;
// Can be done outside of the GUI thread
g_idle_add (histupdateUI, haih); g_idle_add (histupdateUI, haih);
} }

View File

@@ -17,6 +17,7 @@
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>. * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "lwbutton.h" #include "lwbutton.h"
#include "guiutils.h"
LWButton::LWButton (Cairo::RefPtr<Cairo::ImageSurface> i, int aCode, void* aData, Alignment ha, Alignment va, Glib::ustring tooltip) LWButton::LWButton (Cairo::RefPtr<Cairo::ImageSurface> i, int aCode, void* aData, Alignment ha, Alignment va, Glib::ustring tooltip)
: halign(ha), valign(va), icon(i), state(Normal), listener(NULL), actionCode(aCode), actionData(aData), toolTip(tooltip) { : halign(ha), valign(va), icon(i), state(Normal), listener(NULL), actionCode(aCode), actionData(aData), toolTip(tooltip) {
@@ -145,6 +146,7 @@ bool LWButton::releaseNotify (int x, int y) {
void LWButton::redraw (Cairo::RefPtr<Cairo::Context> context) { void LWButton::redraw (Cairo::RefPtr<Cairo::Context> context) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
context->set_line_width (1.0); context->set_line_width (1.0);
context->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); context->set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
context->rectangle (xpos+0.5, ypos+0.5, w-1.0, h-1.0); context->rectangle (xpos+0.5, ypos+0.5, w-1.0, h-1.0);
@@ -162,10 +164,10 @@ void LWButton::redraw (Cairo::RefPtr<Cairo::Context> context) {
if (state==Pressed_In) if (state==Pressed_In)
dilat++; dilat++;
if (icon) { if (icon) {
context->set_source (icon, xpos+dilat, ypos+dilat); context->set_source (icon, xpos+dilat, ypos+dilat);
context->paint (); context->paint ();
} }
} }
void LWButton::getAlignment (Alignment& ha, Alignment& va) { void LWButton::getAlignment (Alignment& ha, Alignment& va) {

View File

@@ -25,6 +25,7 @@ class LWButton;
class LWButtonListener { class LWButtonListener {
public: public:
virtual ~LWButtonListener () {}
virtual void buttonPressed (LWButton* button, int actionCode, void* actionData) {} virtual void buttonPressed (LWButton* button, int actionCode, void* actionData) {}
virtual void redrawNeeded (LWButton* button) {} virtual void redrawNeeded (LWButton* button) {}
}; };

View File

@@ -85,7 +85,7 @@ void LWButtonSet::arrangeButtons (int x, int y, int w, int h) {
void LWButtonSet::move (int nx, int ny) { void LWButtonSet::move (int nx, int ny) {
for (size_t i=0; i<buttons.size(); i++) { for (size_t i=0; i<buttons.size(); i++) {
int x, y; int x, y;
buttons[i]->getPosition (x, y); buttons[i]->getPosition (x, y);
buttons[i]->setPosition (x+nx-ax, y+ny-ay); buttons[i]->setPosition (x+nx-ax, y+ny-ay);
@@ -97,7 +97,7 @@ void LWButtonSet::move (int nx, int ny) {
void LWButtonSet::redraw (Cairo::RefPtr<Cairo::Context> context) { void LWButtonSet::redraw (Cairo::RefPtr<Cairo::Context> context) {
for (size_t i=0; i<buttons.size(); i++) for (size_t i=0; i<buttons.size(); i++)
buttons[i]->redraw (context); buttons[i]->redraw (context);
} }

View File

@@ -24,6 +24,7 @@
// This file is for your program, I won't touch it again! // This file is for your program, I won't touch it again!
#include "config.h" #include "config.h"
#include <glibmm/thread.h>
#include <gtkmm.h> #include <gtkmm.h>
#include <giomm.h> #include <giomm.h>
#include <iostream> #include <iostream>
@@ -53,6 +54,22 @@ Glib::ustring creditsPath;
Glib::ustring licensePath; Glib::ustring licensePath;
Glib::ustring argv1; Glib::ustring argv1;
bool simpleEditor; bool simpleEditor;
Glib::Thread* mainThread;
// This recursive mutex will be used by g_thread_enter/leave instead of a simple mutex
static Glib::RecMutex myGdkRecMutex;
static void myGdkLockEnter() { myGdkRecMutex.lock(); }
static void myGdkLockLeave() {
// Automatic gdk_flush for non main tread
#if AUTO_GDK_FLUSH
if (Glib::Thread::self() != mainThread) {
gdk_flush();
}
#endif
myGdkRecMutex.unlock();
}
/* Process line command options /* Process line command options
* Returns * Returns
@@ -105,9 +122,12 @@ int main(int argc, char **argv)
#endif #endif
Glib::thread_init(); Glib::thread_init();
gdk_threads_set_lock_functions(G_CALLBACK(myGdkLockEnter), ((GCallback) (myGdkLockLeave)));
gdk_threads_init(); gdk_threads_init();
Gio::init (); Gio::init ();
mainThread = Glib::Thread::self();
Options::load (); Options::load ();
extProgStore->init(); extProgStore->init();
SoundManager::init(); SoundManager::init();
@@ -158,9 +178,8 @@ int main(int argc, char **argv)
printf("Error: no default settings to update!\n"); printf("Error: no default settings to update!\n");
#endif #endif
RTWindow *rtWindow = new class RTWindow();
gdk_threads_enter (); gdk_threads_enter ();
RTWindow *rtWindow = new class RTWindow();
// alerting users if the default raw and image profiles are missing // alerting users if the default raw and image profiles are missing
if (options.is_defProfRawMissing()) { if (options.is_defProfRawMissing()) {

View File

@@ -806,6 +806,7 @@ void MyDiagonalCurve::updateBackgroundHistogram (LUTu & hist) {
bghistvalid = false; bghistvalid = false;
mcih->pending++; mcih->pending++;
// Can be done outside of the GUI thread
g_idle_add (diagonalmchistupdateUI, mcih); g_idle_add (diagonalmchistupdateUI, mcih);
} }

View File

@@ -250,6 +250,8 @@ void Options::setDefaults () {
version = "0.0.0.0"; // temporary value; will be correctly set in RTWindow::on_realize version = "0.0.0.0"; // temporary value; will be correctly set in RTWindow::on_realize
thumbSize = 240; thumbSize = 240;
thumbSizeTab = 80; thumbSizeTab = 80;
thumbSizeQueue = 100;
sameThumbSize = true; // preferring speed of switch between file browser and single editor tab
showHistory = true; showHistory = true;
showFilePanelState = 0; // Not used anymore ; was the thumb strip state showFilePanelState = 0; // Not used anymore ; was the thumb strip state
showInfo = true; showInfo = true;
@@ -588,6 +590,8 @@ if (keyFile.has_group ("Profiles")) {
if (keyFile.has_group ("File Browser")) { if (keyFile.has_group ("File Browser")) {
if (keyFile.has_key ("File Browser", "ThumbnailSize")) thumbSize = keyFile.get_integer ("File Browser", "ThumbnailSize"); if (keyFile.has_key ("File Browser", "ThumbnailSize")) thumbSize = keyFile.get_integer ("File Browser", "ThumbnailSize");
if (keyFile.has_key ("File Browser", "ThumbnailSizeTab")) thumbSizeTab = keyFile.get_integer ("File Browser", "ThumbnailSizeTab"); if (keyFile.has_key ("File Browser", "ThumbnailSizeTab")) thumbSizeTab = keyFile.get_integer ("File Browser", "ThumbnailSizeTab");
if (keyFile.has_key ("File Browser", "ThumbnailSizeQueue")) thumbSizeQueue = keyFile.get_integer ("File Browser", "ThumbnailSizeQueue");
if (keyFile.has_key ("File Browser", "SameThumbSize")) sameThumbSize = keyFile.get_integer ("File Browser", "SameThumbSize");
if (keyFile.has_key ("File Browser", "BrowseOnlyRaw")) fbOnlyRaw = keyFile.get_boolean ("File Browser", "BrowseOnlyRaw"); if (keyFile.has_key ("File Browser", "BrowseOnlyRaw")) fbOnlyRaw = keyFile.get_boolean ("File Browser", "BrowseOnlyRaw");
if (keyFile.has_key ("File Browser", "BrowserShowsDate")) fbShowDateTime = keyFile.get_boolean ("File Browser", "BrowserShowsDate"); if (keyFile.has_key ("File Browser", "BrowserShowsDate")) fbShowDateTime = keyFile.get_boolean ("File Browser", "BrowserShowsDate");
if (keyFile.has_key ("File Browser", "BrowserShowsExif")) fbShowBasicExif = keyFile.get_boolean ("File Browser", "BrowserShowsExif"); if (keyFile.has_key ("File Browser", "BrowserShowsExif")) fbShowBasicExif = keyFile.get_boolean ("File Browser", "BrowserShowsExif");
@@ -816,6 +820,8 @@ int Options::saveToFile (Glib::ustring fname) {
keyFile.set_boolean ("File Browser", "BrowserShowsHidden", fbShowHidden); keyFile.set_boolean ("File Browser", "BrowserShowsHidden", fbShowHidden);
keyFile.set_integer ("File Browser", "ThumbnailSize", thumbSize); keyFile.set_integer ("File Browser", "ThumbnailSize", thumbSize);
keyFile.set_integer ("File Browser", "ThumbnailSizeTab", thumbSizeTab); keyFile.set_integer ("File Browser", "ThumbnailSizeTab", thumbSizeTab);
keyFile.set_integer ("File Browser", "ThumbnailSizeQueue", thumbSizeQueue);
keyFile.set_integer ("File Browser", "SameThumbSize", sameThumbSize);
keyFile.set_integer ("File Browser", "MaxPreviewHeight", maxThumbnailHeight); keyFile.set_integer ("File Browser", "MaxPreviewHeight", maxThumbnailHeight);
keyFile.set_integer ("File Browser", "MaxCacheEntries", maxCacheEntries); keyFile.set_integer ("File Browser", "MaxCacheEntries", maxCacheEntries);
Glib::ArrayHandle<Glib::ustring> pext = parseExtensions; Glib::ArrayHandle<Glib::ustring> pext = parseExtensions;

View File

@@ -122,7 +122,8 @@ class Options {
bool multiUser; bool multiUser;
static Glib::ustring rtdir; static Glib::ustring rtdir;
Glib::ustring version; Glib::ustring version;
int thumbSize,thumbSizeTab; int thumbSize,thumbSizeTab, thumbSizeQueue;
bool sameThumbSize; // Will use only one thumb size for the file browser and the single editor tab, and avoid recomputing them
bool showHistory; bool showHistory;
int showFilePanelState; // 0: normal, 1: maximized, 2: normal, 3: hidden int showFilePanelState; // 0: normal, 1: maximized, 2: normal, 3: hidden
bool showInfo; bool showInfo;

View File

@@ -26,9 +26,18 @@
class PParamsChangeListener { class PParamsChangeListener {
public: public:
virtual ~PParamsChangeListener() {}
virtual void procParamsChanged (rtengine::procparams::ProcParams* params, rtengine::ProcEvent ev, Glib::ustring descr, ParamsEdited* paramsEdited=NULL) {} virtual void procParamsChanged (rtengine::procparams::ProcParams* params, rtengine::ProcEvent ev, Glib::ustring descr, ParamsEdited* paramsEdited=NULL) {}
virtual void clearParamChanges () {} virtual void clearParamChanges () {}
}; };
class BatchPParamsChangeListener {
public:
virtual ~BatchPParamsChangeListener() {}
virtual void beginBatchPParamsChange(int numberOfEntries) {}
virtual void endBatchPParamsChange() {}
};
#endif #endif

View File

@@ -23,8 +23,8 @@
using namespace rtengine; using namespace rtengine;
using namespace rtengine::procparams; using namespace rtengine::procparams;
PreviewHandler::PreviewHandler () : image(NULL) { PreviewHandler::PreviewHandler () : image(NULL), previewScale(1.) {
pih = new PreviewHandlerIdleHelper; pih = new PreviewHandlerIdleHelper;
pih->phandler = this; pih->phandler = this;
pih->destroyed = false; pih->destroyed = false;

View File

@@ -26,6 +26,7 @@
class PreviewListener { class PreviewListener {
public: public:
virtual ~PreviewListener () {}
virtual void previewImageChanged () {} virtual void previewImageChanged () {}
}; };
@@ -43,7 +44,7 @@ class PreviewHandler : public rtengine::PreviewImageListener {
friend int imageReadyUI (void* data); friend int imageReadyUI (void* data);
protected: protected:
rtengine::IImage8* image; rtengine::IImage8* image;
rtengine::procparams::CropParams cropParams; rtengine::procparams::CropParams cropParams;
double previewScale; double previewScale;
PreviewHandlerIdleHelper* pih; PreviewHandlerIdleHelper* pih;

View File

@@ -25,9 +25,19 @@
class ProfileChangeListener { class ProfileChangeListener {
public: public:
virtual ~ProfileChangeListener() {}
virtual void profileChange (const rtengine::procparams::PartialProfile* nparams, rtengine::ProcEvent event, const Glib::ustring& descr, const ParamsEdited* paramsEdited=NULL) {} virtual void profileChange (const rtengine::procparams::PartialProfile* nparams, rtengine::ProcEvent event, const Glib::ustring& descr, const ParamsEdited* paramsEdited=NULL) {}
virtual void setDefaults (rtengine::procparams::ProcParams* defparams) {} virtual void setDefaults (rtengine::procparams::ProcParams* defparams) {}
}; };
class BatchProfileChangeListener {
public:
virtual ~BatchProfileChangeListener() {}
virtual void beginBatchProfileChange(int numberOfEntries) {}
virtual void endBatchProfileChange() {}
};
#endif #endif

View File

@@ -58,19 +58,25 @@ ThumbBrowserBase::ThumbBrowserBase ()
} }
void ThumbBrowserBase::scrollChanged () { void ThumbBrowserBase::scrollChanged () {
for (size_t i=0; i<fd.size(); i++) {
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
for (size_t i=0; i<fd.size(); i++)
fd[i]->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value())); fd[i]->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
}
internal.setPosition ((int)(hscroll.get_value()), (int)(vscroll.get_value())); internal.setPosition ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
if (!internal.isDirty()) { if (!internal.isDirty()) {
internal.setDirty (); internal.setDirty ();
internal.queue_draw (); internal.queue_draw ();
// gdk_window_process_updates (get_window()->gobj(), true);
} }
} }
void ThumbBrowserBase::scroll (int direction) { void ThumbBrowserBase::scroll (int direction) {
// GUI already acquired when here
if (arrangement==TB_Vertical) if (arrangement==TB_Vertical)
vscroll.set_value (vscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * vscroll.get_adjustment()->get_step_increment()); vscroll.set_value (vscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * vscroll.get_adjustment()->get_step_increment());
else else
@@ -78,6 +84,7 @@ void ThumbBrowserBase::scroll (int direction) {
} }
void ThumbBrowserBase::scrollPage (int direction) { void ThumbBrowserBase::scrollPage (int direction) {
// GUI already acquired when here
if (arrangement==TB_Vertical) if (arrangement==TB_Vertical)
vscroll.set_value (vscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * vscroll.get_adjustment()->get_page_increment()); vscroll.set_value (vscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * vscroll.get_adjustment()->get_page_increment());
else else
@@ -107,6 +114,9 @@ void ThumbBrowserBase::internalAreaResized (Gtk::Allocation& req) {
void ThumbBrowserBase::configScrollBars () { void ThumbBrowserBase::configScrollBars () {
// HOMBRE:DELETE ME?
GThreadLock tLock; // Acquire the GUI
if (inW>0 && inH>0) { if (inW>0 && inH>0) {
int iw = internal.get_width (); int iw = internal.get_width ();
@@ -136,10 +146,14 @@ void ThumbBrowserBase::configScrollBars () {
} }
void ThumbBrowserBase::arrangeFiles () { void ThumbBrowserBase::arrangeFiles () {
// TODO: Check for Linux
#ifdef WIN32 #if PROTECT_VECTORS
Glib::RWLock::ReaderLock l(entryRW); MYREADERLOCK(l, entryRW);
#endif #endif
// GUI already locked by ::redraw, the only caller of this method for now.
// We could lock it one more time, there's no harm excepted (negligible) speed penalty
//GThreadLock lock;
int N = fd.size (); int N = fd.size ();
// apply filter // apply filter
@@ -152,7 +166,7 @@ void ThumbBrowserBase::arrangeFiles () {
if (!fd[i]->filtered && fd[i]->getMinimalHeight() > rowHeight) if (!fd[i]->filtered && fd[i]->getMinimalHeight() > rowHeight)
rowHeight = fd[i]->getMinimalHeight (); rowHeight = fd[i]->getMinimalHeight ();
if (arrangement==TB_Horizontal) { if (arrangement==TB_Horizontal) {
int numOfRows = 1; int numOfRows = 1;
// if (rowHeight>0) { // if (rowHeight>0) {
@@ -183,6 +197,10 @@ void ThumbBrowserBase::arrangeFiles () {
} }
currx += maxw; currx += maxw;
} }
#if PROTECT_VECTORS
MYREADERLOCK_RELEASE(l);
#endif
// This will require a Writer access
resizeThumbnailArea (currx, numOfRows*rowHeight); resizeThumbnailArea (currx, numOfRows*rowHeight);
} }
else { else {
@@ -234,13 +252,17 @@ void ThumbBrowserBase::arrangeFiles () {
if (currx>0) // there were thumbnails placed in the row if (currx>0) // there were thumbnails placed in the row
curry += rowHeight; curry += rowHeight;
} }
#if PROTECT_VECTORS
MYREADERLOCK_RELEASE(l);
#endif
// This will require a Writer access
resizeThumbnailArea (colsWidth, curry); resizeThumbnailArea (colsWidth, curry);
} }
} }
void ThumbBrowserBase::Internal::on_realize() void ThumbBrowserBase::Internal::on_realize() {
{ // Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
Cairo::FontOptions cfo; Cairo::FontOptions cfo;
cfo.set_antialias (Cairo::ANTIALIAS_SUBPIXEL); cfo.set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
get_pango_context()->set_cairo_font_options (cfo); get_pango_context()->set_cairo_font_options (cfo);
@@ -255,12 +277,20 @@ void ThumbBrowserBase::Internal::on_realize()
} }
bool ThumbBrowserBase::Internal::on_query_tooltip (int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip) { bool ThumbBrowserBase::Internal::on_query_tooltip (int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip) {
// Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
Glib::ustring ttip = ""; Glib::ustring ttip = "";
{
#if PROTECT_VECTORS
MYREADERLOCK(l, parent->entryRW);
#endif
for (size_t i=0; i<parent->fd.size(); i++) for (size_t i=0; i<parent->fd.size(); i++)
if (parent->fd[i]->drawable && parent->fd[i]->inside (x, y)) { if (parent->fd[i]->drawable && parent->fd[i]->inside (x, y)) {
ttip = parent->fd[i]->getToolTip (x, y); ttip = parent->fd[i]->getToolTip (x, y);
break; break;
} }
}
if (ttip!="") { if (ttip!="") {
tooltip->set_text (ttip); tooltip->set_text (ttip);
return true; return true;
@@ -270,8 +300,8 @@ bool ThumbBrowserBase::Internal::on_query_tooltip (int x, int y, bool keyboard_t
} }
void ThumbBrowserBase::styleChanged (const Glib::RefPtr<Gtk::Style>& style) { void ThumbBrowserBase::styleChanged (const Glib::RefPtr<Gtk::Style>& style) {
// GUI will be acquired by refreshThumbImages
refreshThumbImages (); refreshThumbImages ();
} }
ThumbBrowserBase::Internal::Internal () : ofsX(0), ofsY(0), parent(NULL), dirty(true) { ThumbBrowserBase::Internal::Internal () : ofsX(0), ofsY(0), parent(NULL), dirty(true) {
@@ -287,10 +317,12 @@ void ThumbBrowserBase::Internal::setPosition (int x, int y) {
} }
bool ThumbBrowserBase::Internal::on_key_press_event (GdkEventKey* event) { bool ThumbBrowserBase::Internal::on_key_press_event (GdkEventKey* event) {
// Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
return parent->keyPressed (event); return parent->keyPressed (event);
} }
bool ThumbBrowserBase::Internal::on_button_press_event (GdkEventButton* event) { bool ThumbBrowserBase::Internal::on_button_press_event (GdkEventButton* event) {
// Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
grab_focus (); grab_focus ();
parent->eventTime = event->time; parent->eventTime = event->time;
@@ -310,30 +342,43 @@ bool ThumbBrowserBase::Internal::on_button_press_event (GdkEventButton* event) {
} }
void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType type, int state, int clx, int cly, int clw, int clh) { void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType type, int state, int clx, int cly, int clw, int clh) {
// GUI already acquired
ThumbBrowserEntryBase* fileDescr = NULL; ThumbBrowserEntryBase* fileDescr = NULL;
bool handled = false; bool handled = false;
{ {
for (size_t i=0; i<fd.size(); i++) #if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
for (size_t i=0; i<fd.size(); i++)
if (fd[i]->drawable) { if (fd[i]->drawable) {
if (fd[i]->inside (x, y) && fd[i]->insideWindow (clx, cly, clw, clh)) if (fd[i]->inside (x, y) && fd[i]->insideWindow (clx, cly, clw, clh))
fileDescr = fd[i]; fileDescr = fd[i];
bool b = fd[i]->pressNotify (button, type, state, x, y); bool b = fd[i]->pressNotify (button, type, state, x, y);
handled = handled || b; handled = handled || b;
} }
} }
if (handled || (fileDescr && fileDescr->processing)) if (handled || (fileDescr && fileDescr->processing))
return; return;
{
#if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
if (selected.size()==1 && type==GDK_2BUTTON_PRESS && button==1) if (selected.size()==1 && type==GDK_2BUTTON_PRESS && button==1)
doubleClicked (selected[0]); doubleClicked (selected[0]);
else if (button==1 && type==GDK_BUTTON_PRESS) { else if (button==1 && type==GDK_BUTTON_PRESS) {
if (fileDescr && state & GDK_SHIFT_MASK) { if (fileDescr && (state & GDK_SHIFT_MASK)) {
if (selected.empty()) { if (selected.empty()) {
selected.push_back (fileDescr); selected.push_back (fileDescr);
fileDescr->selected = true; fileDescr->selected = true;
lastClicked = fileDescr; lastClicked = fileDescr;
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
selectionChanged (); selectionChanged ();
} }
else { else {
@@ -363,16 +408,19 @@ void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType typ
selected[i]->selected = false; selected[i]->selected = false;
selected.clear (); selected.clear ();
// select thumbnails in the interval // select thumbnails in the interval
for (size_t i=startx; i<=endx; i++) { for (size_t i=startx; i<=endx; i++) {
if (!fd[i]->filtered) { if (!fd[i]->filtered) {
fd[i]->selected = true; fd[i]->selected = true;
selected.push_back (fd[i]); selected.push_back (fd[i]);
} }
} }
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
selectionChanged (); selectionChanged ();
} }
} }
else if (fileDescr && state & GDK_CONTROL_MASK) { else if (fileDescr && (state & GDK_CONTROL_MASK)) {
std::vector<ThumbBrowserEntryBase*>::iterator i = std::find (selected.begin(), selected.end(), fileDescr); std::vector<ThumbBrowserEntryBase*>::iterator i = std::find (selected.begin(), selected.end(), fileDescr);
if (i!=selected.end()) { if (i!=selected.end()) {
(*i)->selected = false; (*i)->selected = false;
@@ -383,10 +431,13 @@ void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType typ
fileDescr->selected = true; fileDescr->selected = true;
} }
lastClicked = fileDescr; lastClicked = fileDescr;
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
selectionChanged (); selectionChanged ();
} }
else { else {
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
selected[i]->selected = false; selected[i]->selected = false;
selected.clear (); selected.clear ();
if (fileDescr) { if (fileDescr) {
@@ -394,28 +445,36 @@ void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType typ
fileDescr->selected = true; fileDescr->selected = true;
} }
lastClicked = fileDescr; lastClicked = fileDescr;
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
selectionChanged (); selectionChanged ();
} }
} }
else if (fileDescr && button==3 && type==GDK_BUTTON_PRESS) { else if (fileDescr && button==3 && type==GDK_BUTTON_PRESS) {
if (!fileDescr->selected) { if (!fileDescr->selected) {
for (size_t i=0; i<selected.size(); i++) for (size_t i=0; i<selected.size(); i++)
selected[i]->selected = false; selected[i]->selected = false;
selected.clear (); selected.clear ();
fileDescr->selected = true; fileDescr->selected = true;
selected.push_back (fileDescr); selected.push_back (fileDescr);
lastClicked = fileDescr; lastClicked = fileDescr;
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
selectionChanged (); selectionChanged ();
} }
#if PROTECT_VECTORS
MYWRITERLOCK_RELEASE(l);
#endif
rightClicked (fileDescr); rightClicked (fileDescr);
} }
} // end of MYWRITERLOCK(l, entryRW);
} }
bool ThumbBrowserBase::Internal::on_expose_event(GdkEventExpose* event) { bool ThumbBrowserBase::Internal::on_expose_event(GdkEventExpose* event) {
// TODO: Check for Linux // Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
#ifdef WIN32
Glib::RWLock::ReaderLock l(parent->entryRW);
#endif
dirty = false; dirty = false;
@@ -428,6 +487,12 @@ bool ThumbBrowserBase::Internal::on_expose_event(GdkEventExpose* event) {
// draw thumbnails // draw thumbnails
Glib::RefPtr<Pango::Context> context = get_pango_context (); Glib::RefPtr<Pango::Context> context = get_pango_context ();
context->set_font_description (get_style()->get_font()); context->set_font_description (get_style()->get_font());
{
#if PROTECT_VECTORS
MYWRITERLOCK(l, parent->entryRW);
#endif
for (size_t i=0; i<parent->fd.size() && !dirty; i++) { // if dirty meanwhile, cancel and wait for next redraw for (size_t i=0; i<parent->fd.size() && !dirty; i++) { // if dirty meanwhile, cancel and wait for next redraw
if (!parent->fd[i]->drawable || !parent->fd[i]->insideWindow (0, 0, w, h)) if (!parent->fd[i]->drawable || !parent->fd[i]->insideWindow (0, 0, w, h))
parent->fd[i]->updatepriority = false; parent->fd[i]->updatepriority = false;
@@ -436,36 +501,56 @@ bool ThumbBrowserBase::Internal::on_expose_event(GdkEventExpose* event) {
parent->fd[i]->draw (); parent->fd[i]->draw ();
} }
} }
}
return true; return true;
} }
bool ThumbBrowserBase::Internal::on_button_release_event (GdkEventButton* event) { bool ThumbBrowserBase::Internal::on_button_release_event (GdkEventButton* event) {
// Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
int w = get_width(); int w = get_width();
int h = get_height(); int h = get_height();
#if PROTECT_VECTORS
MYREADERLOCK(l, parent->entryRW);
#endif
for (size_t i=0; i<parent->fd.size(); i++) for (size_t i=0; i<parent->fd.size(); i++)
if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h)) { if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h)) {
parent->fd[i]->releaseNotify (event->button, event->type, event->state, (int)event->x, (int)event->y); ThumbBrowserEntryBase* tbe = parent->fd[i];
#if PROTECT_VECTORS
MYREADERLOCK_RELEASE(l);
#endif
// This will require a Writer access...
tbe->releaseNotify (event->button, event->type, event->state, (int)event->x, (int)event->y);
#if PROTECT_VECTORS
MYREADERLOCK_ACQUIRE(l);
#endif
} }
return true; return true;
} }
bool ThumbBrowserBase::Internal::on_motion_notify_event (GdkEventMotion* event) { bool ThumbBrowserBase::Internal::on_motion_notify_event (GdkEventMotion* event) {
// Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
int w = get_width(); int w = get_width();
int h = get_height(); int h = get_height();
#if PROTECT_VECTORS
MYREADERLOCK(l, parent->entryRW);
#endif
for (size_t i=0; i<parent->fd.size(); i++) for (size_t i=0; i<parent->fd.size(); i++)
if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h)) { if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h)) {
#ifdef WIN32 /*#if PROTECT_VECTORS
//l.release(); // motionNotify calls the queue, which locks MYREADERLOCK_RELEASE(l); // motionNotify calls the queue, which locks
#endif #endif*/
parent->fd[i]->motionNotify ((int)event->x, (int)event->y); parent->fd[i]->motionNotify ((int)event->x, (int)event->y);
} }
return true; return true;
} }
bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event) { bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event) {
// Gtk signals automatically acquire the GUI (i.e. this method is enclosed by gdk_thread_enter and gdk_thread_leave)
parent->scroll (event->direction); parent->scroll (event->direction);
return true; return true;
@@ -474,6 +559,7 @@ bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event) {
void ThumbBrowserBase::redraw () { void ThumbBrowserBase::redraw () {
GThreadLock lock;
arrangeFiles (); arrangeFiles ();
queue_draw (); queue_draw ();
} }
@@ -481,30 +567,31 @@ void ThumbBrowserBase::redraw () {
void ThumbBrowserBase::zoomChanged (bool zoomIn) { void ThumbBrowserBase::zoomChanged (bool zoomIn) {
int newHeight=0; int newHeight=0;
int optThumbSize=getCurrentThumbSize(); int optThumbSize=getThumbnailHeight();
if (zoomIn) if (zoomIn)
for (size_t i=0; i<options.thumbnailZoomRatios.size(); i++) { for (size_t i=0; i<options.thumbnailZoomRatios.size(); i++) {
newHeight = (int)(options.thumbnailZoomRatios[i] * getMaxThumbnailHeight()); newHeight = (int)(options.thumbnailZoomRatios[i] * getMaxThumbnailHeight());
if (newHeight > optThumbSize) if (newHeight > optThumbSize)
break; break;
} }
else else
for (size_t i=options.thumbnailZoomRatios.size()-1; i>0; i--) { for (size_t i=options.thumbnailZoomRatios.size()-1; i>0; i--) {
newHeight = (int)(options.thumbnailZoomRatios[i] * getMaxThumbnailHeight()); newHeight = (int)(options.thumbnailZoomRatios[i] * getMaxThumbnailHeight());
if (newHeight < optThumbSize) if (newHeight < optThumbSize)
break; break;
} }
previewHeight = newHeight; previewHeight = newHeight;
if (inTabMode) options.thumbSizeTab = newHeight; else options.thumbSize = newHeight;
{ saveThumbnailHeight(newHeight);
// TODO: Check for Linux
#ifdef WIN32
Glib::RWLock::WriterLock l(entryRW);
#endif
for (size_t i=0; i<fd.size(); i++) fd[i]->resize (previewHeight); {
} #if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
for (size_t i=0; i<fd.size(); i++)
fd[i]->resize (previewHeight);
}
redraw (); redraw ();
#ifdef WIN32 #ifdef WIN32
@@ -512,44 +599,46 @@ void ThumbBrowserBase::zoomChanged (bool zoomIn) {
#endif #endif
} }
int ThumbBrowserBase::getCurrentThumbSize() { return inTabMode ? options.thumbSizeTab : options.thumbSize; }
void ThumbBrowserBase::refreshThumbImages () { void ThumbBrowserBase::refreshThumbImages () {
{
// TODO: Check for Linux
#ifdef WIN32
Glib::RWLock::WriterLock l(entryRW);
#endif
int previewHeight = getCurrentThumbSize(); int previewHeight = getThumbnailHeight();
for (size_t i=0; i<fd.size(); i++) fd[i]->resize (previewHeight); {
} #if PROTECT_VECTORS
MYWRITERLOCK(l, entryRW);
#endif
for (size_t i=0; i<fd.size(); i++) fd[i]->resize (previewHeight);
}
redraw (); redraw ();
} }
void ThumbBrowserBase::refreshQuickThumbImages () { void ThumbBrowserBase::refreshQuickThumbImages () {
// TODO: Check for Linux #if PROTECT_VECTORS
#ifdef WIN32 MYWRITERLOCK(l, entryRW);
Glib::RWLock::WriterLock l(entryRW); #endif
#endif
for (size_t i=0; i<fd.size(); ++i) fd[i]->refreshQuickThumbnailImage (); for (size_t i=0; i<fd.size(); ++i) fd[i]->refreshQuickThumbnailImage ();
} }
void ThumbBrowserBase::refreshEditedState (const std::set<Glib::ustring>& efiles) { void ThumbBrowserBase::refreshEditedState (const std::set<Glib::ustring>& efiles) {
editedFiles = efiles; editedFiles = efiles;
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
for (size_t i=0; i<fd.size(); i++) for (size_t i=0; i<fd.size(); i++)
fd[i]->framed = editedFiles.find (fd[i]->filename)!=editedFiles.end(); fd[i]->framed = editedFiles.find (fd[i]->filename)!=editedFiles.end();
}
queue_draw (); queue_draw ();
} }
void ThumbBrowserBase::setArrangement (Arrangement a) { void ThumbBrowserBase::setArrangement (Arrangement a) {
arrangement = a; arrangement = a;
redraw (); redraw ();
} }
@@ -557,31 +646,40 @@ void ThumbBrowserBase::enableTabMode(bool enable) {
inTabMode = enable; inTabMode = enable;
arrangement = inTabMode ? ThumbBrowserBase::TB_Horizontal : ThumbBrowserBase::TB_Vertical; arrangement = inTabMode ? ThumbBrowserBase::TB_Horizontal : ThumbBrowserBase::TB_Vertical;
if (options.thumbSizeTab!=options.thumbSize) { if (!options.sameThumbSize && (options.thumbSizeTab!=options.thumbSize)) {
// TODO: Check for Linux #if PROTECT_VECTORS
#ifdef WIN32 MYWRITERLOCK(l, entryRW);
Glib::RWLock::WriterLock l(entryRW); #endif
#endif
for (size_t i=0; i<fd.size(); i++) for (size_t i=0; i<fd.size(); i++)
fd[i]->resize (getCurrentThumbSize()); fd[i]->resize (getThumbnailHeight());
} }
redraw (); redraw ();
// Scroll to selected position if going into ribbon mode or back // Scroll to selected position if going into ribbon mode or back
// Tab mode is horizontal, file browser is vertical // Tab mode is horizontal, file browser is vertical
{
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
if (!selected.empty()) { if (!selected.empty()) {
if (inTabMode) { if (inTabMode) {
double h=selected[0]->getStartX(); double h=selected[0]->getStartX();
hscroll.set_value (min(h, hscroll.get_adjustment()->get_upper())); #if PROTECT_VECTORS
MYREADERLOCK_RELEASE(l);
#endif
hscroll.set_value (min(h, hscroll.get_adjustment()->get_upper()));
} else { } else {
double v=selected[0]->getStartY(); double v=selected[0]->getStartY();
#if PROTECT_VECTORS
MYREADERLOCK_RELEASE(l);
#endif
vscroll.set_value (min(v, vscroll.get_adjustment()->get_upper())); vscroll.set_value (min(v, vscroll.get_adjustment()->get_upper()));
} }
} }
}
} }
void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase* entry) { void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase* entry) {
@@ -602,6 +700,10 @@ void ThumbBrowserBase::setScrollPosition (double h, double v) {
int ThumbBrowserBase::getEffectiveHeight() { int ThumbBrowserBase::getEffectiveHeight() {
int h=hscroll.get_height() + 2; // have 2 pixels rounding error for scroll bars to appear int h=hscroll.get_height() + 2; // have 2 pixels rounding error for scroll bars to appear
#if PROTECT_VECTORS
MYREADERLOCK(l, entryRW);
#endif
// Filtered items do not change in size, so take a non-filtered // Filtered items do not change in size, so take a non-filtered
for (size_t i=0;i<fd.size();i++) for (size_t i=0;i<fd.size();i++)
if (!fd[i]->filtered) { if (!fd[i]->filtered) {
@@ -610,10 +712,13 @@ int ThumbBrowserBase::getEffectiveHeight() {
} }
return h; return h;
} }
void ThumbBrowserBase::redrawNeeded (ThumbBrowserEntryBase* entry) { void ThumbBrowserBase::redrawNeeded (ThumbBrowserEntryBase* entry) {
// HOMBRE:DELETE ME?
GThreadLock tLock; // Acquire the GUI
if (entry->insideWindow (0, 0, internal.get_width(), internal.get_height())) { if (entry->insideWindow (0, 0, internal.get_width(), internal.get_height())) {
if (!internal.isDirty ()) { if (!internal.isDirty ()) {
internal.setDirty (); internal.setDirty ();

View File

@@ -23,6 +23,7 @@
#include "thumbbrowserentrybase.h" #include "thumbbrowserentrybase.h"
#include <set> #include <set>
#include "options.h" #include "options.h"
#include "guiutils.h"
/* /*
* Class handling the list of ThumbBrowserEntry objects and their position in it's allocated space * Class handling the list of ThumbBrowserEntry objects and their position in it's allocated space
@@ -54,6 +55,8 @@ class ThumbBrowserBase : public Gtk::VBox {
protected: protected:
virtual int getMaxThumbnailHeight() const { return options.maxThumbnailHeight; } // Differs between batch and file virtual int getMaxThumbnailHeight() const { return options.maxThumbnailHeight; } // Differs between batch and file
virtual void saveThumbnailHeight (int height)=0;
virtual int getThumbnailHeight ()=0;
Internal internal; Internal internal;
Gtk::HScrollbar hscroll; Gtk::HScrollbar hscroll;
@@ -62,7 +65,6 @@ class ThumbBrowserBase : public Gtk::VBox {
int inW, inH; int inW, inH;
bool inTabMode; // Tab mode has e.g. different preview heights bool inTabMode; // Tab mode has e.g. different preview heights
int getCurrentThumbSize(); // depending on filmstrip/file browser mode
void resizeThumbnailArea (int w, int h); void resizeThumbnailArea (int w, int h);
void internalAreaResized (Gtk::Allocation& req); void internalAreaResized (Gtk::Allocation& req);
@@ -80,7 +82,7 @@ class ThumbBrowserBase : public Gtk::VBox {
int eventTime; int eventTime;
Glib::RWLock entryRW; // Locks access to following vectors MyRWMutex entryRW; // Locks access to following 'fd' AND 'selected'
std::vector<ThumbBrowserEntryBase*> fd; std::vector<ThumbBrowserEntryBase*> fd;
std::vector<ThumbBrowserEntryBase*> selected; std::vector<ThumbBrowserEntryBase*> selected;
ThumbBrowserEntryBase* lastClicked; ThumbBrowserEntryBase* lastClicked;

View File

@@ -22,26 +22,18 @@
#include "../rtengine/mytime.h" #include "../rtengine/mytime.h"
ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname) ThumbBrowserEntryBase::ThumbBrowserEntryBase (const Glib::ustring& fname)
: preh(0), preview(NULL), buttonSet(NULL), exp_width(0), exp_height(0), redrawRequests(0), : fnlabw(0), fnlabh(0), dtlabw(0), dtlabh(0), exlabw(0), exlabh(0), prew(0), preh(0),
parent(NULL), filename(fname), exifline(""), datetimeline(""), selected(false), prex(0), prey(0), upperMargin(6), borderWidth(1), textGap(6), sideMargin(8), lowerMargin(8),
drawable(false),framed(false), processing(false), italicstyle(false), preview(NULL), dispname(Glib::path_get_basename (fname)), buttonSet(NULL), width(0), height(0),
updatepriority(false) { exp_width(0), exp_height(0), startx(0), starty(0), ofsX(0), ofsY(0), redrawRequests(0),
parent(NULL), bbSelected(false), bbFramed(false), bbPreview(NULL),
shortname = Glib::path_get_basename (fname); thumbnail(NULL), filename(fname), shortname(dispname), exifline(""), datetimeline(""),
dispname = shortname; selected(false), drawable(false), filtered(false), framed(false), processing(false), italicstyle(false),
edited(false), recentlysaved(false), updatepriority(false) {}
upperMargin = 6;
borderWidth = 1;
sideMargin = 8;
lowerMargin = 8;
textGap = 6;
ofsX = ofsY = 0;
}
ThumbBrowserEntryBase::~ThumbBrowserEntryBase () { ThumbBrowserEntryBase::~ThumbBrowserEntryBase () {
delete [] preview; if (preview) delete [] preview;
delete buttonSet; delete buttonSet;
} }
@@ -259,7 +251,10 @@ void ThumbBrowserEntryBase::getTextSizes (int& infow, int& infoh) {
} }
void ThumbBrowserEntryBase::resize (int h) { void ThumbBrowserEntryBase::resize (int h) {
Glib::RWLock::WriterLock l(lockRW);
#if PROTECT_VECTORS
MYWRITERLOCK(l, lockRW);
#endif
height = h; height = h;
int old_preh = preh, old_width = width; int old_preh = preh, old_width = width;
@@ -348,7 +343,9 @@ void ThumbBrowserEntryBase::draw () {
if (!drawable || !parent) if (!drawable || !parent)
return; return;
Glib::RWLock::ReaderLock l(lockRW); // No resizes, position moves etc. inbetween #if PROTECT_VECTORS
MYREADERLOCK(l, lockRW); // No resizes, position moves etc. inbetween
#endif
int bbWidth, bbHeight; int bbWidth, bbHeight;
if (backBuffer) if (backBuffer)
@@ -376,12 +373,16 @@ void ThumbBrowserEntryBase::draw () {
// redraw button set above the thumbnail // redraw button set above the thumbnail
if (buttonSet) { if (buttonSet) {
buttonSet->setColors (selected ? bgs : bgn, selected ? bgn : bgs); buttonSet->setColors (selected ? bgs : bgn, selected ? bgn : bgs);
buttonSet->redraw (w->get_window()->create_cairo_context()); Cairo::RefPtr<Cairo::Context> cc = w->get_window()->create_cairo_context();
buttonSet->redraw (cc);
} }
} }
void ThumbBrowserEntryBase::setPosition (int x, int y, int w, int h) { void ThumbBrowserEntryBase::setPosition (int x, int y, int w, int h) {
Glib::RWLock::WriterLock l(lockRW);
#if PROTECT_VECTORS
MYWRITERLOCK(l, lockRW);
#endif
exp_width = w; exp_width = w;
exp_height = h; exp_height = h;
@@ -393,7 +394,10 @@ void ThumbBrowserEntryBase::setPosition (int x, int y, int w, int h) {
} }
void ThumbBrowserEntryBase::setOffset (int x, int y) { void ThumbBrowserEntryBase::setOffset (int x, int y) {
Glib::RWLock::WriterLock l(lockRW);
#if PROTECT_VECTORS
MYWRITERLOCK(l, lockRW);
#endif
ofsX = -x; ofsX = -x;
ofsY = -y; ofsY = -y;

View File

@@ -22,6 +22,7 @@
#include <gtkmm.h> #include <gtkmm.h>
#include "lwbuttonset.h" #include "lwbuttonset.h"
#include "thumbnail.h" #include "thumbnail.h"
#include "guiutils.h"
class ThumbBrowserBase; class ThumbBrowserBase;
class ThumbBrowserEntryBase { class ThumbBrowserEntryBase {
@@ -42,7 +43,7 @@ protected:
int lowerMargin; int lowerMargin;
Glib::RWLock lockRW; // Locks access to all image thumb changing actions MyRWMutex lockRW; // Locks access to all image thumb changing actions
guint8* preview; // holds the preview image. used in updateBackBuffer. TODO Olli: Make a cache to reduce mem significantly guint8* preview; // holds the preview image. used in updateBackBuffer. TODO Olli: Make a cache to reduce mem significantly

View File

@@ -48,6 +48,8 @@ public:
Job(): Job():
tbe_(0), tbe_(0),
priority_(NULL),
upgrade_(false),
listener_(0) listener_(0)
{} {}
@@ -86,7 +88,7 @@ public:
Glib::Cond inactive_; Glib::Cond inactive_;
void void
processNextJob(void) processNextJob()
{ {
Job j; Job j;

View File

@@ -33,13 +33,18 @@
using namespace rtengine::procparams; using namespace rtengine::procparams;
Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageData* cf) Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageData* cf, const rtengine::procparams::ProcParams *pparams)
: fname(fname), cfs(*cf), cachemgr(cm), ref(1), enqueueNumber(0), tpp(NULL), : fname(fname), cfs(*cf), cachemgr(cm), ref(1), enqueueNumber(0), tpp(NULL),
pparamsValid(false), needsReProcessing(true),imageLoading(false), lastImg(NULL), pparamsValid(false), needsReProcessing(true),imageLoading(false), lastImg(NULL),
lastW(0), lastH(0), lastScale(0), initial_(false) { lastW(0), lastH(0), lastScale(0), initial_(false)
{
cfs.load (getCacheFileName ("data")+".txt"); if (pparams) {
loadProcParams (); this->pparams = *pparams;
pparamsValid = true;
}
else
loadProcParams ();
_loadThumbnail (); _loadThumbnail ();
generateExifDateTimeStrings (); generateExifDateTimeStrings ();
@@ -55,25 +60,31 @@ Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageDa
setStage(cfs.inTrashOld); setStage(cfs.inTrashOld);
} }
delete tpp; delete tpp;
tpp = 0; tpp = 0;
} }
Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, const std::string& md5) Thumbnail::Thumbnail (CacheManager* cm, const Glib::ustring& fname, const std::string& md5, const rtengine::procparams::ProcParams *pparams)
: fname(fname), cachemgr(cm), ref(1), enqueueNumber(0), tpp(NULL), pparamsValid(false), : fname(fname), cachemgr(cm), ref(1), enqueueNumber(0), tpp(NULL), pparamsValid(false),
needsReProcessing(true),imageLoading(false), lastImg(NULL), needsReProcessing(true),imageLoading(false), lastImg(NULL),
initial_(true) { initial_(true)
{
cfs.md5 = md5; cfs.md5 = md5;
_generateThumbnailImage (); _generateThumbnailImage ();
loadProcParams (); if (pparams) {
this->pparams = *pparams;
pparamsValid = true;
}
else
loadProcParams ();
cfs.recentlySaved = false; cfs.recentlySaved = false;
initial_ = false; initial_ = false;
delete tpp; delete tpp;
tpp = 0; tpp = 0;
} }
void Thumbnail::_generateThumbnailImage () { void Thumbnail::_generateThumbnailImage () {
@@ -96,7 +107,7 @@ void Thumbnail::_generateThumbnailImage () {
cfs.timeValid = false; cfs.timeValid = false;
if (ext.lowercase()=="jpg" || ext.lowercase()=="jpeg") { if (ext.lowercase()=="jpg" || ext.lowercase()=="jpeg") {
tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1, infoFromImage (fname)); tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1);
if (tpp) if (tpp)
cfs.format = FT_Jpeg; cfs.format = FT_Jpeg;
} }
@@ -106,8 +117,7 @@ void Thumbnail::_generateThumbnailImage () {
cfs.format = FT_Png; cfs.format = FT_Png;
} }
else if (ext.lowercase()=="tif" || ext.lowercase()=="tiff") { else if (ext.lowercase()=="tif" || ext.lowercase()=="tiff") {
int deg = infoFromImage (fname); tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1);
tpp = rtengine::Thumbnail::loadFromImage (fname, tw, th, 1, deg);
if (tpp) if (tpp)
cfs.format = FT_Tiff; cfs.format = FT_Tiff;
} }

View File

@@ -76,8 +76,8 @@ class Thumbnail {
Glib::ustring getCacheFileName (Glib::ustring subdir); Glib::ustring getCacheFileName (Glib::ustring subdir);
public: public:
Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageData* cf); Thumbnail (CacheManager* cm, const Glib::ustring& fname, CacheImageData* cf, const rtengine::procparams::ProcParams *pparams=NULL);
Thumbnail (CacheManager* cm, const Glib::ustring& fname, const std::string& md5); Thumbnail (CacheManager* cm, const Glib::ustring& fname, const std::string& md5, const rtengine::procparams::ProcParams *pparams=NULL);
~Thumbnail (); ~Thumbnail ();
bool hasProcParams (); bool hasProcParams ();

View File

@@ -494,8 +494,6 @@ void ToneCurve::autoExpChanged (double expcomp, int bright, int contr, int black
nextHlcompr = hlcompr; nextHlcompr = hlcompr;
nextHlcomprthresh = hlcomprthresh; nextHlcomprthresh = hlcomprthresh;
g_idle_add (autoExpChangedUI, this); g_idle_add (autoExpChangedUI, this);
// Glib::signal_idle().connect (sigc::mem_fun(*this, &ToneCurve::autoExpComputed_));
} }
void ToneCurve::enableAll () { void ToneCurve::enableAll () {
@@ -515,6 +513,7 @@ void ToneCurve::enableAll () {
bool ToneCurve::autoExpComputed_ () { bool ToneCurve::autoExpComputed_ () {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
disableListener (); disableListener ();
enableAll (); enableAll ();
expcomp->setValue (nextExpcomp); expcomp->setValue (nextExpcomp);

View File

@@ -580,6 +580,7 @@ bool ToolPanelCoordinator::handleShortcutKey (GdkEventKey* event) {
} }
void ToolPanelCoordinator::updateVScrollbars (bool hide) { void ToolPanelCoordinator::updateVScrollbars (bool hide) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
Gtk::PolicyType policy = hide ? Gtk::POLICY_NEVER : Gtk::POLICY_AUTOMATIC; Gtk::PolicyType policy = hide ? Gtk::POLICY_NEVER : Gtk::POLICY_AUTOMATIC;
exposurePanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy); exposurePanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
detailsPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy); detailsPanelSW->set_policy (Gtk::POLICY_AUTOMATIC, policy);
@@ -589,9 +590,10 @@ void ToolPanelCoordinator::updateVScrollbars (bool hide) {
} }
void ToolPanelCoordinator::updateTabsHeader (bool useIcons) { void ToolPanelCoordinator::updateTabsHeader (bool useIcons) {
TOITypes type = useIcons ? TOI_ICON : TOI_TEXT; GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
TOITypes type = useIcons ? TOI_ICON : TOI_TEXT;
toiE->switchTo(type); toiE->switchTo(type);
toiD->switchTo(type); toiD->switchTo(type);
toiC->switchTo(type); toiC->switchTo(type);
toiT->switchTo(type); toiT->switchTo(type);
@@ -609,6 +611,7 @@ void ToolPanelCoordinator::updateTabsUsesIcons (bool useIcons) {
} }
void ToolPanelCoordinator::toolSelected (ToolMode tool) { void ToolPanelCoordinator::toolSelected (ToolMode tool) {
GThreadLock lock; // All GUI acces from idle_add callbacks or separate thread HAVE to be protected
switch (tool) { switch (tool) {
case TMCropSelect: case TMCropSelect:
crop->exp->set_expanded(true); crop->exp->set_expanded(true);