Back to index

unity  6.0.0
ResultViewGrid.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright 2011 Canonical Ltd.
00004  *
00005  * This program is free software: you can redistribute it and/or modify it
00006  * under the terms of the GNU Lesser General Public License version 3, as
00007  * published by the  Free Software Foundation.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranties of
00011  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00012  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00013  * License for more details.
00014  *
00015  * You should have received a copy of both the GNU Lesser General Public
00016  * License version 3 along with this program.  If not, see
00017  * <http://www.gnu.org/licenses/>
00018  *
00019  * Authored by: Gordon Allott <gord.allott@canonical.com>
00020  *
00021  */
00022 #include <algorithm>
00023 #include <Nux/Nux.h>
00024 #include <NuxCore/Logger.h>
00025 #include <Nux/VLayout.h>
00026 #include <NuxGraphics/GdkGraphics.h>
00027 #include <gtk/gtk.h>
00028 #include <gdk/gdk.h>
00029 
00030 #include "unity-shared/Timer.h"
00031 #include "unity-shared/ubus-server.h"
00032 #include "unity-shared/UBusMessages.h"
00033 #include "ResultViewGrid.h"
00034 #include "math.h"
00035 
00036 namespace
00037 {
00038 nux::logging::Logger logger("unity.dash.results");
00039 }
00040 
00041 namespace unity
00042 {
00043 namespace dash
00044 {
00045 NUX_IMPLEMENT_OBJECT_TYPE(ResultViewGrid);
00046 
00047 ResultViewGrid::ResultViewGrid(NUX_FILE_LINE_DECL)
00048   : ResultView(NUX_FILE_LINE_PARAM)
00049   , horizontal_spacing(0)
00050   , vertical_spacing(0)
00051   , padding(0)
00052   , mouse_over_index_(-1)
00053   , active_index_(-1)
00054   , selected_index_(-1)
00055   , last_lazy_loaded_result_(0)
00056   , last_mouse_down_x_(-1)
00057   , last_mouse_down_y_(-1)
00058   , recorded_dash_width_(-1)
00059   , recorded_dash_height_(-1)
00060   , mouse_last_x_(-1)
00061   , mouse_last_y_(-1)
00062   , extra_horizontal_spacing_(0)
00063 {
00064   SetAcceptKeyNavFocusOnMouseDown(false);
00065 
00066   auto needredraw_lambda = [&](int value) { NeedRedraw(); };
00067   horizontal_spacing.changed.connect(needredraw_lambda);
00068   vertical_spacing.changed.connect(needredraw_lambda);
00069   padding.changed.connect(needredraw_lambda);
00070   selected_index_.changed.connect(needredraw_lambda);
00071 
00072   key_nav_focus_change.connect(sigc::mem_fun(this, &ResultViewGrid::OnKeyNavFocusChange));
00073   key_nav_focus_activate.connect([&] (nux::Area *area) { UriActivated.emit (focused_uri_); });
00074   key_down.connect(sigc::mem_fun(this, &ResultViewGrid::OnKeyDown));
00075   mouse_move.connect(sigc::mem_fun(this, &ResultViewGrid::MouseMove));
00076   mouse_click.connect(sigc::mem_fun(this, &ResultViewGrid::MouseClick));
00077 
00078   mouse_down.connect([&](int x, int y, unsigned long mouse_state, unsigned long button_state)
00079   {
00080     last_mouse_down_x_ = x;
00081     last_mouse_down_y_ = y;
00082     uint index = GetIndexAtPosition(x, y);
00083     mouse_over_index_ = index;
00084   });
00085 
00086   mouse_leave.connect([&](int x, int y, unsigned long mouse_state, unsigned long button_state)
00087   {
00088     mouse_over_index_ = -1;
00089     mouse_last_x_ = -1;
00090     mouse_last_y_ = -1;
00091     NeedRedraw();
00092   });
00093 
00094   ubus_.RegisterInterest(UBUS_DASH_SIZE_CHANGED, [this] (GVariant* data) {
00095     // on dash size changed, we update our stored values, this sucks
00096     //FIXME in P - make dash size the size of our dash not the entire screen
00097     g_variant_get (data, "(ii)", &recorded_dash_width_, &recorded_dash_height_);
00098   });
00099 
00100   SetDndEnabled(true, false);
00101 }
00102 
00103 void ResultViewGrid::QueueLazyLoad()
00104 {
00105   lazy_load_source_.reset(new glib::Idle(glib::Source::Priority::DEFAULT));
00106   lazy_load_source_->Run(sigc::mem_fun(this, &ResultViewGrid::DoLazyLoad));
00107   last_lazy_loaded_result_ = 0; // we always want to reset the lazy load index here
00108 }
00109 
00110 void ResultViewGrid::QueueViewChanged()
00111 {
00112   if (!view_changed_idle_)
00113   {
00114     // using glib::Source::Priority::HIGH because this needs to happen *before* next draw
00115     view_changed_idle_.reset(new glib::Idle(glib::Source::Priority::HIGH));
00116     view_changed_idle_->Run([&] () {
00117       SizeReallocate();
00118       last_lazy_loaded_result_ = 0; // reset the lazy load index
00119       DoLazyLoad(); // also calls QueueDraw
00120 
00121       view_changed_idle_.reset();
00122       return false;
00123     });
00124   }
00125 }
00126 
00127 bool ResultViewGrid::DoLazyLoad()
00128 {
00129   // FIXME - so this code was nice, it would only load the visible entries on the screen
00130   // however nux does not give us a good enough indicator right now that we are scrolling,
00131   // thus if you scroll more than a screen in one frame, you will end up with at least one frame where
00132   // no icons are displayed (they have not been preloaded yet) - it sucked. we should fix this next cycle when we can break api
00133   //~ int index = 0;
00134 //~
00135   //~ ResultListBounds visible_bounds = GetVisableResults();
00136   //~ int lower_bound = std::get<0>(visible_bounds);
00137   //~ int upper_bound = std::get<1>(visible_bounds);
00138 //~
00139   //~ ResultList::iterator it;
00140   //~ for (it = results_.begin(); it != results_.end(); it++)
00141   //~ {
00142     //~ if (index >= lower_bound && index <= upper_bound)
00143     //~ {
00144       //~ renderer_->Preload((*it));
00145     //~ }
00146     //~ index++;
00147   //~ }
00148 
00149   util::Timer timer;
00150   bool queue_additional_load = false; // if this is set, we will return early and start loading more next frame
00151 
00152   // instead we will just pre-load all the items if expanded or just one row if not
00153   int index = 0;
00154   int items_per_row = GetItemsPerRow();
00155   for (auto it = results_.begin() + last_lazy_loaded_result_; it != results_.end(); it++)
00156   {
00157     if ((!expanded && index < items_per_row) || expanded)
00158     {
00159       renderer_->Preload((*it));
00160       last_lazy_loaded_result_ = index;
00161     }
00162 
00163     if (timer.ElapsedSeconds() > 0.008)
00164     {
00165       queue_additional_load = true;
00166       break;
00167     }
00168 
00169     if (!expanded && index >= items_per_row)
00170       break; //early exit
00171 
00172     index++;
00173   }
00174 
00175   if (queue_additional_load)
00176   {
00177     //we didn't load all the results because we exceeded our time budget, so queue another lazy load
00178     lazy_load_source_.reset(new glib::Timeout(1000/60 - 8));
00179     lazy_load_source_->Run(sigc::mem_fun(this, &ResultViewGrid::DoLazyLoad));
00180   }
00181 
00182   QueueDraw();
00183 
00184   return false;
00185 }
00186 
00187 
00188 int ResultViewGrid::GetItemsPerRow()
00189 {
00190   int items_per_row = (GetGeometry().width - (padding * 2) + horizontal_spacing) / (renderer_->width + horizontal_spacing);
00191   return (items_per_row) ? items_per_row : 1; // always at least one item per row
00192 }
00193 
00194 void ResultViewGrid::SetModelRenderer(ResultRenderer* renderer)
00195 {
00196   ResultView::SetModelRenderer(renderer);
00197   SizeReallocate();
00198 }
00199 
00200 void ResultViewGrid::AddResult(Result& result)
00201 {
00202   results_.push_back(result);
00203   QueueViewChanged();
00204 }
00205 
00206 void ResultViewGrid::RemoveResult(Result& result)
00207 {
00208   ResultView::RemoveResult(result);
00209   QueueViewChanged();
00210 }
00211 
00212 void ResultViewGrid::SizeReallocate()
00213 {
00214   //FIXME - needs to use the geometry assigned to it, but only after a layout
00215   int items_per_row = GetItemsPerRow();
00216 
00217   int total_rows = std::ceil(results_.size() / (double)items_per_row) ;
00218   int total_height = 0;
00219 
00220   if (expanded)
00221   {
00222     total_height = (total_rows * renderer_->height) + (total_rows * vertical_spacing);
00223   }
00224   else
00225   {
00226     total_height = renderer_->height + vertical_spacing;
00227   }
00228 
00229   int width = (items_per_row * renderer_->width) + (padding*2) + ((items_per_row - 1) * horizontal_spacing);
00230   int geo_width = GetBaseWidth();
00231   int extra_width = geo_width - (width + 25-3);
00232 
00233   if (items_per_row != 1)
00234     extra_horizontal_spacing_ = extra_width / (items_per_row - 1);
00235   if (extra_horizontal_spacing_ < 0)
00236     extra_horizontal_spacing_ = 0;
00237 
00238   SetMinimumHeight(total_height + (padding * 2));
00239   SetMaximumHeight(total_height + (padding * 2));
00240 
00241   mouse_over_index_ = GetIndexAtPosition(mouse_last_x_, mouse_last_y_);
00242   results_per_row = items_per_row;
00243 }
00244 
00245 bool ResultViewGrid::InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character)
00246 {
00247   nux::KeyNavDirection direction = nux::KEY_NAV_NONE;
00248   switch (keysym)
00249   {
00250     case NUX_VK_UP:
00251       direction = nux::KeyNavDirection::KEY_NAV_UP;
00252       break;
00253     case NUX_VK_DOWN:
00254       direction = nux::KeyNavDirection::KEY_NAV_DOWN;
00255       break;
00256     case NUX_VK_LEFT:
00257       direction = nux::KeyNavDirection::KEY_NAV_LEFT;
00258       break;
00259     case NUX_VK_RIGHT:
00260       direction = nux::KeyNavDirection::KEY_NAV_RIGHT;
00261       break;
00262     case NUX_VK_LEFT_TAB:
00263       direction = nux::KeyNavDirection::KEY_NAV_TAB_PREVIOUS;
00264       break;
00265     case NUX_VK_TAB:
00266       direction = nux::KeyNavDirection::KEY_NAV_TAB_NEXT;
00267       break;
00268     case NUX_VK_ENTER:
00269     case NUX_KP_ENTER:
00270       direction = nux::KeyNavDirection::KEY_NAV_ENTER;
00271       break;
00272     default:
00273       direction = nux::KeyNavDirection::KEY_NAV_NONE;
00274       break;
00275   }
00276 
00277   if (direction == nux::KeyNavDirection::KEY_NAV_NONE
00278       || direction == nux::KeyNavDirection::KEY_NAV_TAB_NEXT
00279       || direction == nux::KeyNavDirection::KEY_NAV_TAB_PREVIOUS
00280       || direction == nux::KeyNavDirection::KEY_NAV_ENTER)
00281   {
00282     // we don't handle these cases
00283     return false;
00284   }
00285 
00286   int items_per_row = GetItemsPerRow();
00287   int total_rows = std::ceil(results_.size() / static_cast<float>(items_per_row)); // items per row is always at least 1
00288   total_rows = (expanded) ? total_rows : 1; // restrict to one row if not expanded
00289 
00290    // check for edge cases where we want the keynav to bubble up
00291   if (direction == nux::KEY_NAV_LEFT && (selected_index_ % items_per_row == 0))
00292     return false; // pressed left on the first item, no diiice
00293   else if (direction == nux::KEY_NAV_RIGHT && (selected_index_ == static_cast<int>(results_.size() - 1)))
00294     return false; // pressed right on the last item, nope. nothing for you
00295   else if (direction == nux::KEY_NAV_RIGHT  && (selected_index_ % items_per_row) == (items_per_row - 1))
00296     return false; // pressed right on the last item in the first row in non expanded mode. nothing doing.
00297   else if (direction == nux::KEY_NAV_UP && selected_index_ < items_per_row)
00298     return false; // key nav up when already on top row
00299   else if (direction == nux::KEY_NAV_DOWN && selected_index_ >= (total_rows-1) * items_per_row)
00300     return false; // key nav down when on bottom row
00301 
00302   return true;
00303 }
00304 
00305 bool ResultViewGrid::AcceptKeyNavFocus()
00306 {
00307   return true;
00308 }
00309 
00310 void ResultViewGrid::OnKeyDown (unsigned long event_type, unsigned long event_keysym,
00311                                 unsigned long event_state, const TCHAR* character,
00312                                 unsigned short key_repeat_count)
00313 {
00314   nux::KeyNavDirection direction = nux::KEY_NAV_NONE;
00315   switch (event_keysym)
00316   {
00317     case NUX_VK_UP:
00318       direction = nux::KeyNavDirection::KEY_NAV_UP;
00319       break;
00320     case NUX_VK_DOWN:
00321       direction = nux::KeyNavDirection::KEY_NAV_DOWN;
00322       break;
00323     case NUX_VK_LEFT:
00324       direction = nux::KeyNavDirection::KEY_NAV_LEFT;
00325       break;
00326     case NUX_VK_RIGHT:
00327       direction = nux::KeyNavDirection::KEY_NAV_RIGHT;
00328       break;
00329     case NUX_VK_LEFT_TAB:
00330       direction = nux::KeyNavDirection::KEY_NAV_TAB_PREVIOUS;
00331       break;
00332     case NUX_VK_TAB:
00333       direction = nux::KeyNavDirection::KEY_NAV_TAB_NEXT;
00334       break;
00335     case NUX_VK_ENTER:
00336     case NUX_KP_ENTER:
00337       direction = nux::KeyNavDirection::KEY_NAV_ENTER;
00338       break;
00339     default:
00340       direction = nux::KeyNavDirection::KEY_NAV_NONE;
00341       break;
00342   }
00343 
00344   // if we got this far, we definately got a keynav signal
00345 
00346   ResultList::iterator current_focused_result = results_.end();
00347   if (focused_uri_.empty())
00348     focused_uri_ = results_.front().uri;
00349 
00350   std::string next_focused_uri;
00351   ResultList::iterator it;
00352   int items_per_row = GetItemsPerRow();
00353   int total_rows = std::ceil(results_.size() / static_cast<float>(items_per_row)); // items per row is always at least 1
00354   total_rows = (expanded) ? total_rows : 1; // restrict to one row if not expanded
00355 
00356   // find the currently focused item
00357   for (it = results_.begin(); it != results_.end(); it++)
00358   {
00359     std::string result_uri = (*it).uri;
00360     if (result_uri == focused_uri_)
00361     {
00362       current_focused_result = it;
00363       break;
00364     }
00365   }
00366 
00367   if (direction == nux::KEY_NAV_LEFT && (selected_index_ == 0))
00368     return; // pressed left on the first item, no diiice
00369 
00370   if (direction == nux::KEY_NAV_RIGHT && (selected_index_ == static_cast<int>(results_.size() - 1)))
00371     return; // pressed right on the last item, nope. nothing for you
00372 
00373   if (direction == nux::KEY_NAV_RIGHT && !expanded && selected_index_ == items_per_row - 1)
00374     return; // pressed right on the last item in the first row in non expanded mode. nothing doing.
00375 
00376   switch (direction)
00377   {
00378     case (nux::KEY_NAV_LEFT):
00379     {
00380       selected_index_ = selected_index_ - 1;
00381       break;
00382     }
00383     case (nux::KEY_NAV_RIGHT):
00384     {
00385      selected_index_ = selected_index_ + 1;
00386       break;
00387     }
00388     case (nux::KEY_NAV_UP):
00389     {
00390       selected_index_ = selected_index_ - items_per_row;
00391       break;
00392     }
00393     case (nux::KEY_NAV_DOWN):
00394     {
00395       selected_index_ = selected_index_ + items_per_row;
00396       break;
00397     }
00398     default:
00399       break;
00400   }
00401 
00402   selected_index_ = std::max(0, selected_index_());
00403   selected_index_ = std::min(static_cast<int>(results_.size() - 1), selected_index_());
00404   focused_uri_ = results_[selected_index_].uri;
00405 
00406   int focused_x = (renderer_->width + horizontal_spacing + extra_horizontal_spacing_) * (selected_index_ % items_per_row);
00407   int focused_y = (renderer_->height + vertical_spacing) * (selected_index_ / items_per_row);
00408 
00409   ubus_.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED,
00410                     g_variant_new("(iiii)", focused_x, focused_y, renderer_->width(), renderer_->height()));
00411   selection_change.emit();
00412 }
00413 
00414 nux::Area* ResultViewGrid::KeyNavIteration(nux::KeyNavDirection direction)
00415 {
00416   return this;
00417 }
00418 
00419 void ResultViewGrid::OnKeyNavFocusChange(nux::Area *area, bool has_focus, nux::KeyNavDirection direction)
00420 {
00421   if (HasKeyFocus())
00422   {
00423     if (selected_index_ < 0 and !results_.empty())
00424     {
00425         focused_uri_ = results_.front().uri;
00426         selected_index_ = 0;
00427     }
00428 
00429     int focused_x = 0;
00430     int focused_y = 0;
00431 
00432     int items_per_row = GetItemsPerRow();
00433 
00434     if (direction == nux::KEY_NAV_UP && expanded)
00435     {
00436       // This View just got focused through keyboard navigation and the
00437       // focus is comming from the bottom. We want to focus the
00438       // first item (on the left) of the last row in this grid.
00439 
00440       int total_rows = std::ceil(results_.size() / (double)items_per_row);
00441       selected_index_ = items_per_row * (total_rows-1);
00442 
00443       focused_x = (renderer_->width + horizontal_spacing + extra_horizontal_spacing_) * (selected_index_ % items_per_row);
00444       focused_y = (renderer_->height + vertical_spacing) * (selected_index_ / items_per_row);
00445 
00446     }
00447     else
00448     {
00449       focused_x = (renderer_->width + horizontal_spacing + extra_horizontal_spacing_) * (selected_index_ % items_per_row);
00450       focused_y = (renderer_->height + vertical_spacing) * (selected_index_ / items_per_row);
00451     }
00452 
00453     if (direction != nux::KEY_NAV_NONE)
00454     {
00455       ubus_.SendMessage(UBUS_RESULT_VIEW_KEYNAV_CHANGED,
00456                         g_variant_new("(iiii)", focused_x, focused_y, renderer_->width(), renderer_->height()));
00457     }
00458 
00459     selection_change.emit();
00460 
00461   }
00462   else
00463   {
00464     selected_index_ = -1;
00465     focused_uri_.clear();
00466 
00467     selection_change.emit();
00468   }
00469 }
00470 
00471 long ResultViewGrid::ComputeContentSize()
00472 {
00473   SizeReallocate();
00474   QueueLazyLoad();
00475 
00476   return ResultView::ComputeContentSize();
00477 }
00478 
00479 
00480 typedef std::tuple <int, int> ResultListBounds;
00481 ResultListBounds ResultViewGrid::GetVisableResults()
00482 {
00483   int items_per_row = GetItemsPerRow();
00484   int start, end;
00485 
00486   if (!expanded)
00487   {
00488     // we are not expanded, so the bounds are known
00489     start = 0;
00490     end = items_per_row - 1;
00491   }
00492   else
00493   {
00494     //find the row we start at
00495     int absolute_y = GetAbsoluteY() - GetToplevel()->GetAbsoluteY();
00496     uint row_size = renderer_->height + vertical_spacing;
00497 
00498     if (absolute_y < 0)
00499     {
00500       // we are scrolled up a little,
00501       int row_index = abs(absolute_y) / row_size;
00502       start = row_index * items_per_row;
00503     }
00504     else
00505     {
00506       start = 0;
00507     }
00508 
00509     if (absolute_y + GetAbsoluteHeight() > GetToplevel()->GetAbsoluteHeight())
00510     {
00511       // our elements overflow the visable viewport
00512       int visible_height = (GetToplevel()->GetAbsoluteHeight() - std::max(absolute_y, 0));
00513       visible_height = std::min(visible_height, absolute_y + GetAbsoluteHeight());
00514 
00515       int visible_rows = std::ceil(visible_height / static_cast<float>(row_size));
00516       end = start + (visible_rows * items_per_row) + items_per_row;
00517     }
00518     else
00519     {
00520       end = results_.size() - 1;
00521     }
00522   }
00523 
00524   start = std::max(start, 0);
00525   end = std::min(end, static_cast<int>(results_.size()) - 1);
00526 
00527   return ResultListBounds(start, end);
00528 }
00529 
00530 void ResultViewGrid::Draw(nux::GraphicsEngine& GfxContext, bool force_draw)
00531 {
00532   int items_per_row = GetItemsPerRow();
00533   uint total_rows = (!expanded) ? 0 : (results_.size() / items_per_row) + 1;
00534 
00535   ResultView::ResultList::iterator it;
00536 
00537   //find the row we start at
00538   int absolute_y = GetAbsoluteY();
00539   uint row_size = renderer_->height + vertical_spacing;
00540 
00541   int y_position = padding + GetGeometry().y;
00542 
00543   ResultListBounds visible_bounds = GetVisableResults();
00544 
00545   for (uint row_index = 0; row_index <= total_rows; row_index++)
00546   {
00547     int row_lower_bound = row_index * items_per_row;
00548     if (row_lower_bound >= std::get<0>(visible_bounds) &&
00549         row_lower_bound <= std::get<1>(visible_bounds))
00550     {
00551       int x_position = padding + GetGeometry().x;
00552       for (int column_index = 0; column_index < items_per_row; column_index++)
00553       {
00554         uint index = (row_index * items_per_row) + column_index;
00555         if (index >= results_.size())
00556           break;
00557 
00558         ResultRenderer::ResultRendererState state = ResultRenderer::RESULT_RENDERER_NORMAL;
00559         if ((int)(index) == selected_index_)
00560         {
00561           state = ResultRenderer::RESULT_RENDERER_SELECTED;
00562         }
00563         else if ((int)(index) == active_index_)
00564         {
00565           state = ResultRenderer::RESULT_RENDERER_ACTIVE;
00566         }
00567 
00568         int half_width = recorded_dash_width_ / 2;
00569         int half_height = recorded_dash_height_;
00570 
00571         int offset_x, offset_y;
00572 
00573         /* Guard against divide-by-zero. SIGFPEs are not mythological
00574          * contrary to popular belief */
00575         if (half_width >= 10)
00576           offset_x = MAX(MIN((x_position - half_width) / (half_width / 10), 5), -5);
00577         else
00578           offset_x = 0;
00579 
00580         if (half_height >= 10)
00581           offset_y = MAX(MIN(((y_position + absolute_y) - half_height) / (half_height / 10), 5), -5);
00582         else
00583           offset_y = 0;
00584 
00585         if (recorded_dash_width_ < 1 || recorded_dash_height_ < 1)
00586         {
00587           offset_x = 0;
00588           offset_y = 0;
00589         }
00590         nux::Geometry render_geo(x_position, y_position, renderer_->width, renderer_->height);
00591 //nux::GetPainter().Paint2DQuadColor(GfxContext, render_geo, nux::color::Blue*0.20);
00592         renderer_->Render(GfxContext, results_[index], state, render_geo, offset_x, offset_y);
00593 
00594         x_position += renderer_->width + horizontal_spacing + extra_horizontal_spacing_;
00595       }
00596     }
00597 
00598     y_position += row_size;
00599   }
00600 }
00601 
00602 void ResultViewGrid::DrawContent(nux::GraphicsEngine& GfxContent, bool force_draw)
00603 {
00604   nux::Geometry base = GetGeometry();
00605   GfxContent.PushClippingRectangle(base);
00606 
00607   if (GetCompositionLayout())
00608   {
00609     nux::Geometry geo = GetCompositionLayout()->GetGeometry();
00610     GetCompositionLayout()->ProcessDraw(GfxContent, force_draw);
00611   }
00612 
00613   GfxContent.PopClippingRectangle();
00614 }
00615 
00616 
00617 void ResultViewGrid::MouseMove(int x, int y, int dx, int dy, unsigned long button_flags, unsigned long key_flags)
00618 {
00619   uint index = GetIndexAtPosition(x, y);
00620 
00621   if (mouse_over_index_ != index)
00622   {
00623     selected_index_ = mouse_over_index_ = index;
00624 
00625     nux::GetWindowCompositor().SetKeyFocusArea(this);
00626   }
00627   mouse_last_x_ = x;
00628   mouse_last_y_ = y;
00629 }
00630 
00631 void ResultViewGrid::MouseClick(int x, int y, unsigned long button_flags, unsigned long key_flags)
00632 {
00633   uint index = GetIndexAtPosition(x, y);
00634   mouse_over_index_ = index;
00635   if (index >= 0 && index < results_.size())
00636   {
00637     // we got a click on a button so activate it
00638     Result result = results_[index];
00639     selected_index_ = index;
00640     focused_uri_ = result.uri;
00641     UriActivated.emit(result.uri);
00642   }
00643 }
00644 
00645 uint ResultViewGrid::GetIndexAtPosition(int x, int y)
00646 {
00647   uint items_per_row = GetItemsPerRow();
00648 
00649   uint column_size = renderer_->width + horizontal_spacing + extra_horizontal_spacing_;
00650   uint row_size = renderer_->height + vertical_spacing;
00651 
00652   int x_bound = items_per_row * column_size + padding;
00653 
00654   if ((x < padding) || (x >= x_bound))
00655     return -1;
00656 
00657   if (y < padding)
00658     return -1;
00659 
00660   uint row_number = std::max((y - padding), 0) / row_size ;
00661   uint column_number = std::max((x - padding), 0) / column_size;
00662 
00663   return (row_number * items_per_row) + column_number;
00664 }
00665 
00666 /* --------
00667    DND code
00668    --------
00669 */
00670 bool
00671 ResultViewGrid::DndSourceDragBegin()
00672 {
00673 
00674   uint drag_index = GetIndexAtPosition(last_mouse_down_x_, last_mouse_down_y_);
00675 
00676   if (drag_index >= results_.size())
00677     return false;
00678 
00679   Reference();
00680 
00681   Result drag_result = results_[drag_index];
00682 
00683   current_drag_uri_ = drag_result.dnd_uri;
00684   if (current_drag_uri_ == "")
00685     current_drag_uri_ = drag_result.uri().substr(drag_result.uri().find(":") + 1);
00686 
00687   current_drag_icon_name_ = drag_result.icon_hint;
00688 
00689   LOG_DEBUG (logger) << "Dnd begin at " <<
00690                      last_mouse_down_x_ << ", " << last_mouse_down_y_ << " - using; "
00691                      << current_drag_uri_ << " - "
00692                      << current_drag_icon_name_;
00693 
00694   return true;
00695 }
00696 
00697 GdkPixbuf *
00698 _icon_hint_get_drag_pixbuf (std::string icon_hint)
00699 {
00700   GdkPixbuf *pbuf;
00701   GtkIconTheme *theme;
00702   GtkIconInfo *info;
00703   GError *error = NULL;
00704   GIcon *icon;
00705   int size = 64;
00706   if (icon_hint.empty())
00707     icon_hint = "application-default-icon";
00708   if (g_str_has_prefix(icon_hint.c_str(), "/"))
00709   {
00710     pbuf = gdk_pixbuf_new_from_file_at_scale (icon_hint.c_str(),
00711                                               size, size, FALSE, &error);
00712     if (error != NULL || !pbuf || !GDK_IS_PIXBUF (pbuf))
00713     {
00714       icon_hint = "application-default-icon";
00715       g_error_free (error);
00716       error = NULL;
00717     }
00718     else
00719       return pbuf;
00720   }
00721   theme = gtk_icon_theme_get_default();
00722   icon = g_icon_new_for_string(icon_hint.c_str(), NULL);
00723 
00724   if (G_IS_ICON(icon))
00725   {
00726      info = gtk_icon_theme_lookup_by_gicon(theme, icon, size, (GtkIconLookupFlags)0);
00727       g_object_unref(icon);
00728   }
00729   else
00730   {
00731      info = gtk_icon_theme_lookup_icon(theme,
00732                                         icon_hint.c_str(),
00733                                         size,
00734                                         (GtkIconLookupFlags) 0);
00735   }
00736 
00737   if (!info)
00738   {
00739       info = gtk_icon_theme_lookup_icon(theme,
00740                                         "application-default-icon",
00741                                         size,
00742                                         (GtkIconLookupFlags) 0);
00743   }
00744 
00745   if (gtk_icon_info_get_filename(info) == NULL)
00746   {
00747       gtk_icon_info_free(info);
00748       info = gtk_icon_theme_lookup_icon(theme,
00749                                         "application-default-icon",
00750                                         size,
00751                                         (GtkIconLookupFlags) 0);
00752   }
00753 
00754   pbuf = gtk_icon_info_load_icon(info, &error);
00755 
00756   if (error != NULL)
00757   {
00758     LOG_WARN (logger) << "could not find a pixbuf for " << icon_hint;
00759     g_error_free (error);
00760     pbuf = NULL;
00761   }
00762 
00763   gtk_icon_info_free(info);
00764   return pbuf;
00765 }
00766 
00767 nux::NBitmapData*
00768 ResultViewGrid::DndSourceGetDragImage()
00769 {
00770   nux::NBitmapData* result = 0;
00771   GdkPixbuf* pbuf;
00772   pbuf = _icon_hint_get_drag_pixbuf (current_drag_icon_name_);
00773 
00774   if (pbuf && GDK_IS_PIXBUF(pbuf))
00775   {
00776     // we don't free the pbuf as GdkGraphics will do it for us will do it for us
00777     nux::GdkGraphics graphics(pbuf);
00778     result = graphics.GetBitmap();
00779   }
00780 
00781   return result;
00782 }
00783 
00784 std::list<const char*>
00785 ResultViewGrid::DndSourceGetDragTypes()
00786 {
00787   std::list<const char*> result;
00788   result.push_back("text/uri-list");
00789   return result;
00790 }
00791 
00792 const char*
00793 ResultViewGrid::DndSourceGetDataForType(const char* type, int* size, int* format)
00794 {
00795   *format = 8;
00796 
00797   if (!current_drag_uri_.empty())
00798   {
00799     *size = strlen(current_drag_uri_.c_str());
00800     return current_drag_uri_.c_str();
00801   }
00802   else
00803   {
00804     *size = 0;
00805     return 0;
00806   }
00807 }
00808 
00809 void
00810 ResultViewGrid::DndSourceDragFinished(nux::DndAction result)
00811 {
00812   UnReference();
00813   last_mouse_down_x_ = -1;
00814   last_mouse_down_y_ = -1;
00815   current_drag_uri_.clear();
00816   current_drag_icon_name_.clear();
00817 
00818   // We need this because the drag can start in a ResultViewGrid and can
00819   // end in another ResultViewGrid
00820   EmitMouseLeaveSignal(0, 0, 0, 0);
00821 
00822   // We need an extra mouse motion to highlight the icon under the mouse
00823   // as soon as dnd finish
00824   Display* display = nux::GetGraphicsDisplay()->GetX11Display();
00825   if (display)
00826   {
00827     XWarpPointer(display, None, None, 0, 0, 0, 0, 0, 0);
00828     XSync(display, 0);
00829   }
00830 }
00831 
00832 int
00833 ResultViewGrid::GetSelectedIndex()
00834 {
00835   return selected_index_;
00836 }
00837 
00838 }
00839 }