Back to index

unity  6.0.0
SwitcherController.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011 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: Jason Smith <jason.smith@canonical.com>
00017  */
00018 
00019 #include <math.h>
00020 
00021 #include <Nux/Nux.h>
00022 #include <Nux/HLayout.h>
00023 
00024 #include "unity-shared/UBusMessages.h"
00025 #include "unity-shared/WindowManager.h"
00026 
00027 #include "SwitcherController.h"
00028 
00029 namespace unity
00030 {
00031 using launcher::AbstractLauncherIcon;
00032 using launcher::ActionArg;
00033 using ui::LayoutWindowList;
00034 
00035 namespace
00036 {
00037   const std::string LAZY_TIMEOUT = "lazy-timeout";
00038   const std::string SHOW_TIMEOUT = "show-timeout";
00039   const std::string DETAIL_TIMEOUT = "detail-timeout";
00040   const std::string VIEW_CONSTRUCT_IDLE = "view-construct-idle";
00041 }
00042 
00043 namespace switcher
00044 {
00045 
00046 Controller::Controller(unsigned int load_timeout)
00047   :  timeout_length(75)
00048   ,  detail_on_timeout(true)
00049   ,  detail_timeout_length(500)
00050   ,  initial_detail_timeout_length(1500)
00051   ,  construct_timeout_(load_timeout)
00052   ,  main_layout_(nullptr)
00053   ,  monitor_(0)
00054   ,  visible_(false)
00055   ,  bg_color_(0, 0, 0, 0.5)
00056 {
00057   ubus_manager_.RegisterInterest(UBUS_BACKGROUND_COLOR_CHANGED, sigc::mem_fun(this, &Controller::OnBackgroundUpdate));
00058 
00059   auto lazy_timeout = std::make_shared<glib::TimeoutSeconds>(construct_timeout_, glib::Source::Priority::LOW);
00060   lazy_timeout->Run([&] { ConstructWindow(); return false; });
00061   sources_.Add(lazy_timeout, LAZY_TIMEOUT);
00062 }
00063 
00064 void Controller::OnBackgroundUpdate(GVariant* data)
00065 {
00066   gdouble red, green, blue, alpha;
00067   g_variant_get(data, "(dddd)", &red, &green, &blue, &alpha);
00068   bg_color_ = nux::Color(red, green, blue, alpha);
00069 
00070   if (view_)
00071     view_->background_color = bg_color_;
00072 }
00073 
00074 void Controller::Show(ShowMode show, SortMode sort, bool reverse,
00075                       std::vector<AbstractLauncherIcon::Ptr> results)
00076 {
00077   if (sort == SortMode::FOCUS_ORDER)
00078   {
00079     std::sort(results.begin(), results.end(), CompareSwitcherItemsPriority);
00080   }
00081 
00082   model_.reset(new SwitcherModel(results));
00083   AddChild(model_.get());
00084   model_->selection_changed.connect(sigc::mem_fun(this, &Controller::OnModelSelectionChanged));
00085   model_->only_detail_on_viewport = (show == ShowMode::CURRENT_VIEWPORT);
00086 
00087   SelectFirstItem();
00088 
00089   visible_ = true;
00090 
00091   if (timeout_length > 0)
00092   {
00093     auto view_idle_construct = std::make_shared<glib::Idle>();
00094     sources_.Add(view_idle_construct, VIEW_CONSTRUCT_IDLE);
00095     view_idle_construct->Run([&] () { ConstructView(); return false; });
00096 
00097     auto show_timeout = std::make_shared<glib::Timeout>(timeout_length);
00098     sources_.Add(show_timeout, SHOW_TIMEOUT);
00099     show_timeout->Run([&] () { ShowView(); return false; });
00100   }
00101   else
00102   {
00103     ShowView();
00104   }
00105 
00106   if (detail_on_timeout)
00107   {
00108     auto detail_timeout = std::make_shared<glib::Timeout>(initial_detail_timeout_length);
00109     sources_.Add(detail_timeout, DETAIL_TIMEOUT);
00110     detail_timeout->Run(sigc::mem_fun(this, &Controller::OnDetailTimer));
00111   }
00112 
00113   ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
00114   ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN, g_variant_new("(bi)", true, monitor_));
00115 }
00116 
00117 void Controller::Select(int index)
00118 {
00119   if (visible_)
00120     model_->Select(index);
00121 }
00122 
00123 bool Controller::OnDetailTimer()
00124 {
00125   if (visible_ && !model_->detail_selection)
00126   {
00127     SetDetail(true, 2);
00128     detail_mode_ = TAB_NEXT_WINDOW;
00129   }
00130 
00131   return false;
00132 }
00133 
00134 void Controller::OnModelSelectionChanged(AbstractLauncherIcon::Ptr icon)
00135 {
00136   if (detail_on_timeout)
00137   {
00138     auto detail_timeout = std::make_shared<glib::Timeout>(detail_timeout_length);
00139     sources_.Add(detail_timeout, DETAIL_TIMEOUT);
00140     detail_timeout->Run(sigc::mem_fun(this, &Controller::OnDetailTimer));
00141   }
00142 
00143   if (icon)
00144   {
00145     if (!visible_)
00146     {
00147       ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN, g_variant_new("(bi)", true, monitor_));
00148     }
00149 
00150     ubus_manager_.SendMessage(UBUS_SWITCHER_SELECTION_CHANGED,
00151                               g_variant_new_string(icon->tooltip_text().c_str()));
00152   }
00153 }
00154 
00155 void Controller::ShowView()
00156 {
00157   if (!visible_)
00158     return;
00159 
00160   ConstructView();
00161 
00162   ubus_manager_.SendMessage(UBUS_SWITCHER_START, NULL);
00163 
00164   if (view_window_) {
00165     view_window_->ShowWindow(true);
00166     view_window_->PushToFront();
00167     view_window_->SetOpacity(1.0f);
00168   }
00169 }
00170 
00171 void Controller::ConstructWindow()
00172 {
00173   sources_.Remove(LAZY_TIMEOUT);
00174 
00175   if (!view_window_)
00176   {
00177     main_layout_ = new nux::HLayout(NUX_TRACKER_LOCATION);
00178     main_layout_->SetVerticalExternalMargin(0);
00179     main_layout_->SetHorizontalExternalMargin(0);
00180 
00181     view_window_ = new nux::BaseWindow("Switcher");
00182     view_window_->SetLayout(main_layout_);
00183     view_window_->SetBackgroundColor(nux::Color(0x00000000));
00184     view_window_->SetGeometry(workarea_);
00185     view_window_->EnableInputWindow(true, "Switcher", false, false);
00186   }
00187 }
00188 
00189 void Controller::ConstructView()
00190 {
00191   if (view_ || !model_)
00192     return;
00193 
00194   sources_.Remove(VIEW_CONSTRUCT_IDLE);
00195 
00196   view_ = SwitcherView::Ptr(new SwitcherView());
00197   AddChild(view_.GetPointer());
00198   view_->SetModel(model_);
00199   view_->background_color = bg_color_;
00200   view_->monitor = monitor_;
00201   view_->SetupBackground();
00202 
00203   ConstructWindow();
00204   main_layout_->AddView(view_.GetPointer(), 1);
00205   view_window_->SetGeometry(workarea_);
00206   view_window_->SetOpacity(0.0f);
00207 }
00208 
00209 void Controller::SetWorkspace(nux::Geometry geo, int monitor)
00210 {
00211   monitor_ = monitor;
00212   workarea_ = geo;
00213 
00214   if (view_)
00215     view_->monitor = monitor_;
00216 }
00217 
00218 void Controller::Hide(bool accept_state)
00219 {
00220   if (!visible_)
00221     return;
00222 
00223   if (accept_state)
00224   {
00225     AbstractLauncherIcon::Ptr selection = model_->Selection();
00226     if (selection)
00227     {
00228       if (model_->detail_selection)
00229       {
00230         selection->Activate(ActionArg(ActionArg::SWITCHER, 0, model_->DetailSelectionWindow ()));
00231       }
00232       else
00233       {
00234         if (selection->GetQuirk (AbstractLauncherIcon::QUIRK_ACTIVE) &&
00235             !model_->DetailXids().empty ())
00236         {
00237           selection->Activate(ActionArg (ActionArg::SWITCHER, 0, model_->DetailXids()[0]));
00238         }
00239         else
00240         {
00241           selection->Activate(ActionArg(ActionArg::SWITCHER, 0));
00242         }
00243       }
00244     }
00245   }
00246 
00247   ubus_manager_.SendMessage(UBUS_SWITCHER_END, g_variant_new_boolean(!accept_state));
00248 
00249   sources_.Remove(VIEW_CONSTRUCT_IDLE);
00250   sources_.Remove(SHOW_TIMEOUT);
00251   sources_.Remove(DETAIL_TIMEOUT);
00252 
00253   model_.reset();
00254   visible_ = false;
00255 
00256   if (view_)
00257     main_layout_->RemoveChildObject(view_.GetPointer());
00258 
00259   if (view_window_)
00260   {
00261     view_window_->SetOpacity(0.0f);
00262     view_window_->ShowWindow(false);
00263     view_window_->PushToBack();
00264     view_window_->EnableInputWindow(false);
00265   }
00266 
00267   ubus_manager_.SendMessage(UBUS_SWITCHER_SHOWN, g_variant_new("(bi)", false, monitor_));
00268 
00269   view_.Release();
00270 }
00271 
00272 bool Controller::Visible()
00273 {
00274   return visible_;
00275 }
00276 
00277 void Controller::Next()
00278 {
00279   if (!model_)
00280     return;
00281 
00282   if (model_->detail_selection)
00283   {
00284     switch (detail_mode_)
00285     {
00286       case TAB_NEXT_WINDOW:
00287         if (model_->detail_selection_index < model_->DetailXids().size () - 1)
00288           model_->NextDetail();
00289         else
00290           model_->Next();
00291         break;
00292       case TAB_NEXT_TILE:
00293         model_->Next();
00294         break;
00295       case TAB_NEXT_WINDOW_LOOP:
00296         model_->NextDetail(); //looping automatic
00297         break;
00298     }
00299   }
00300   else
00301   {
00302     model_->Next();
00303   }
00304 }
00305 
00306 void Controller::Prev()
00307 {
00308   if (!model_)
00309     return;
00310 
00311   if (model_->detail_selection)
00312   {
00313     switch (detail_mode_)
00314     {
00315       case TAB_NEXT_WINDOW:
00316         if (model_->detail_selection_index > (unsigned int) 0)
00317           model_->PrevDetail();
00318         else
00319           model_->Prev();
00320         break;
00321       case TAB_NEXT_TILE:
00322         model_->Prev();
00323         break;
00324       case TAB_NEXT_WINDOW_LOOP:
00325         model_->PrevDetail(); //looping automatic
00326         break;
00327     }
00328   }
00329   else
00330   {
00331     model_->Prev();
00332   }
00333 }
00334 
00335 SwitcherView* Controller::GetView()
00336 {
00337   return view_.GetPointer();
00338 }
00339 
00340 void Controller::SetDetail(bool value, unsigned int min_windows)
00341 {
00342   if (value && model_->DetailXids().size () >= min_windows)
00343   {
00344     model_->detail_selection = true;
00345     detail_mode_ = TAB_NEXT_WINDOW;
00346   }
00347   else
00348   {
00349     model_->detail_selection = false;
00350   }
00351 }
00352 
00353 void Controller::NextDetail()
00354 {
00355   if (!model_)
00356     return;
00357 
00358   if (!model_->detail_selection)
00359   {
00360     SetDetail(true);
00361     detail_mode_ = TAB_NEXT_TILE;
00362   }
00363   else
00364   {
00365     model_->NextDetail();
00366   }
00367 }
00368 
00369 void Controller::PrevDetail()
00370 {
00371   if (!model_)
00372     return;
00373 
00374   if (!model_->detail_selection)
00375   {
00376     SetDetail(true);
00377     detail_mode_ = TAB_NEXT_TILE;
00378     model_->PrevDetail();
00379   }
00380   else
00381   {
00382     model_->PrevDetail();
00383   }
00384 }
00385 
00386 LayoutWindowList Controller::ExternalRenderTargets()
00387 {
00388   if (!view_)
00389   {
00390     LayoutWindowList result;
00391     return result;
00392   }
00393   return view_->ExternalTargets();
00394 }
00395 
00396 guint Controller::GetSwitcherInputWindowId() const
00397 {
00398   return view_window_->GetInputWindowId();
00399 }
00400 
00401 bool Controller::CompareSwitcherItemsPriority(AbstractLauncherIcon::Ptr first,
00402                                               AbstractLauncherIcon::Ptr second)
00403 {
00404   if (first->GetIconType() == second->GetIconType())
00405     return first->SwitcherPriority() > second->SwitcherPriority();
00406 
00407   if (first->GetIconType() == AbstractLauncherIcon::IconType::TYPE_DESKTOP)
00408     return true;
00409 
00410   if (second->GetIconType() == AbstractLauncherIcon::IconType::TYPE_DESKTOP)
00411     return false;
00412 
00413   return first->GetIconType() < second->GetIconType();
00414 }
00415 
00416 void Controller::SelectFirstItem()
00417 {
00418   if (!model_)
00419     return;
00420 
00421   AbstractLauncherIcon::Ptr first  = model_->at(1);
00422   AbstractLauncherIcon::Ptr second = model_->at(2);
00423 
00424   if (!first)
00425   {
00426     model_->Select(0);
00427     return;
00428   }
00429   else if (!second)
00430   {
00431     model_->Select(1);
00432     return;
00433   }
00434 
00435   unsigned int first_highest = 0;
00436   unsigned int first_second = 0; // first icons second highest active
00437   unsigned int second_first = 0; // second icons first highest active
00438 
00439   for (guint32 xid : first->Windows())
00440   {
00441     unsigned int num = WindowManager::Default()->GetWindowActiveNumber(xid);
00442 
00443     if (num > first_highest)
00444     {
00445       first_second = first_highest;
00446       first_highest = num;
00447     }
00448     else if (num > first_second)
00449     {
00450       first_second = num;
00451     }
00452   }
00453 
00454   for (guint32 xid : second->Windows())
00455   {
00456     second_first = MAX (WindowManager::Default()->GetWindowActiveNumber(xid), second_first);
00457   }
00458 
00459   if (first_second > second_first)
00460     model_->Select (first);
00461   else
00462     model_->Select (second);
00463 }
00464 
00465 /* Introspection */
00466 std::string
00467 Controller::GetName() const
00468 {
00469   return "SwitcherController";
00470 }
00471 
00472 void
00473 Controller::AddProperties(GVariantBuilder* builder)
00474 {
00475   unity::variant::BuilderWrapper(builder)
00476   .add("timeout-length", timeout_length())
00477   .add("detail-on-timeout", detail_on_timeout())
00478   .add("initial-detail-timeout-length", initial_detail_timeout_length())
00479   .add("detail-timeout-length", detail_timeout_length())
00480   .add("visible", visible_)
00481   .add("monitor", monitor_)
00482   .add("detail-mode", detail_mode_);
00483 }
00484 
00485 } // switcher namespace
00486 } // unity namespace