From 01414d05503e3bed83dde9ca4630e7cb05ba9103 Mon Sep 17 00:00:00 2001 From: michael Date: Tue, 2 Jul 2013 13:33:46 -0400 Subject: [PATCH] Keyboard navigation in File Browser (issue 1919, on behalf of Adam Reichold) Select a thumbnail using the arrow keys, select multiple with Shift --- rtgui/filebrowser.cc | 44 ++++++--- rtgui/thumbbrowserbase.cc | 171 ++++++++++++++++++++++++++++++---- rtgui/thumbbrowserbase.h | 4 + rtgui/thumbbrowserentrybase.h | 3 + 4 files changed, 188 insertions(+), 34 deletions(-) diff --git a/rtgui/filebrowser.cc b/rtgui/filebrowser.cc index 805c0f7cc..15ce93619 100644 --- a/rtgui/filebrowser.cc +++ b/rtgui/filebrowser.cc @@ -310,12 +310,12 @@ void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) { trash->set_sensitive (false); untrash->set_sensitive (false); - for (size_t i=0; i(selected[i]))->thumbnail->getStage()==1) { untrash->set_sensitive (true); break; } - for (size_t i=0; i(selected[i]))->thumbnail->getStage()==0) { trash->set_sensitive (true); break; @@ -331,7 +331,7 @@ void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) { int p = 0; Gtk::Menu* applmenu = Gtk::manage (new Gtk::Menu ()); std::vector profnames = profileStore.getProfileNames (); - for (size_t i=0; iattach (*mi, 0, 1, p, p+1); p++; mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyMenuItemActivated), profnames[i])); @@ -343,7 +343,7 @@ void FileBrowser::rightClicked (ThumbBrowserEntryBase* entry) { p = 0; Gtk::Menu* applpartmenu = Gtk::manage (new Gtk::Menu ()); //std::vector profnames = profileStore.getProfileNames (); // this is already created for submenu applmenu above - for (size_t i=0; iattach (*mi, 0, 1, p, p+1); p++; mi->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &FileBrowser::applyPartialMenuItemActivated), profnames[i])); @@ -404,7 +404,7 @@ struct addparams { int AddEntryUIThread (void* data) { - addparams* ap = static_cast(data); + addparams* ap = static_cast(data); FileBrowserIdleHelper* fbih = ap->fbih; if (fbih->destroyed) { @@ -563,7 +563,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { MYREADERLOCK(l, entryRW); #endif - for (size_t i=0; i(selected[i])); } @@ -786,7 +786,7 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { else if (m==partpasteprof) partPasteProfile (); else if (m==clearprof) { - for (size_t i=0; ithumbnail->clearProcParams (FILEBROWSER); queue_draw (); } else if (m==execcustprof) { @@ -802,12 +802,12 @@ void FileBrowser::menuItemActivated (Gtk::MenuItem* m) { if (!mselected.empty() && bppcl) bppcl->endBatchPParamsChange(); } else if (m==clearFromCache) { - for (size_t i=0; iclearFromCacheRequested (mselected, false); //queue_draw (); } else if (m==clearFromCacheFull) { - for (size_t i=0; iclearFromCacheRequested (mselected, true); //queue_draw (); } else if (miOpenDefaultViewer!=NULL && m==miOpenDefaultViewer) { @@ -919,7 +919,7 @@ void FileBrowser::openDefaultViewer (int destination) { } 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 (); } } @@ -970,6 +970,22 @@ bool FileBrowser::keyPressed (GdkEventKey* event) { FileBrowser::openNextImage (); return true; } + else if (event->keyval==GDK_Left) { + selectPrev (1, shift); + return true; + } + else if (event->keyval==GDK_Right) { + selectNext (1, shift); + return true; + } + else if (event->keyval==GDK_Up) { + selectPrev (numOfCols, shift); + return true; + } + else if (event->keyval==GDK_Down) { + selectNext (numOfCols, shift); + return true; + } else if (event->keyval==GDK_F5) { int dest = 1; @@ -1065,7 +1081,7 @@ void FileBrowser::applyMenuItemActivated (Glib::ustring ppname) { if (partProfile->pparams && !selected.empty()) { if (bppcl) bppcl->beginBatchPParamsChange(selected.size()); - for (size_t i=0; i(selected[i]))->thumbnail->setProcParams (*partProfile->pparams, partProfile->pedited, FILEBROWSER); if (bppcl) bppcl->endBatchPParamsChange(); @@ -1090,7 +1106,7 @@ void FileBrowser::applyPartialMenuItemActivated (Glib::ustring ppname) { if (bppcl) bppcl->beginBatchPParamsChange(selected.size()); - for (size_t i=0; ithumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file rtengine::procparams::PartialProfile dstProfile(true); @@ -1213,7 +1229,7 @@ bool FileBrowser::checkFilter (ThumbBrowserEntryBase* entryb) { // true -> entry void FileBrowser::toTrashRequested (std::vector tbe) { - for (size_t i=0; ithumbnail->createProcParamsForUpdate(false, false); // this can execute customprofilebuilder to generate param file @@ -1235,7 +1251,7 @@ void FileBrowser::toTrashRequested (std::vector tbe) { void FileBrowser::fromTrashRequested (std::vector tbe) { - for (size_t i=0; ithumbnail->getStage()==0) diff --git a/rtgui/thumbbrowserbase.cc b/rtgui/thumbbrowserbase.cc index 248ea2218..d11b8a83c 100644 --- a/rtgui/thumbbrowserbase.cc +++ b/rtgui/thumbbrowserbase.cc @@ -14,18 +14,18 @@ * You should have received a copy of the GNU General Public License * along with RawTherapee. If not, see . */ -#include -#include "../rtengine/rt_math.h" - +#include +#include "../rtengine/rt_math.h" + #include "thumbbrowserbase.h" #include "multilangmgr.h" #include "options.h" #include "../rtengine/mytime.h" -using namespace std; - +using namespace std; + ThumbBrowserBase::ThumbBrowserBase () - : lastClicked(NULL), previewHeight(options.thumbSize) { + : lastClicked(NULL), previewHeight(options.thumbSize), numOfCols(1) { inTabMode=false; // corresponding to take thumbSize inW = -1; inH = -1; @@ -91,6 +91,137 @@ void ThumbBrowserBase::scrollPage (int direction) { hscroll.set_value (hscroll.get_value() + (direction==GDK_SCROLL_DOWN ? +1 : -1) * hscroll.get_adjustment()->get_page_increment()); } +static void scrollToEntry (double& h, double& v, int iw, int ih, ThumbBrowserEntryBase* entry) { + const int hmin = entry->getX (); + const int hmax = hmin + entry->getEffectiveWidth () - iw; + const int vmin = entry->getY (); + const int vmax = vmin + entry->getEffectiveHeight () - ih; + + if (hmin < 0) + h += hmin; + else if (hmax > 0) + h += hmax; + + if(vmin < 0) + v += vmin; + else if (vmax > 0) + v += vmax; +} + +void ThumbBrowserBase::selectPrev (int distance, bool enlarge) { + double h, v; + getScrollPosition (h, v); + + { + #if PROTECT_VECTORS + MYWRITERLOCK(l, entryRW); + #endif + + if (!selected.empty ()) { + std::vector::iterator front = std::find (fd.begin (), fd.end (), selected.front ()); + std::vector::iterator back = std::find (fd.begin (), fd.end (), selected.back ()); + + if(front > back) + std::swap(front, back); + + // find next thumbnail at filtered distance before 'front' + for (; front >= fd.begin (); --front) { + if(!(*front)->filtered) { + if (distance-- == 0) { + // clear current selection + for (size_t i=0; iselected = false; + redrawNeeded (selected[i]); + } + selected.clear (); + + // make sure the newly selected thumbnail is visible + scrollToEntry (h, v, internal.get_width (), internal.get_height (), *front); + + // either enlarge current selection or set new selection + for(; front <= back; ++front) { + if(!(*front)->filtered) { + (*front)->selected = true; + redrawNeeded (*front); + selected.push_back (*front); + } + + if(!enlarge) + break; + } + + break; + } + } + } + } + + #if PROTECT_VECTORS + MYWRITERLOCK_RELEASE(l); + #endif + selectionChanged (); + } + + setScrollPosition (h, v); +} + +void ThumbBrowserBase::selectNext (int distance, bool enlarge) { + double h, v; + getScrollPosition (h, v); + + { + #if PROTECT_VECTORS + MYWRITERLOCK(l, entryRW); + #endif + + if (!selected.empty ()) { + std::vector::iterator front = std::find (fd.begin (), fd.end (), selected.front ()); + std::vector::iterator back = std::find (fd.begin (), fd.end (), selected.back ()); + + if(front > back) + std::swap(front, back); + + // find next thumbnail at filtered distance after 'back' + for (; back < fd.end (); ++back) { + if(!(*back)->filtered) { + if (distance-- == 0) { + // clear current selection + for (size_t i=0; iselected = false; + redrawNeeded (selected[i]); + } + selected.clear (); + + // make sure the newly selected thumbnail is visible + scrollToEntry (h, v, internal.get_width (), internal.get_height (), *back); + + // either enlarge current selection or set new selection + for(; back >= front; --back) { + if(!(*back)->filtered) { + (*back)->selected = true; + redrawNeeded (*back); + selected.push_back (*back); + } + + if(!enlarge) + break; + } + + break; + } + } + } + } + + #if PROTECT_VECTORS + MYWRITERLOCK_RELEASE(l); + #endif + selectionChanged (); + } + + setScrollPosition (h, v); +} + void ThumbBrowserBase::resizeThumbnailArea (int w, int h) { inW = w; @@ -167,7 +298,7 @@ void ThumbBrowserBase::arrangeFiles () { rowHeight = fd[i]->getMinimalHeight (); if (arrangement==TB_Horizontal) { - + numOfCols = 1; int numOfRows = 1; // if (rowHeight>0) { // numOfRows = (internal.get_height()+rowHeight/2)/rowHeight; @@ -206,7 +337,7 @@ void ThumbBrowserBase::arrangeFiles () { else { int availWidth = internal.get_width(); // initial number of columns - int numOfCols = 0; + numOfCols = 0; int colsWidth = 0; for (int i=0; ifiltered && colsWidth + fd[i]->getMinimalWidth() <= availWidth) { @@ -285,7 +416,7 @@ bool ThumbBrowserBase::Internal::on_query_tooltip (int x, int y, bool keyboard_t MYREADERLOCK(l, parent->entryRW); #endif - for (size_t i=0; ifd.size(); i++) + for (size_t i=0; ifd.size(); i++) if (parent->fd[i]->drawable && parent->fd[i]->inside (x, y)) { ttip = parent->fd[i]->getToolTip (x, y); break; @@ -383,18 +514,18 @@ void ThumbBrowserBase::buttonPressed (int x, int y, int button, GdkEventType typ } else { // find the start and the end of the selection interval - size_t startx = fd.size()-1; + size_t startx = fd.size()-1; if (lastClicked) { - for (; startx>0; startx--) + for (; startx>0; startx--) if (fd[startx]==lastClicked) break; } else { - for (; startx>0; startx--) + for (; startx>0; startx--) if (fd[startx]==selected[0]) break; } - size_t endx = 0; + size_t endx = 0; for (; endxselected = false; selected.clear (); // select thumbnails in the interval @@ -515,7 +646,7 @@ bool ThumbBrowserBase::Internal::on_button_release_event (GdkEventButton* event) MYREADERLOCK(l, parent->entryRW); #endif - for (size_t i=0; ifd.size(); i++) + for (size_t i=0; ifd.size(); i++) if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h)) { ThumbBrowserEntryBase* tbe = parent->fd[i]; #if PROTECT_VECTORS @@ -539,7 +670,7 @@ bool ThumbBrowserBase::Internal::on_motion_notify_event (GdkEventMotion* event) MYREADERLOCK(l, parent->entryRW); #endif - for (size_t i=0; ifd.size(); i++) + for (size_t i=0; ifd.size(); i++) if (parent->fd[i]->drawable && parent->fd[i]->insideWindow (0, 0, w, h)) { /*#if PROTECT_VECTORS MYREADERLOCK_RELEASE(l); // motionNotify calls the queue, which locks @@ -629,7 +760,7 @@ void ThumbBrowserBase::refreshEditedState (const std::set& efiles MYREADERLOCK(l, entryRW); #endif - for (size_t i=0; iframed = editedFiles.find (fd[i]->filename)!=editedFiles.end(); } @@ -672,11 +803,11 @@ void ThumbBrowserBase::enableTabMode(bool enable) { #endif hscroll.set_value (min(h, hscroll.get_adjustment()->get_upper())); } 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())); } } } @@ -705,7 +836,7 @@ int ThumbBrowserBase::getEffectiveHeight() { #endif // Filtered items do not change in size, so take a non-filtered - for (size_t i=0;ifiltered) { h+=fd[i]->getEffectiveHeight(); break; diff --git a/rtgui/thumbbrowserbase.h b/rtgui/thumbbrowserbase.h index fd1b84712..ba798a34c 100644 --- a/rtgui/thumbbrowserbase.h +++ b/rtgui/thumbbrowserbase.h @@ -78,6 +78,9 @@ class ThumbBrowserBase : public Gtk::VBox { void scroll (int direction); void scrollPage (int direction); + void selectPrev(int distance, bool enlarge); + void selectNext(int distance, bool enlarge); + protected: int eventTime; @@ -88,6 +91,7 @@ class ThumbBrowserBase : public Gtk::VBox { ThumbBrowserEntryBase* lastClicked; int previewHeight; + int numOfCols; Arrangement arrangement; diff --git a/rtgui/thumbbrowserentrybase.h b/rtgui/thumbbrowserentrybase.h index a49860c70..a8ac27935 100644 --- a/rtgui/thumbbrowserentrybase.h +++ b/rtgui/thumbbrowserentrybase.h @@ -110,10 +110,13 @@ protected: int getMinimalHeight () { return height; } int getMinimalWidth () { return width; } + int getEffectiveWidth () const { return exp_width; } int getEffectiveHeight () const { return exp_height; } int getPreviewHeight () const { return preh; } int getStartX () const { return startx; } int getStartY () const { return starty; } + int getX () const { return ofsX+startx; } + int getY () const { return ofsY+starty; } bool inside (int x, int y); bool insideWindow (int x, int y, int w, int h);