Back to index

unity  6.0.0
DashView.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
00017  */
00018 
00019 #include "DashView.h"
00020 #include "DashViewPrivate.h"
00021 
00022 #include <math.h>
00023 
00024 #include <gio/gdesktopappinfo.h>
00025 #include <glib/gi18n-lib.h>
00026 #include <gtk/gtk.h>
00027 
00028 #include <NuxCore/Logger.h>
00029 #include <UnityCore/GLibWrapper.h>
00030 #include <UnityCore/RadioOptionFilter.h>
00031 
00032 #include "unity-shared/DashStyle.h"
00033 #include "unity-shared/KeyboardUtil.h"
00034 #include "unity-shared/UnitySettings.h"
00035 #include "unity-shared/UBusMessages.h"
00036 
00037 namespace unity
00038 {
00039 namespace dash
00040 {
00041 namespace
00042 {
00043 
00044 nux::logging::Logger logger("unity.dash.view");
00045 
00046 }
00047 
00048 // This is so we can access some protected members in nux::VLayout and
00049 // break the natural key navigation path.
00050 class DashLayout: public nux::VLayout
00051 {
00052 public:
00053   DashLayout(NUX_FILE_LINE_DECL)
00054     : nux::VLayout(NUX_FILE_LINE_PARAM)
00055     , area_(nullptr)
00056   {}
00057 
00058   void SetSpecialArea(nux::Area* area)
00059   {
00060     area_ = area;
00061   }
00062 
00063 protected:
00064   nux::Area* KeyNavIteration(nux::KeyNavDirection direction)
00065   {
00066     if (direction == nux::KEY_NAV_DOWN && area_ &&  area_->HasKeyFocus())
00067       return nullptr;
00068     else
00069       return nux::VLayout::KeyNavIteration(direction);
00070   }
00071 
00072 private:
00073   nux::Area* area_;
00074 };
00075 
00076 NUX_IMPLEMENT_OBJECT_TYPE(DashView);
00077 
00078 DashView::DashView()
00079   : nux::View(NUX_TRACKER_LOCATION)
00080   , home_lens_(new HomeLens(_("Home"), _("Home screen"), _("Search")))
00081   , active_lens_view_(0)
00082   , last_activated_uri_("")
00083   , search_in_progress_(false)
00084   , activate_on_finish_(false)
00085   , visible_(false)
00086 {
00087   renderer_.SetOwner(this);
00088   renderer_.need_redraw.connect([this] () {
00089     QueueDraw();
00090   });
00091 
00092   SetupViews();
00093   SetupUBusConnections();
00094 
00095   Settings::Instance().changed.connect(sigc::mem_fun(this, &DashView::Relayout));
00096   lenses_.lens_added.connect(sigc::mem_fun(this, &DashView::OnLensAdded));
00097   mouse_down.connect(sigc::mem_fun(this, &DashView::OnMouseButtonDown));
00098 
00099   Relayout();
00100 
00101   home_lens_->AddLenses(lenses_);
00102   home_lens_->search_finished.connect(sigc::mem_fun(this, &DashView::OnGlobalSearchFinished));
00103   lens_bar_->Activate("home.lens");
00104 }
00105 
00106 DashView::~DashView()
00107 {
00108   // Do this explicitely, otherwise dee will complain about invalid access
00109   // to the lens models
00110   RemoveLayout();
00111 }
00112 
00113 void DashView::SetMonitorOffset(int x, int y)
00114 {
00115   renderer_.x_offset = x;
00116   renderer_.y_offset = y;
00117 }
00118 
00119 void DashView::AboutToShow()
00120 {
00121   ubus_manager_.SendMessage(UBUS_BACKGROUND_REQUEST_COLOUR_EMIT);
00122   visible_ = true;
00123   search_bar_->text_entry()->SelectAll();
00124 
00125   /* Give the lenses a chance to prep data before we map them  */
00126   lens_bar_->Activate(active_lens_view_->lens()->id());
00127   if (active_lens_view_->lens()->id() == "home.lens")
00128   {
00129     for (auto lens : lenses_.GetLenses())
00130       {
00131         lens->view_type = ViewType::HOME_VIEW;
00132         LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HOME_VIEW
00133                               << " on '" << lens->id() << "'";
00134       }
00135 
00136       home_lens_->view_type = ViewType::LENS_VIEW;
00137       LOG_DEBUG(logger) << "Setting ViewType " << ViewType::LENS_VIEW
00138                                 << " on '" << home_lens_->id() << "'";
00139   }
00140   else if (active_lens_view_)
00141   {
00142     // careful here, the lens_view's view_type doesn't get reset when the dash
00143     // hides, but lens' view_type does, so we need to update the lens directly
00144     active_lens_view_->lens()->view_type = ViewType::LENS_VIEW;
00145   }
00146 
00147   // this will make sure the spinner animates if the search takes a while
00148   search_bar_->ForceSearchChanged();
00149 
00150   renderer_.AboutToShow();
00151 }
00152 
00153 void DashView::AboutToHide()
00154 {
00155   visible_ = false;
00156   renderer_.AboutToHide();
00157 
00158   for (auto lens : lenses_.GetLenses())
00159   {
00160     lens->view_type = ViewType::HIDDEN;
00161     LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HIDDEN
00162                           << " on '" << lens->id() << "'";
00163   }
00164 
00165   home_lens_->view_type = ViewType::HIDDEN;
00166   LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HIDDEN
00167                             << " on '" << home_lens_->id() << "'";
00168 }
00169 
00170 void DashView::SetupViews()
00171 {
00172   dash::Style& style = dash::Style::Instance();
00173 
00174   layout_ = new nux::VLayout();
00175   layout_->SetLeftAndRightPadding(style.GetVSeparatorSize(), 0);
00176   layout_->SetTopAndBottomPadding(style.GetHSeparatorSize(), 0);
00177   SetLayout(layout_);
00178 
00179   content_layout_ = new DashLayout(NUX_TRACKER_LOCATION);
00180   content_layout_->SetTopAndBottomPadding(style.GetDashViewTopPadding(), 0);
00181   layout_->AddLayout(content_layout_, 1, nux::MINOR_POSITION_LEFT, nux::MINOR_SIZE_FULL);
00182 
00183   search_bar_layout_ = new nux::HLayout();
00184   search_bar_layout_->SetLeftAndRightPadding(style.GetSearchBarLeftPadding(), 0);
00185   content_layout_->AddLayout(search_bar_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);
00186 
00187   search_bar_ = new SearchBar();
00188   AddChild(search_bar_);
00189   search_bar_->SetMinimumHeight(style.GetSearchBarHeight());
00190   search_bar_->SetMaximumHeight(style.GetSearchBarHeight());
00191   search_bar_->activated.connect(sigc::mem_fun(this, &DashView::OnEntryActivated));
00192   search_bar_->search_changed.connect(sigc::mem_fun(this, &DashView::OnSearchChanged));
00193   search_bar_->live_search_reached.connect(sigc::mem_fun(this, &DashView::OnLiveSearchReached));
00194   search_bar_->showing_filters.changed.connect([&] (bool showing) { if (active_lens_view_) active_lens_view_->filters_expanded = showing; QueueDraw(); });
00195   search_bar_layout_->AddView(search_bar_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);
00196   content_layout_->SetSpecialArea(search_bar_->show_filters());
00197 
00198   lenses_layout_ = new nux::VLayout();
00199   content_layout_->AddView(lenses_layout_, 1, nux::MINOR_POSITION_LEFT);
00200 
00201   home_view_ = new LensView(home_lens_, nullptr);
00202   AddChild(home_view_);
00203   active_lens_view_ = home_view_;
00204   lens_views_[home_lens_->id] = home_view_;
00205   lenses_layout_->AddView(home_view_);
00206 
00207   lens_bar_ = new LensBar();
00208   AddChild(lens_bar_);
00209   lens_bar_->lens_activated.connect(sigc::mem_fun(this, &DashView::OnLensBarActivated));
00210   content_layout_->AddView(lens_bar_, 0, nux::MINOR_POSITION_CENTER);
00211 }
00212 
00213 void DashView::SetupUBusConnections()
00214 {
00215   ubus_manager_.RegisterInterest(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST,
00216       sigc::mem_fun(this, &DashView::OnActivateRequest));
00217 }
00218 
00219 long DashView::PostLayoutManagement (long LayoutResult)
00220 {
00221   Relayout();
00222   return LayoutResult;
00223 }
00224 
00225 void DashView::Relayout()
00226 {
00227   nux::Geometry const& geo = GetGeometry();
00228   content_geo_ = GetBestFitGeometry(geo);
00229   dash::Style& style = dash::Style::Instance();
00230   
00231   if (style.always_maximised)
00232   {
00233     if (geo.width >= content_geo_.width && geo.height > content_geo_.height)
00234       content_geo_ = geo;
00235   }
00236 
00237 
00238   // kinda hacky, but it makes sure the content isn't so big that it throws
00239   // the bottom of the dash off the screen
00240   // not hugely happy with this, so FIXME
00241   lenses_layout_->SetMaximumHeight (content_geo_.height - search_bar_->GetGeometry().height - lens_bar_->GetGeometry().height - style.GetDashViewTopPadding());
00242   lenses_layout_->SetMinimumHeight (content_geo_.height - search_bar_->GetGeometry().height - lens_bar_->GetGeometry().height - style.GetDashViewTopPadding());
00243 
00244   layout_->SetMinMaxSize(content_geo_.width, content_geo_.height);
00245 
00246   // Minus the padding that gets added to the left
00247   float tile_width = style.GetTileWidth();
00248   style.SetDefaultNColumns(floorf((content_geo_.width - 32) / tile_width));
00249 
00250   ubus_manager_.SendMessage(UBUS_DASH_SIZE_CHANGED, g_variant_new("(ii)", content_geo_.width, content_geo_.height));
00251 
00252   QueueDraw();
00253 }
00254 
00255 // Gives us the width and height of the contents that will give us the best "fit",
00256 // which means that the icons/views will not have unnecessary padding, everything will
00257 // look tight
00258 nux::Geometry DashView::GetBestFitGeometry(nux::Geometry const& for_geo)
00259 {
00260   dash::Style& style = dash::Style::Instance();
00261 
00262   int width = 0, height = 0;
00263   int tile_width = style.GetTileWidth();
00264   int tile_height = style.GetTileHeight();
00265   int half = for_geo.width / 2;
00266 
00267   // if default dash size is bigger than half a screens worth of items, go for that.
00268   while ((width += tile_width) + (19 * 2) < half)
00269     ;
00270 
00271   width = MAX(width, tile_width * 6);
00272 
00273   width += 20 + 40; // add the left padding and the group plugin padding
00274 
00275   height = search_bar_->GetGeometry().height;
00276   height += tile_height * 3;
00277   height += (style.GetPlacesGroupTopSpace() - 2 + 24 + 8) * 3; // adding three group headers
00278   height += 1*2; // hseparator height
00279   height += style.GetDashViewTopPadding();
00280   height += lens_bar_->GetGeometry().height;
00281 
00282   if (for_geo.width > 800 && for_geo.height > 550)
00283   {
00284     width = MIN(width, for_geo.width-66);
00285     height = MIN(height, for_geo.height-24);
00286   }
00287 
00288   return nux::Geometry(0, 0, width, height);
00289 }
00290 
00291 void DashView::Draw(nux::GraphicsEngine& gfx_context, bool force_draw)
00292 {
00293   renderer_.DrawFull(gfx_context, content_geo_, GetAbsoluteGeometry(), GetGeometry());
00294 }
00295 
00296 void DashView::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw)
00297 {
00298   renderer_.DrawInner(gfx_context, content_geo_, GetAbsoluteGeometry(), GetGeometry());
00299 
00300   if (IsFullRedraw())
00301   {
00302     nux::GetPainter().PushBackgroundStack();
00303     layout_->ProcessDraw(gfx_context, force_draw);
00304     nux::GetPainter().PopBackgroundStack();
00305   }
00306   else
00307   {
00308     layout_->ProcessDraw(gfx_context, force_draw);
00309   }
00310 
00311   renderer_.DrawInnerCleanup(gfx_context, content_geo_, GetAbsoluteGeometry(), GetGeometry());
00312 }
00313 
00314 void DashView::OnMouseButtonDown(int x, int y, unsigned long button, unsigned long key)
00315 {
00316   dash::Style& style = dash::Style::Instance();
00317   nux::Geometry geo(content_geo_);
00318 
00319   if (Settings::Instance().GetFormFactor() == FormFactor::DESKTOP)
00320   {
00321     geo.width += style.GetDashRightTileWidth();
00322     geo.height += style.GetDashBottomTileHeight();
00323   }
00324 
00325   if (!geo.IsPointInside(x, y))
00326   {
00327     ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00328   }
00329 }
00330 
00331 void DashView::OnActivateRequest(GVariant* args)
00332 {
00333   glib::String uri;
00334   glib::String search_string;
00335   dash::HandledType handled_type;
00336 
00337   g_variant_get(args, "(sus)", &uri, &handled_type, &search_string);
00338 
00339   std::string id(AnalyseLensURI(uri.Str()));
00340 
00341   if (!visible_)
00342   {
00343     lens_bar_->Activate(id);
00344     ubus_manager_.SendMessage(UBUS_DASH_EXTERNAL_ACTIVATION);
00345   }
00346   else if (/* visible_ && */ handled_type == NOT_HANDLED)
00347   {
00348     ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00349   }
00350   else if (/* visible_ && */ handled_type == GOTO_DASH_URI)
00351   {
00352     lens_bar_->Activate(id);
00353   }
00354 }
00355 
00356 std::string DashView::AnalyseLensURI(std::string const& uri)
00357 {
00358   impl::LensFilter filter = impl::parse_lens_uri(uri);
00359 
00360   if (!filter.filters.empty())
00361   {
00362     lens_views_[filter.id]->filters_expanded = true;
00363     // update the lens for each filter
00364     for (auto p : filter.filters) {
00365       UpdateLensFilter(filter.id, p.first, p.second);
00366     }
00367   }
00368 
00369   return filter.id;
00370 }
00371 
00372 void DashView::UpdateLensFilter(std::string lens_id, std::string filter_name, std::string value)
00373 {
00374   if (lenses_.GetLens(lens_id))
00375   {
00376     Lens::Ptr lens = lenses_.GetLens(lens_id);
00377 
00378     Filters::Ptr filters = lens->filters;
00379 
00380     for (unsigned int i = 0; i < filters->count(); ++i)
00381     {
00382       Filter::Ptr filter = filters->FilterAtIndex(i);
00383 
00384       if (filter->id() == filter_name)
00385       {
00386         UpdateLensFilterValue(filter, value);
00387       }
00388     }
00389   }
00390 }
00391 
00392 void DashView::UpdateLensFilterValue(Filter::Ptr filter, std::string value)
00393 {
00394   if (filter->renderer_name == "filter-radiooption")
00395   {
00396     RadioOptionFilter::Ptr radio = std::static_pointer_cast<RadioOptionFilter>(filter);
00397     for (auto option: radio->options())
00398     {
00399       if (option->id == value)
00400         option->active = true;
00401     }
00402   }
00403 }
00404 
00405 void DashView::OnSearchChanged(std::string const& search_string)
00406 {
00407   LOG_DEBUG(logger) << "Search changed: " << search_string;
00408   if (active_lens_view_)
00409   {
00410     search_in_progress_ = true;
00411     // it isn't guaranteed that we get a SearchFinished signal, so we need
00412     // to make sure this isn't set even though we aren't doing any search
00413     // 250ms for the Search method call, rest for the actual search
00414     searching_timeout_.reset(new glib::Timeout(500, [&] () {
00415       search_in_progress_ = false;
00416       activate_on_finish_ = false;
00417       return false;
00418     }));
00419 
00420     // 150ms to hide the no reults message if its take a while to return results
00421     hide_message_delay_.reset(new glib::Timeout(150, [&] () {
00422       active_lens_view_->HideResultsMessage();
00423       return false;
00424     }));
00425   }
00426 }
00427 
00428 void DashView::OnLiveSearchReached(std::string const& search_string)
00429 {
00430   LOG_DEBUG(logger) << "Live search reached: " << search_string;
00431   if (active_lens_view_)
00432   {
00433     active_lens_view_->PerformSearch(search_string);
00434   }
00435 }
00436 
00437 void DashView::OnLensAdded(Lens::Ptr& lens)
00438 {
00439   std::string id = lens->id;
00440   lens_bar_->AddLens(lens);
00441 
00442   LensView* view = new LensView(lens, search_bar_->show_filters());
00443   AddChild(view);
00444   view->SetVisible(false);
00445   view->uri_activated.connect(sigc::mem_fun(this, &DashView::OnUriActivated));
00446   lenses_layout_->AddView(view, 1);
00447   lens_views_[lens->id] = view;
00448 
00449   lens->activated.connect(sigc::mem_fun(this, &DashView::OnUriActivatedReply));
00450   lens->search_finished.connect(sigc::mem_fun(this, &DashView::OnSearchFinished));
00451   lens->connected.changed.connect([&] (bool value)
00452   {
00453     std::string const& search_string = search_bar_->search_string;
00454     if (value && lens->search_in_global && active_lens_view_ == home_view_
00455         && !search_string.empty())
00456     {
00457       // force a (global!) search with the correct string
00458       lens->GlobalSearch(search_bar_->search_string);
00459     }
00460   });
00461 
00462   // global search done is handled by the home lens, no need to connect to it
00463   // BUT, we will special case global search finished coming from 
00464   // the applications lens, because we want to be able to launch applications
00465   // immediately without waiting for the search finished signal which will
00466   // be delayed by all the lenses we're searching
00467   if (id == "applications.lens")
00468   {
00469     lens->global_search_finished.connect(sigc::mem_fun(this, &DashView::OnAppsGlobalSearchFinished));
00470   }
00471 }
00472 
00473 void DashView::OnLensBarActivated(std::string const& id)
00474 {
00475   if (lens_views_.find(id) == lens_views_.end())
00476   {
00477     LOG_WARN(logger) << "Unable to find Lens " << id;
00478     return;
00479   }
00480 
00481   LensView* view = active_lens_view_ = lens_views_[id];
00482   view->JumpToTop();
00483 
00484   for (auto it: lens_views_)
00485   {
00486     bool id_matches = it.first == id;
00487     ViewType view_type = id_matches ? LENS_VIEW : (view == home_view_ ? HOME_VIEW : HIDDEN);
00488     it.second->SetVisible(id_matches);
00489     it.second->view_type = view_type;
00490 
00491     LOG_DEBUG(logger) << "Setting ViewType " << view_type
00492                       << " on '" << it.first << "'";
00493   }
00494 
00495   search_bar_->search_string = view->search_string;
00496   search_bar_->search_hint = view->lens()->search_hint;
00497   // lenses typically return immediately from Search() if the search query
00498   // doesn't change, so SearchFinished will be called in a few ms
00499   // FIXME: if we're forcing a search here, why don't we get rid of view types?
00500   search_bar_->ForceSearchChanged();
00501 
00502   bool expanded = view->filters_expanded;
00503   search_bar_->showing_filters = expanded;
00504 
00505   nux::GetWindowCompositor().SetKeyFocusArea(default_focus());
00506 
00507   search_bar_->text_entry()->SelectAll();
00508   search_bar_->can_refine_search = view->can_refine_search();
00509   hide_message_delay_.reset();
00510 
00511   view->QueueDraw();
00512   QueueDraw();
00513 }
00514 
00515 void DashView::OnSearchFinished(Lens::Hints const& hints)
00516 {
00517   hide_message_delay_.reset();
00518 
00519   if (active_lens_view_ == NULL) return;
00520 
00521   active_lens_view_->CheckNoResults(hints);
00522   std::string const& search_string = search_bar_->search_string;
00523 
00524   if (active_lens_view_->search_string == search_string)
00525   {
00526     search_bar_->SearchFinished();
00527     search_in_progress_ = false;
00528     if (activate_on_finish_)
00529       this->OnEntryActivated();
00530   }
00531 }
00532 
00533 void DashView::OnGlobalSearchFinished(Lens::Hints const& hints)
00534 {
00535   if (active_lens_view_ == home_view_)
00536     OnSearchFinished(hints);
00537 }
00538 
00539 void DashView::OnAppsGlobalSearchFinished(Lens::Hints const& hints)
00540 {
00541   if (active_lens_view_ == home_view_)
00542   {
00543     /* HACKITY HACK! We're resetting the state of search_in_progress when
00544      * doing searches in the home lens and we get results from apps lens.
00545      * This way typing a search query and pressing enter immediately will
00546      * wait for the apps lens results and will run correct application.
00547      * See lp:966417 and lp:856206 for more info about why we do this.
00548      */
00549     search_in_progress_ = false;
00550     if (activate_on_finish_)
00551       this->OnEntryActivated();
00552   }
00553 }
00554 
00555 void DashView::OnUriActivated(std::string const& uri)
00556 {
00557   last_activated_uri_ = uri;
00558 }
00559 
00560 void DashView::OnUriActivatedReply(std::string const& uri, HandledType type, Lens::Hints const&)
00561 {
00562   // We don't want to close the dash if there was another activation pending
00563   if (type == NOT_HANDLED)
00564   {
00565     if (!DoFallbackActivation(uri))
00566       return;
00567   }
00568   else if (type == SHOW_DASH)
00569   {
00570     return;
00571   }
00572 
00573   ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00574 }
00575 
00576 bool DashView::DoFallbackActivation(std::string const& fake_uri)
00577 {
00578   size_t pos = fake_uri.find(":");
00579   std::string uri = fake_uri.substr(++pos);
00580 
00581   LOG_DEBUG(logger) << "Fallback activating " << uri;
00582 
00583   if (g_str_has_prefix(uri.c_str(), "application://"))
00584   {
00585     std::string appname = uri.substr(14);
00586     return LaunchApp(appname);
00587   }
00588   else if (g_str_has_prefix(uri.c_str(), "unity-runner://"))
00589   {
00590     std::string appname = uri.substr(15);
00591     return LaunchApp(appname);
00592   }
00593   else
00594     return gtk_show_uri(NULL, uri.c_str(), time(NULL), NULL);
00595 
00596   return false;
00597 }
00598 
00599 bool DashView::LaunchApp(std::string const& appname)
00600 {
00601   GDesktopAppInfo* info;
00602   bool ret = false;
00603   char *id = g_strdup(appname.c_str());
00604   int i = 0;
00605 
00606   LOG_DEBUG(logger) << "Launching " << appname;
00607 
00608   while (id != NULL)
00609   {
00610     info = g_desktop_app_info_new(id);
00611     if (info != NULL)
00612     {
00613       GError* error = NULL;
00614 
00615       g_app_info_launch(G_APP_INFO(info), NULL, NULL, &error);
00616       if (error)
00617       {
00618         g_warning("Unable to launch %s: %s", id,  error->message);
00619         g_error_free(error);
00620       }
00621       else
00622         ret = true;
00623       g_object_unref(info);
00624       break;
00625     }
00626 
00627     /* Try to replace the next - with a / and do the lookup again.
00628      * If we set id=NULL we'll exit the outer loop */
00629     for (i = 0; ; i++)
00630     {
00631       if (id[i] == '-')
00632       {
00633         id[i] = '/';
00634         break;
00635       }
00636       else if (id[i] == '\0')
00637       {
00638         g_free(id);
00639         id = NULL;
00640         break;
00641       }
00642     }
00643   }
00644 
00645   g_free(id);
00646   return ret;
00647 }
00648 
00649 void DashView::DisableBlur()
00650 {
00651   renderer_.DisableBlur();
00652 }
00653 void DashView::OnEntryActivated()
00654 {
00655   if (!search_in_progress_)
00656   {
00657     active_lens_view_->ActivateFirst();
00658   }
00659   // delay the activation until we get the SearchFinished signal
00660   activate_on_finish_ = search_in_progress_;
00661 }
00662 
00663 // Keyboard navigation
00664 bool DashView::AcceptKeyNavFocus()
00665 {
00666   return false;
00667 }
00668 
00669 std::string const DashView::GetIdForShortcutActivation(std::string const& shortcut) const
00670 {
00671   Lens::Ptr lens = lenses_.GetLensForShortcut(shortcut);
00672   if (lens)
00673     return lens->id;
00674   return "";
00675 }
00676 
00677 std::vector<char> DashView::GetAllShortcuts()
00678 {
00679   std::vector<char> result;
00680 
00681   for (Lens::Ptr lens: lenses_.GetLenses())
00682   {
00683     std::string shortcut = lens->shortcut;
00684     if(shortcut.size() > 0)
00685       result.push_back(shortcut.at(0));
00686   }
00687   return result;
00688 }
00689 
00690 bool DashView::InspectKeyEvent(unsigned int eventType,
00691                                unsigned int key_sym,
00692                                const char* character)
00693 {
00694   if ((eventType == nux::NUX_KEYDOWN) && (key_sym == NUX_VK_ESCAPE))
00695   {
00696     if (search_bar_->search_string == "")
00697       ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00698     else
00699       search_bar_->search_string = "";
00700 
00701     return true;
00702   }
00703   return false;
00704 }
00705 
00706 nux::View* DashView::default_focus() const
00707 {
00708   return search_bar_->text_entry();
00709 }
00710 
00711 // Introspectable
00712 std::string DashView::GetName() const
00713 {
00714   return "DashView";
00715 }
00716 
00717 void DashView::AddProperties(GVariantBuilder* builder)
00718 {
00719   dash::Style& style = dash::Style::Instance();
00720   int num_rows = 1; // The search bar
00721 
00722   if (active_lens_view_)
00723     num_rows += active_lens_view_->GetNumRows();
00724 
00725   std::string form_factor("unknown");
00726 
00727   if (Settings::Instance().GetFormFactor() == FormFactor::NETBOOK)
00728     form_factor = "netbook";
00729   else if (Settings::Instance().GetFormFactor() == FormFactor::DESKTOP)
00730     form_factor = "desktop";
00731   else if (Settings::Instance().GetFormFactor() == FormFactor::TV)
00732     form_factor = "tv";
00733 
00734   unity::variant::BuilderWrapper wrapper(builder);
00735   wrapper.add(nux::Geometry(GetAbsoluteX(), GetAbsoluteY(), content_geo_.width, content_geo_.height));
00736   wrapper.add("num_rows", num_rows);
00737   wrapper.add("form_factor", form_factor);
00738   wrapper.add("right-border-width", style.GetDashRightTileWidth());
00739   wrapper.add("bottom-border-height", style.GetDashBottomTileHeight());
00740 }
00741 
00742 nux::Area* DashView::KeyNavIteration(nux::KeyNavDirection direction)
00743 {
00744   if (direction == nux::KEY_NAV_DOWN && search_bar_ && active_lens_view_)
00745   {
00746     auto show_filters = search_bar_->show_filters();
00747     auto fscroll_view = active_lens_view_->fscroll_view();
00748 
00749     if (show_filters && show_filters->HasKeyFocus())
00750     {
00751       if (fscroll_view->IsVisible() && fscroll_view)
00752         return fscroll_view->KeyNavIteration(direction);
00753       else
00754         return active_lens_view_->KeyNavIteration(direction);
00755     }
00756   }
00757   return this;
00758 }
00759 
00760 void DashView::ProcessDndEnter()
00761 {
00762   ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00763 }
00764 
00765 nux::Area* DashView::FindKeyFocusArea(unsigned int key_symbol,
00766                                       unsigned long x11_key_code,
00767                                       unsigned long special_keys_state)
00768 {
00769   // Do what nux::View does, but if the event isn't a key navigation,
00770   // designate the text entry to process it.
00771 
00772   using namespace nux;
00773   nux::KeyNavDirection direction = KEY_NAV_NONE;
00774   bool ctrl = (special_keys_state & NUX_STATE_CTRL);
00775 
00776   switch (x11_key_code)
00777   {
00778   case NUX_VK_UP:
00779     direction = KEY_NAV_UP;
00780     break;
00781   case NUX_VK_DOWN:
00782     direction = KEY_NAV_DOWN;
00783     break;
00784   case NUX_VK_LEFT:
00785     direction = KEY_NAV_LEFT;
00786     break;
00787   case NUX_VK_RIGHT:
00788     direction = KEY_NAV_RIGHT;
00789     break;
00790   case NUX_VK_LEFT_TAB:
00791   case NUX_VK_PAGE_UP:
00792     direction = KEY_NAV_TAB_PREVIOUS;
00793     break;
00794   case NUX_VK_TAB:
00795   case NUX_VK_PAGE_DOWN:
00796     direction = KEY_NAV_TAB_NEXT;
00797     break;
00798   case NUX_VK_ENTER:
00799   case NUX_KP_ENTER:
00800     // Not sure if Enter should be a navigation key
00801     direction = KEY_NAV_ENTER;
00802     break;
00803   case NUX_VK_F4:
00804     // Maybe we should not do it here, but it needs to be checked where
00805     // we are able to know if alt is pressed.
00806     if (special_keys_state == NUX_STATE_ALT)
00807     {
00808       ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00809     }
00810     break;
00811   default:
00812     direction = KEY_NAV_NONE;
00813   }
00814 
00815   // We should not do it here, but I really don't want to make DashView
00816   // focusable and I'm not able to know if ctrl is pressed in
00817   // DashView::KeyNavIteration.
00818    nux::InputArea* focus_area = nux::GetWindowCompositor().GetKeyFocusArea();
00819 
00820   if (direction != KEY_NAV_NONE && key_symbol == nux::NUX_KEYDOWN && !search_bar_->im_preedit)
00821   {
00822     std::list<nux::Area*> tabs;
00823     for (auto category : active_lens_view_->categories())
00824     {
00825       if (category->IsVisible())
00826         tabs.push_back(category);
00827     }
00828 
00829     if (search_bar_ && search_bar_->show_filters() &&
00830         search_bar_->show_filters()->IsVisible())
00831     {
00832       tabs.push_back(search_bar_->show_filters());
00833     }
00834 
00835     if (active_lens_view_->filter_bar() && active_lens_view_->fscroll_view() &&
00836         active_lens_view_->fscroll_view()->IsVisible())
00837     {
00838       for (auto filter : active_lens_view_->filter_bar()->GetLayout()->GetChildren())
00839       {
00840         tabs.push_back(filter);
00841       }
00842     }
00843 
00844     if (direction == KEY_NAV_TAB_PREVIOUS)
00845     {
00846       if (ctrl)
00847       {
00848         lens_bar_->ActivatePrevious();
00849       }
00850       else
00851       {
00852         auto rbegin = tabs.rbegin();
00853         auto rend = tabs.rend();
00854 
00855         bool use_the_prev = false;
00856         for (auto tab = rbegin; tab != rend; ++tab)
00857         {
00858           const auto& tab_ptr = *tab;
00859 
00860           if (use_the_prev)
00861             return tab_ptr;
00862 
00863           if (focus_area)
00864             use_the_prev = focus_area->IsChildOf(tab_ptr);
00865         }
00866 
00867         for (auto tab = rbegin; tab != rend; ++tab)
00868           return *tab;
00869       }
00870     }
00871     else if (direction == KEY_NAV_TAB_NEXT)
00872     {
00873       if (ctrl)
00874       {
00875         lens_bar_->ActivateNext();
00876       }
00877       else
00878       {
00879         bool use_the_next = false;
00880         for (auto tab : tabs)
00881         {
00882           if (use_the_next)
00883             return tab;
00884 
00885           if (focus_area)
00886             use_the_next = focus_area->IsChildOf(tab);
00887         }
00888 
00889         for (auto tab : tabs)
00890           return tab;
00891       }
00892     }
00893   }
00894 
00895   bool search_key = false;
00896 
00897   if (direction == KEY_NAV_NONE)
00898   {
00899     if (ui::KeyboardUtil::IsPrintableKeySymbol(x11_key_code) ||
00900         ui::KeyboardUtil::IsMoveKeySymbol(x11_key_code))
00901     {
00902       search_key = true;
00903     }
00904   }
00905 
00906   if (search_key || search_bar_->im_preedit)
00907   {
00908     // then send the event to the search entry
00909     return search_bar_->text_entry();
00910   }
00911   else if (next_object_to_key_focus_area_)
00912   {
00913     return next_object_to_key_focus_area_->FindKeyFocusArea(key_symbol, x11_key_code, special_keys_state);
00914   }
00915 
00916   return nullptr;
00917 }
00918 
00919 }
00920 }