2 * This file is part of RawTherapee.
4 * Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
6 * RawTherapee is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * RawTherapee is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with RawTherapee. If not, see <http://www.gnu.org/licenses/>.
19 #include <thumbbrowserbase.h>
21 #include <multilangmgr.h>
25 ThumbBrowserBase::ThumbBrowserBase ()
26 : previewHeight(options
.thumbSize
), lastClicked(NULL
) {
30 Gtk::HBox
* hb1
= new Gtk::HBox ();
31 Gtk::HBox
* hb2
= new Gtk::HBox ();
32 Gtk::Frame
* frame
= new Gtk::Frame ();
33 frame
->add (internal
);
34 frame
->set_shadow_type (Gtk::SHADOW_IN
);
35 hb1
->pack_start (*frame
);
36 hb1
->pack_end (vscroll
, Gtk::PACK_SHRINK
, 0);
40 Gtk::HBox
* tmp
= new Gtk::HBox ();
41 hb2
->pack_start (hscroll
);
42 Gtk::Requisition vcr
= vscroll
.size_request ();
43 tmp
->set_size_request (vcr
.width
, vcr
.width
);
44 hb2
->pack_end (*tmp
, Gtk::PACK_SHRINK
, 0);
46 pack_start (*hb2
,Gtk::PACK_SHRINK
, 0);
48 internal
.setParent (this);
52 hscroll
.set_update_policy (Gtk::UPDATE_CONTINUOUS
);
53 vscroll
.set_update_policy (Gtk::UPDATE_CONTINUOUS
);
55 vscroll
.signal_value_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::scrollChanged
) );
56 hscroll
.signal_value_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::scrollChanged
) );
58 internal
.signal_size_allocate().connect( sigc::mem_fun(*this, &ThumbBrowserBase::internalAreaResized
) );
59 signal_style_changed().connect( sigc::mem_fun(*this, &ThumbBrowserBase::styleChanged
) );
62 void ThumbBrowserBase::scrollChanged () {
64 for (int i
=0; i
<fd
.size(); i
++)
65 fd
[i
]->setOffset ((int)(hscroll
.get_value()), (int)(vscroll
.get_value()));
67 internal
.setPosition ((int)(hscroll
.get_value()), (int)(vscroll
.get_value()));
69 if (!internal
.isDirty()) {
71 internal
.queue_draw ();
72 // gdk_window_process_updates (get_window()->gobj(), true);
76 void ThumbBrowserBase::scroll (int direction
) {
78 if (arrangement
==TB_Vertical
)
79 vscroll
.set_value (vscroll
.get_value() + (direction
==GDK_SCROLL_DOWN
? +1 : -1) * vscroll
.get_adjustment()->get_step_increment());
81 hscroll
.set_value (hscroll
.get_value() + (direction
==GDK_SCROLL_DOWN
? +1 : -1) * hscroll
.get_adjustment()->get_step_increment());
84 void ThumbBrowserBase::resizeThumbnailArea (int w
, int h
) {
89 if (hscroll
.get_value() + internal
.get_width() > inW
)
90 hscroll
.set_value (inW
- internal
.get_width());
91 if (vscroll
.get_value() + internal
.get_height() > inH
)
92 vscroll
.set_value (inH
- internal
.get_height());
97 void ThumbBrowserBase::internalAreaResized (Gtk::Allocation
& req
) {
105 void ThumbBrowserBase::configScrollBars () {
107 if (inW
>0 && inH
>0) {
109 int iw
= internal
.get_width ();
110 int ih
= internal
.get_height ();
112 hscroll
.get_adjustment()->set_upper (inW
);
113 vscroll
.get_adjustment()->set_upper (inH
);
114 hscroll
.get_adjustment()->set_lower (0);
115 vscroll
.get_adjustment()->set_lower (0);
116 hscroll
.get_adjustment()->set_step_increment (32);
117 vscroll
.get_adjustment()->set_step_increment (32);
118 hscroll
.get_adjustment()->set_page_increment (iw
);
119 vscroll
.get_adjustment()->set_page_increment (ih
);
120 hscroll
.get_adjustment()->set_page_size (iw
);
121 vscroll
.get_adjustment()->set_page_size (ih
);
125 void ThumbBrowserBase::arrangeFiles () {
129 for (int i
=0; i
<N
; i
++)
130 fd
[i
]->filtered
= !checkFilter (fd
[i
]);
133 // compute size of the items
134 for (int i
=0; i
<N
; i
++)
135 if (!fd
[i
]->filtered
&& fd
[i
]->getMinimalHeight() > rowHeight
)
136 rowHeight
= fd
[i
]->getMinimalHeight ();
138 if (arrangement
==TB_Horizontal
) {
142 numOfRows
= (internal
.get_height()+rowHeight
/2)/rowHeight
;
148 int currx
= 0; int curry
= 0;
150 // find widest item in the column
152 for (int i
=0; ct
+i
<N
&& i
<numOfRows
; i
++)
153 if (fd
[ct
+i
]->getMinimalWidth() > maxw
)
154 maxw
= fd
[ct
+i
]->getMinimalWidth ();
156 // arrange items in the column
158 for (int i
=0; ct
<N
&& i
<numOfRows
; i
++, ct
++) {
159 while (ct
<N
&& fd
[ct
]->filtered
)
160 fd
[ct
++]->drawable
= false;
162 fd
[ct
]->setPosition (currx
, curry
, maxw
, rowHeight
);
163 fd
[ct
]->drawable
= true;
169 resizeThumbnailArea (currx
, numOfRows
*rowHeight
);
172 int availWidth
= internal
.get_width();
173 // initial number of columns
177 for (int i
=0; i
<N
; i
++)
178 if (!fd
[i
]->filtered
&& colsWidth
+ fd
[i
]->getMinimalWidth() <= availWidth
) {
179 colsWidth
+= fd
[numOfCols
]->getMinimalWidth ();
184 std::vector
<int> colWidths
;
185 for (; numOfCols
>0; numOfCols
--) {
186 // compute column widths
187 colWidths
.resize (numOfCols
);
188 for (int i
=0; i
<numOfCols
; i
++)
190 for (int i
=0, j
=0; i
<N
; i
++) {
191 if (!fd
[i
]->filtered
&& fd
[i
]->getMinimalWidth() > colWidths
[j
%numOfCols
])
192 colWidths
[j
%numOfCols
] = fd
[i
]->getMinimalWidth ();
193 if (!fd
[i
]->filtered
)
196 // if not wider than the space available, arrange it and we are ready
198 for (int i
=0; i
<numOfCols
; i
++)
199 colsWidth
+= colWidths
[i
];
200 if (numOfCols
==1 || colsWidth
< availWidth
)
205 int currx
= 0; int curry
= 0;
207 // arrange items in the row
209 for (int i
=0; ct
<N
&& i
<numOfCols
; i
++, ct
++) {
210 while (ct
<N
&& fd
[ct
]->filtered
)
211 fd
[ct
++]->drawable
= false;
213 fd
[ct
]->setPosition (currx
, curry
, colWidths
[i
%numOfCols
], rowHeight
);
214 fd
[ct
]->drawable
= true;
215 currx
+= colWidths
[i
%numOfCols
];
218 if (currx
>0) // there were thumbnails placed in the row
221 resizeThumbnailArea (colsWidth
, curry
);
226 void ThumbBrowserBase::Internal::on_realize()
228 Cairo::FontOptions cfo
;
229 cfo
.set_antialias (Cairo::ANTIALIAS_SUBPIXEL
);
230 get_pango_context()->set_cairo_font_options (cfo
);
232 Gtk::DrawingArea::on_realize();
233 Glib::RefPtr
<Gdk::Window
> window
= get_window();
234 set_flags (Gtk::CAN_FOCUS
);
235 add_events(Gdk::EXPOSURE_MASK
| Gdk::BUTTON_PRESS_MASK
| Gdk::BUTTON_RELEASE_MASK
| Gdk::POINTER_MOTION_MASK
| Gdk::SCROLL_MASK
| Gdk::KEY_PRESS_MASK
);
236 gc_
= Gdk::GC::create(window
);
237 set_has_tooltip (true);
238 signal_query_tooltip().connect( sigc::mem_fun(*this, &ThumbBrowserBase::Internal::on_query_tooltip
) );
241 bool ThumbBrowserBase::Internal::on_query_tooltip (int x
, int y
, bool keyboard_tooltip
, const Glib::RefPtr
<Gtk::Tooltip
>& tooltip
) {
243 Glib::ustring ttip
= "";
244 for (int i
=0; i
<parent
->fd
.size(); i
++)
245 if (parent
->fd
[i
]->drawable
&& parent
->fd
[i
]->inside (x
, y
)) {
246 ttip
= parent
->fd
[i
]->getToolTip (x
, y
);
250 tooltip
->set_text (ttip
);
257 void ThumbBrowserBase::styleChanged (const Glib::RefPtr
<Gtk::Style
>& style
) {
259 refreshThumbImages ();
262 ThumbBrowserBase::Internal::Internal () : parent(NULL
), ofsX(0), ofsY(0), dirty(true) {
265 void ThumbBrowserBase::Internal::setParent (ThumbBrowserBase
* p
) {
270 void ThumbBrowserBase::Internal::setPosition (int x
, int y
) {
276 bool ThumbBrowserBase::Internal::on_key_press_event (GdkEventKey
* event
) {
278 return parent
->keyPressed (event
);
281 bool ThumbBrowserBase::Internal::on_button_press_event (GdkEventButton
* event
) {
285 parent
->eventTime
= event
->time
;
287 parent
->buttonPressed ((int)event
->x
, (int)event
->y
, event
->button
, event
->type
, event
->state
, 0, 0, get_width(), get_height());
288 Glib::RefPtr
<Gdk::Window
> window
= get_window();
293 window
->get_size (rect
.width
, rect
.height
);
295 gdk_window_invalidate_rect (window
->gobj(), &rect
, true);
296 gdk_window_process_updates (window
->gobj(), true);
300 void ThumbBrowserBase::buttonPressed (int x
, int y
, int button
, GdkEventType type
, int state
, int clx
, int cly
, int clw
, int clh
) {
302 ThumbBrowserEntryBase
* fileDescr
= NULL
;
303 bool handled
= false;
304 for (int i
=0; i
<fd
.size(); i
++)
305 if (fd
[i
]->drawable
) {
306 if (fd
[i
]->inside (x
, y
) && fd
[i
]->insideWindow (clx
, cly
, clw
, clh
))
308 bool b
= fd
[i
]->pressNotify (button
, type
, state
, x
, y
);
309 handled
= handled
|| b
;
312 if (handled
|| (fileDescr
&& fileDescr
->processing
))
315 if (selected
.size()==1 && type
==GDK_2BUTTON_PRESS
&& button
==1)
316 doubleClicked (selected
[0]);
317 else if (button
==1 && type
==GDK_BUTTON_PRESS
) {
318 if (fileDescr
&& state
& GDK_SHIFT_MASK
) {
319 if (selected
.size()==0) {
320 selected
.push_back (fileDescr
);
321 fileDescr
->selected
= true;
322 lastClicked
= fileDescr
;
326 // find the start and the end of the selection interval
327 int startx
= fd
.size()-1;
329 for (; startx
>=0; startx
--)
330 if (fd
[startx
]==lastClicked
)
334 for (; startx
>=0; startx
--)
335 if (fd
[startx
]==selected
[0])
339 for (; endx
<fd
.size(); endx
++)
340 if (fd
[endx
]==fileDescr
)
347 // clear current selection
348 for (int i
=0; i
<selected
.size(); i
++)
349 selected
[i
]->selected
= false;
351 // select thumbnails in the interval
352 for (int i
=startx
; i
<=endx
; i
++) {
353 if (!fd
[i
]->filtered
) {
354 fd
[i
]->selected
= true;
355 selected
.push_back (fd
[i
]);
361 else if (fileDescr
&& state
& GDK_CONTROL_MASK
) {
362 std::vector
<ThumbBrowserEntryBase
*>::iterator i
= std::find (selected
.begin(), selected
.end(), fileDescr
);
363 if (i
!=selected
.end()) {
364 (*i
)->selected
= false;
368 selected
.push_back (fileDescr
);
369 fileDescr
->selected
= true;
371 lastClicked
= fileDescr
;
375 for (int i
=0; i
<selected
.size(); i
++)
376 selected
[i
]->selected
= false;
379 selected
.push_back (fileDescr
);
380 fileDescr
->selected
= true;
382 lastClicked
= fileDescr
;
386 else if (fileDescr
&& button
==3 && type
==GDK_BUTTON_PRESS
) {
387 if (!fileDescr
->selected
) {
388 for (int i
=0; i
<selected
.size(); i
++)
389 selected
[i
]->selected
= false;
391 fileDescr
->selected
= true;
392 selected
.push_back (fileDescr
);
393 lastClicked
= fileDescr
;
396 rightClicked (fileDescr
);
400 bool ThumbBrowserBase::Internal::on_expose_event(GdkEventExpose
* event
) {
404 Glib::RefPtr
<Gdk::Window
> window
= get_window();
407 int h
= get_height();
411 Glib::RefPtr
<Pango::Context
> context
= get_pango_context ();
412 context
->set_font_description (get_style()->get_font());
413 for (int i
=0; i
<parent
->fd
.size(); i
++) {
414 if (!parent
->fd
[i
]->drawable
|| !parent
->fd
[i
]->insideWindow (0, 0, w
, h
))
415 parent
->fd
[i
]->updatepriority
= false;
417 parent
->fd
[i
]->updatepriority
= true;
418 parent
->fd
[i
]->draw ();
425 bool ThumbBrowserBase::Internal::on_button_release_event (GdkEventButton
* event
) {
428 int h
= get_height();
430 for (int i
=0; i
<parent
->fd
.size(); i
++)
431 if (parent
->fd
[i
]->drawable
&& parent
->fd
[i
]->insideWindow (0, 0, w
, h
))
432 parent
->fd
[i
]->releaseNotify (event
->button
, event
->type
, event
->state
, (int)event
->x
, (int)event
->y
);
436 bool ThumbBrowserBase::Internal::on_motion_notify_event (GdkEventMotion
* event
) {
439 int h
= get_height();
441 for (int i
=0; i
<parent
->fd
.size(); i
++)
442 if (parent
->fd
[i
]->drawable
&& parent
->fd
[i
]->insideWindow (0, 0, w
, h
))
443 parent
->fd
[i
]->motionNotify ((int)event
->x
, (int)event
->y
);
447 bool ThumbBrowserBase::Internal::on_scroll_event (GdkEventScroll
* event
) {
449 parent
->scroll (event
->direction
);
454 void ThumbBrowserBase::redraw () {
460 void ThumbBrowserBase::zoomChanged (bool zoomIn
) {
465 for (i
=0; i
<options
.thumbnailZoomRatios
.size(); i
++) {
466 newHeight
= (int)(options
.thumbnailZoomRatios
[i
] * options
.maxThumbnailHeight
);
467 if (newHeight
> options
.thumbSize
)
471 for (i
=options
.thumbnailZoomRatios
.size()-1; i
>=0; i
--) {
472 newHeight
= (int)(options
.thumbnailZoomRatios
[i
] * options
.maxThumbnailHeight
);
473 if (newHeight
< options
.thumbSize
)
476 previewHeight
= options
.thumbSize
= newHeight
;
477 for (int i
=0; i
<fd
.size(); i
++)
478 fd
[i
]->resize (previewHeight
);
481 gdk_window_process_updates (get_window()->gobj(), true);
484 void ThumbBrowserBase::refreshThumbImages () {
486 for (int i
=0; i
<fd
.size(); i
++)
487 fd
[i
]->refreshThumbnailImage ();
492 void ThumbBrowserBase::refreshEditedState (const std::set
<Glib::ustring
>& efiles
) {
494 editedFiles
= efiles
;
495 for (int i
=0; i
<fd
.size(); i
++)
496 fd
[i
]->framed
= editedFiles
.find (fd
[i
]->filename
)!=editedFiles
.end();
500 void ThumbBrowserBase::setArrangement (Arrangement a
) {
506 void ThumbBrowserBase::initEntry (ThumbBrowserEntryBase
* entry
) {
508 entry
->setOffset ((int)(hscroll
.get_value()), (int)(vscroll
.get_value()));
510 void ThumbBrowserBase::getScrollPosition (double& h
, double& v
) {
512 h
= hscroll
.get_value ();
513 v
= vscroll
.get_value ();
516 void ThumbBrowserBase::setScrollPosition (double h
, double v
) {
518 hscroll
.set_value (h
>hscroll
.get_adjustment()->get_upper() ? hscroll
.get_adjustment()->get_upper() : h
);
519 vscroll
.set_value (v
>vscroll
.get_adjustment()->get_upper() ? vscroll
.get_adjustment()->get_upper() : v
);
522 /*void PreviewImgUpdater::processCustomOrder () {
524 // find first filtered entry, if any
525 std::list<ThumbBrowserEntryBase*>::iterator i;
526 for (i=jqueue.begin (); i!=jqueue.end(); i++)
532 ThumbBrowserEntryBase* current = *i;
535 current->updateImg ();
537 gdk_threads_enter ();
538 parent->queue_draw ();
539 if (parent->get_window())
540 gdk_window_process_updates (parent->get_window()->gobj(), true);
541 gdk_threads_leave ();