rawTherapee/rtgui/thumbbrowserbase.cc
ffsup2 eef14f76dd Added dark frame subtraction
Moved debayer and preprocessing parameters to class ProcParams for every single image.
Added tab RAW for changing those parameters.
Progress bar shows only load step (work to do)
2010-08-19 00:37:53 +02:00

549 lines
17 KiB
C++

/*
* This file is part of RawTherapee.
*
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
*/
#include <thumbbrowserbase.h>
#include <glibmm.h>
#include <multilangmgr.h>
#include <options.h>
#include <mytime.h>
ThumbBrowserBase::ThumbBrowserBase ()
: previewHeight(options.thumbSize), lastClicked(NULL) {
inW = -1; inH = -1;
Gtk::HBox* hb1 = new Gtk::HBox ();
Gtk::HBox* hb2 = new Gtk::HBox ();
Gtk::Frame* frame = new Gtk::Frame ();
frame->add (internal);
frame->set_shadow_type (Gtk::SHADOW_IN );
hb1->pack_start (*frame);
hb1->pack_end (vscroll, Gtk::PACK_SHRINK, 0);
pack_start (*hb1);
Gtk::HBox* tmp = new Gtk::HBox ();
hb2->pack_start (hscroll);
Gtk::Requisition vcr = vscroll.size_request ();
tmp->set_size_request (vcr.width, vcr.width);
hb2->pack_end (*tmp, Gtk::PACK_SHRINK, 0);
pack_start (*hb2,Gtk::PACK_SHRINK, 0);
internal.setParent (this);
show_all ();
hscroll.set_update_policy (Gtk::UPDATE_CONTINUOUS);
vscroll.set_update_policy (Gtk::UPDATE_CONTINUOUS);
vscroll.signal_value_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::scrollChanged) );
hscroll.signal_value_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::scrollChanged) );
internal.signal_size_allocate().connect( sigc::mem_fun(*this, &ThumbBrowserBase::internalAreaResized) );
signal_style_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::styleChanged) );
}
void ThumbBrowserBase::scrollChanged () {
for (int i=0; i<fd.size(); i++)
fd[i]->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
internal.setPosition ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
if (!internal.isDirty()) {
internal.setDirty ();
internal.queue_draw ();
// gdk_window_process_updates (get_window()->gobj(), true);
}
}
void ThumbBrowserBase::scroll (int direction) {
if (arrangement==TB_Vertical)
vscroll.set_value (vscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * vscroll.get_adjustment()->get_step_increment());
else
hscroll.set_value (hscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * hscroll.get_adjustment()->get_step_increment());
}
void ThumbBrowserBase::resizeThumbnailArea (int w, int h) {
inW = w;
inH = h;
if (hscroll.get_value() + internal.get_width() > inW)
hscroll.set_value (inW - internal.get_width());
if (vscroll.get_value() + internal.get_height() > inH)
vscroll.set_value (inH - internal.get_height());
configScrollBars ();
}
void ThumbBrowserBase::internalAreaResized (Gtk::Allocation& req) {
if (inW>0 && inH>0) {
configScrollBars ();
redraw ();
}
}
void ThumbBrowserBase::configScrollBars () {
if (inW>0 && inH>0) {
int iw = internal.get_width ();
int ih = internal.get_height ();
hscroll.get_adjustment()->set_upper (inW);
vscroll.get_adjustment()->set_upper (inH);
hscroll.get_adjustment()->set_lower (0);
vscroll.get_adjustment()->set_lower (0);
hscroll.get_adjustment()->set_step_increment (32);
vscroll.get_adjustment()->set_step_increment (32);
hscroll.get_adjustment()->set_page_increment (iw);
vscroll.get_adjustment()->set_page_increment (ih);
hscroll.get_adjustment()->set_page_size (iw);
vscroll.get_adjustment()->set_page_size (ih);
}
}
void ThumbBrowserBase::arrangeFiles () {
int N = fd.size ();
// apply filter
for (int i=0; i<N; i++)
fd[i]->filtered = !checkFilter (fd[i]);
int rowHeight = 0;
// compute size of the items
for (int i=0; i<N; i++)
if (!fd[i]->filtered && fd[i]->getMinimalHeight() > rowHeight)
rowHeight = fd[i]->getMinimalHeight ();
if (arrangement==TB_Horizontal) {
int numOfRows = 1;
if (rowHeight>0) {
numOfRows = (internal.get_height()+rowHeight/2)/rowHeight;
if (numOfRows<1)
numOfRows = 1;
}
int ct = 0;
int currx = 0; int curry = 0;
while (ct<N) {
// find widest item in the column
int maxw = 0;
for (int i=0; ct+i<N && i<numOfRows; i++)
if (fd[ct+i]->getMinimalWidth() > maxw)
maxw = fd[ct+i]->getMinimalWidth ();
// arrange items in the column
curry = 0;
for (int i=0; ct<N && i<numOfRows; i++, ct++) {
while (ct<N && fd[ct]->filtered)
fd[ct++]->drawable = false;
if (ct<N) {
fd[ct]->setPosition (currx, curry, maxw, rowHeight);
fd[ct]->drawable = true;
curry += rowHeight;
}
}
currx += maxw;
}
resizeThumbnailArea (currx, numOfRows*rowHeight);
}
else {
int availWidth = internal.get_width();
// initial number of columns
int numOfCols = 0;
int currColNum = 0;
int colsWidth = 0;
for (int i=0; i<N; i++)
if (!fd[i]->filtered && colsWidth + fd[i]->getMinimalWidth() <= availWidth) {
colsWidth += fd[numOfCols]->getMinimalWidth ();
numOfCols++;
}
if (numOfCols<1)
numOfCols = 1;
std::vector<int> colWidths;
for (; numOfCols>0; numOfCols--) {
// compute column widths
colWidths.resize (numOfCols);
for (int i=0; i<numOfCols; i++)
colWidths[i] = 0;
for (int i=0, j=0; i<N; i++) {
if (!fd[i]->filtered && fd[i]->getMinimalWidth() > colWidths[j%numOfCols])
colWidths[j%numOfCols] = fd[i]->getMinimalWidth ();
if (!fd[i]->filtered)
j++;
}
// if not wider than the space available, arrange it and we are ready
colsWidth = 0;
for (int i=0; i<numOfCols; i++)
colsWidth += colWidths[i];
if (numOfCols==1 || colsWidth < availWidth)
break;
}
// arrange files
int ct = 0;
int currx = 0; int curry = 0;
while (ct<N) {
// arrange items in the row
currx = 0;
for (int i=0; ct<N && i<numOfCols; i++, ct++) {
while (ct<N && fd[ct]->filtered)
fd[ct++]->drawable = false;
if (ct<N) {
fd[ct]->setPosition (currx, curry, colWidths[i%numOfCols], rowHeight);
fd[ct]->drawable = true;
currx += colWidths[i%numOfCols];
}
}
if (currx>0) // there were thumbnails placed in the row
curry += rowHeight;
}
resizeThumbnailArea (colsWidth, curry);
}
}
void ThumbBrowserBase::Internal::on_realize()
{
Cairo::FontOptions cfo;
cfo.set_antialias (Cairo::ANTIALIAS_SUBPIXEL);
get_pango_context()->set_cairo_font_options (cfo);
Gtk::DrawingArea::on_realize();
Glib::RefPtr<Gdk::Window> window = get_window();
set_flags (Gtk::CAN_FOCUS);
add_events(Gdk::EXPOSURE_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK | Gdk::SCROLL_MASK | Gdk::KEY_PRESS_MASK);
gc_ = Gdk::GC::create(window);
set_has_tooltip (true);
signal_query_tooltip().connect( sigc::mem_fun(*this, &ThumbBrowserBase::Internal::on_query_tooltip) );
}
bool ThumbBrowserBase::Internal::on_query_tooltip (int x, int y, bool keyboard_tooltip, const Glib::RefPtr<Gtk::Tooltip>& tooltip) {
Glib::ustring ttip = "";
for (int i=0; i<parent->fd.size(); i++)
if (parent->fd[i]->drawable && parent->fd[i]->inside (x, y)) {
ttip = parent->fd[i]->getToolTip (x, y);
break;
}
if (ttip!="") {
tooltip->set_text (ttip);
return true;
}
else
return false;
}
void ThumbBrowserBase::styleChanged (const Glib::RefPtr<Gtk::Style>& style) {
refreshThumbImages ();
}
ThumbBrowserBase::Internal::Internal () : parent(NULL), ofsX(0), ofsY(0), dirty(true) {
}
void ThumbBrowserBase::Internal::setParent (ThumbBrowserBase* p) {
parent = p;
}
void ThumbBrowserBase::Internal::setPosition (int x, int y) {
ofsX = x;
ofsY = y;
}
bool ThumbBrowserBase::Internal::on_key_press_event (GdkEventKey* event) {
return parent->keyPressed (event);
}
bool ThumbBrowserBase::Internal::on_button_press_event (GdkEventButton* event) {
grab_focus ();
parent->eventTime = event->time;
parent->buttonPressed ((int)event->x, (int)event->y, event->button, event->type, event->state, 0, 0, get_width(), get_height());
Glib::RefPtr<Gdk::Window> window = get_window();
GdkRectangle rect;
rect.x = 0;
rect.y = 0;
window->get_size (rect.width, rect.height);
gdk_window_invalidate_rect (window->gobj(), &rect, true);
gdk_window_process_updates (window->gobj(), true);
return true;
}
void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType type, int state, int clx, int cly, int clw, int clh) {
ThumbBrowserEntryBase* fileDescr = NULL;
bool handled = false;
for (int i=0; i<fd.size(); i++)
if (fd[i]->drawable) {
if (fd[i]->inside (x, y) && fd[i]->insideWindow (clx, cly, clw, clh))
fileDescr = fd[i];
bool b = fd[i]->pressNotify (button, type, state, x, y);
handled = handled || b;
}
if (handled || (fileDescr && fileDescr->processing))
return;
if (selected.size()==1 && type==GDK_2BUTTON_PRESS && button==1)
doubleClicked (selected[0]);
else if (button==1 && type==GDK_BUTTON_PRESS) {
if (fileDescr && state & GDK_SHIFT_MASK) {
if (selected.size()==0) {
selected.push_back (fileDescr);
fileDescr->selected = true;
lastClicked = fileDescr;
selectionChanged ();
}
else {
// find the start and the end of the selection interval
int startx = fd.size()-1;
if (lastClicked) {
for (; startx>=0; startx--)
if (fd[startx]==lastClicked)
break;
}
else {
for (; startx>=0; startx--)
if (fd[startx]==selected[0])
break;
}
int endx = 0;
for (; endx<fd.size(); endx++)
if (fd[endx]==fileDescr)
break;
if (endx < startx) {
int tmp = endx;
endx = startx;
startx = tmp;
}
// clear current selection
for (int i=0; i<selected.size(); i++)
selected[i]->selected = false;
selected.clear ();
// select thumbnails in the interval
for (int i=startx; i<=endx; i++) {
if (!fd[i]->filtered) {
fd[i]->selected = true;
selected.push_back (fd[i]);
}
}
selectionChanged ();
}
}
else if (fileDescr && state & GDK_CONTROL_MASK) {
std::vector<ThumbBrowserEntryBase*>::iterator i = std::find (selected.begin(), selected.end(), fileDescr);
if (i!=selected.end()) {
(*i)->selected = false;
selected.erase (i);
}
else {
selected.push_back (fileDescr);
fileDescr->selected = true;
}
lastClicked = fileDescr;
selectionChanged ();
}
else {
for (int i=0; i<selected.size(); i++)
selected[i]->selected = false;
selected.clear ();
if (fileDescr) {
selected.push_back (fileDescr);
fileDescr->selected = true;
}
lastClicked = fileDescr;
selectionChanged ();
}
}
else if (fileDescr && button==3 && type==GDK_BUTTON_PRESS) {
if (!fileDescr->selected) {
for (int i=0; i<selected.size(); i++)
selected[i]->selected = false;
selected.clear ();
fileDescr->selected = true;
selected.push_back (fileDescr);
lastClicked = fileDescr;
selectionChanged ();
}
rightClicked (fileDescr);
}
}
bool ThumbBrowserBase::Internal::on_expose_event(GdkEventExpose* event) {
dirty = false;
Glib::RefPtr<Gdk::Window> window = get_window();
int w = get_width();
int h = get_height();
window->clear();
// draw thumbnails
Glib::RefPtr<Pango::Context> context = get_pango_context ();
context->set_font_description (get_style()->get_font());
for (int i=0; i<parent->fd.size(); i++) {
if (!parent->fd[i]->drawable || !parent->fd[i]->insideWindow (0, 0, w, h))
parent->fd[i]->updatepriority = false;
else {
parent->fd[i]->updatepriority = true;
parent->fd[i]->draw ();
}
}
return true;
}
bool ThumbBrowserBase::Internal::on_button_release_event (GdkEventButton* event) {
int w = get_width();
int h = get_height();
for (int i=0; i<parent->fd.size(); i++)
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);
return true;
}
bool ThumbBrowserBase::Internal::on_motion_notify_event (GdkEventMotion* event) {
int w = get_width();
int h = get_height();
for (int i=0; i<parent->fd.size(); i++)
if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h))
parent->fd[i]->motionNotify ((int)event->x, (int)event->y);
return true;
}
bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll* event) {
parent->scroll (event->direction);
return true;
}
void ThumbBrowserBase::redraw () {
arrangeFiles ();
queue_draw ();
}
void ThumbBrowserBase::zoomChanged (bool zoomIn) {
int newHeight;
int i=0;
if (zoomIn)
for (i=0; i<options.thumbnailZoomRatios.size(); i++) {
newHeight = (int)(options.thumbnailZoomRatios[i] * options.maxThumbnailHeight);
if (newHeight > options.thumbSize)
break;
}
else
for (i=options.thumbnailZoomRatios.size()-1; i>=0; i--) {
newHeight = (int)(options.thumbnailZoomRatios[i] * options.maxThumbnailHeight);
if (newHeight < options.thumbSize)
break;
}
previewHeight = options.thumbSize = newHeight;
for (int i=0; i<fd.size(); i++)
fd[i]->resize (previewHeight);
redraw ();
#ifdef _WIN32
gdk_window_process_updates (get_window()->gobj(), true);
#endif
}
void ThumbBrowserBase::refreshThumbImages () {
for (int i=0; i<fd.size(); i++){
previewHeight = options.thumbSize;
fd[i]->resize (previewHeight);// TODO!!! Might be performance bottleneck
fd[i]->refreshThumbnailImage ();
}
redraw ();
}
void ThumbBrowserBase::refreshEditedState (const std::set<Glib::ustring>& efiles) {
editedFiles = efiles;
for (int i=0; i<fd.size(); i++)
fd[i]->framed = editedFiles.find (fd[i]->filename)!=editedFiles.end();
queue_draw ();
}
void ThumbBrowserBase::setArrangement (Arrangement a) {
arrangement = a;
redraw ();
}
void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase* entry) {
entry->setOffset ((int)(hscroll.get_value()), (int)(vscroll.get_value()));
}
void ThumbBrowserBase::getScrollPosition (double& h, double& v) {
h = hscroll.get_value ();
v = vscroll.get_value ();
}
void ThumbBrowserBase::setScrollPosition (double h, double v) {
hscroll.set_value (h>hscroll.get_adjustment()->get_upper() ? hscroll.get_adjustment()->get_upper() : h);
vscroll.set_value (v>vscroll.get_adjustment()->get_upper() ? vscroll.get_adjustment()->get_upper() : v);
}
/*void PreviewImgUpdater::processCustomOrder () {
// find first filtered entry, if any
std::list<ThumbBrowserEntryBase*>::iterator i;
for (i=jqueue.begin (); i!=jqueue.end(); i++)
if (!(*i)->filtered)
break;
if (i==jqueue.end())
i = jqueue.begin();
ThumbBrowserEntryBase* current = *i;
jqueue.erase (i);
current->updateImg ();
if (parent) {
gdk_threads_enter ();
parent->queue_draw ();
if (parent->get_window())
gdk_window_process_updates (parent->get_window()->gobj(), true);
gdk_threads_leave ();
}
}
*/