Back to index

unity  6.0.0
PanelMenuView.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2010-2012 Canonical Ltd
00004  *
00005  * This program is free software: you can redistribute it and/or modify
00006  * it under the terms of the GNU 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,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016  *
00017  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
00018  *              Marco Trevisan <3v1n0@ubuntu.com>
00019  */
00020 
00021 #include <Nux/Nux.h>
00022 #include <NuxCore/Logger.h>
00023 
00024 #include "unity-shared/CairoTexture.h"
00025 #include "PanelMenuView.h"
00026 #include "unity-shared/PanelStyle.h"
00027 #include "unity-shared/UnitySettings.h"
00028 #include "unity-shared/UBusMessages.h"
00029 #include "unity-shared/UScreen.h"
00030 
00031 #include <UnityCore/Variant.h>
00032 
00033 #include <glib/gi18n-lib.h>
00034 
00035 namespace unity
00036 {
00037 
00038 namespace
00039 {
00040   nux::logging::Logger logger("unity.panel.menu");
00041   const int MAIN_LEFT_PADDING = 4;
00042   const int TITLE_PADDING = 2;
00043   const int MENUBAR_PADDING = 4;
00044   const int MENU_ENTRIES_PADDING = 6;
00045   const int DEFAULT_MENUS_FADEIN = 100;
00046   const int DEFAULT_MENUS_FADEOUT = 120;
00047   const int DEFAULT_MENUS_DISCOVERY = 2;
00048   const int DEFAULT_DISCOVERY_FADEIN = 200;
00049   const int DEFAULT_DISCOVERY_FADEOUT = 300;
00050 
00051   const std::string NEW_APP_HIDE_TIMEOUT = "new-app-hide-timeout";
00052   const std::string NEW_APP_SHOW_TIMEOUT = "new-app-show-timeout";
00053   const std::string WINDOW_MOVED_TIMEOUT = "window-moved-timeout";
00054   const std::string UPDATE_SHOW_NOW_TIMEOUT = "update-show-now-timeout";
00055 }
00056 
00057 PanelMenuView::PanelMenuView()
00058   : _matcher(bamf_matcher_get_default()),
00059     _is_inside(false),
00060     _is_grabbed(false),
00061     _is_maximized(false),
00062     _last_active_view(nullptr),
00063     _new_application(nullptr),
00064     _overlay_showing(false),
00065     _switcher_showing(false),
00066     _launcher_keynav(false),
00067     _show_now_activated(false),
00068     _we_control_active(false),
00069     _new_app_menu_shown(false),
00070     _monitor(0),
00071     _active_xid(0),
00072     _desktop_name(_("Ubuntu Desktop")),
00073     _menus_fadein(DEFAULT_MENUS_FADEIN),
00074     _menus_fadeout(DEFAULT_MENUS_FADEOUT),
00075     _menus_discovery(DEFAULT_MENUS_DISCOVERY),
00076     _menus_discovery_fadein(DEFAULT_DISCOVERY_FADEIN),
00077     _menus_discovery_fadeout(DEFAULT_DISCOVERY_FADEOUT),
00078     _fade_in_animator(_menus_fadein),
00079     _fade_out_animator(_menus_fadeout)
00080 {
00081   layout_->SetContentDistribution(nux::eStackLeft);
00082 
00083   BamfWindow* active_win = bamf_matcher_get_active_window(_matcher);
00084   if (BAMF_IS_WINDOW(active_win))
00085     _active_xid = bamf_window_get_xid(active_win);
00086 
00087   _view_opened_signal.Connect(_matcher, "view-opened",
00088                               sigc::mem_fun(this, &PanelMenuView::OnViewOpened));
00089   _view_closed_signal.Connect(_matcher, "view-closed",
00090                               sigc::mem_fun(this, &PanelMenuView::OnViewClosed));
00091   _active_win_changed_signal.Connect(_matcher, "active-window-changed",
00092                                      sigc::mem_fun(this, &PanelMenuView::OnActiveWindowChanged));
00093   _active_app_changed_signal.Connect(_matcher, "active-application-changed",
00094                                      sigc::mem_fun(this, &PanelMenuView::OnActiveAppChanged));
00095 
00096   _window_buttons = new WindowButtons();
00097   _window_buttons->SetParentObject(this);
00098   _window_buttons->SetMonitor(_monitor);
00099   _window_buttons->SetControlledWindow(_active_xid);
00100   _window_buttons->SetLeftAndRightPadding(MAIN_LEFT_PADDING, MENUBAR_PADDING);
00101   _window_buttons->SetMaximumHeight(panel::Style::Instance().panel_height);
00102   _window_buttons->ComputeContentSize();
00103 
00104   _window_buttons->mouse_enter.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseEnter));
00105   _window_buttons->mouse_leave.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseLeave));
00106   //_window_buttons->mouse_move.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseMove));
00107   AddChild(_window_buttons.GetPointer());
00108 
00109   layout_->SetLeftAndRightPadding(_window_buttons->GetContentWidth(), 0);
00110   layout_->SetBaseHeight(panel::Style::Instance().panel_height);
00111 
00112   _titlebar_grab_area = new PanelTitlebarGrabArea();
00113   _titlebar_grab_area->SetParentObject(this);
00114   _titlebar_grab_area->activate_request.connect(sigc::mem_fun(this, &PanelMenuView::OnMaximizedActivate));
00115   _titlebar_grab_area->restore_request.connect(sigc::mem_fun(this, &PanelMenuView::OnMaximizedRestore));
00116   _titlebar_grab_area->lower_request.connect(sigc::mem_fun(this, &PanelMenuView::OnMaximizedLower));
00117   _titlebar_grab_area->grab_started.connect(sigc::mem_fun(this, &PanelMenuView::OnMaximizedGrabStart));
00118   _titlebar_grab_area->grab_move.connect(sigc::mem_fun(this, &PanelMenuView::OnMaximizedGrabMove));
00119   _titlebar_grab_area->grab_end.connect(sigc::mem_fun(this, &PanelMenuView::OnMaximizedGrabEnd));
00120   AddChild(_titlebar_grab_area.GetPointer());
00121 
00122   WindowManager* win_manager = WindowManager::Default();
00123   win_manager->window_minimized.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowMinimized));
00124   win_manager->window_unminimized.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowUnminimized));
00125   win_manager->window_maximized.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowMaximized));
00126   win_manager->window_restored.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowRestored));
00127   win_manager->window_unmapped.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowUnmapped));
00128   win_manager->window_mapped.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowMapped));
00129   win_manager->window_moved.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowMoved));
00130   win_manager->window_resized.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowMoved));
00131   win_manager->window_decorated.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowDecorated));
00132   win_manager->window_undecorated.connect(sigc::mem_fun(this, &PanelMenuView::OnWindowUndecorated));
00133   win_manager->initiate_spread.connect(sigc::mem_fun(this, &PanelMenuView::OnSpreadInitiate));
00134   win_manager->terminate_spread.connect(sigc::mem_fun(this, &PanelMenuView::OnSpreadTerminate));
00135   win_manager->initiate_expo.connect(sigc::mem_fun(this, &PanelMenuView::OnExpoInitiate));
00136   win_manager->terminate_expo.connect(sigc::mem_fun(this, &PanelMenuView::OnExpoTerminate));
00137   win_manager->compiz_screen_viewport_switch_ended.connect(sigc::mem_fun(this, &PanelMenuView::OnExpoTerminate));
00138 
00139   _style_changed_connection = panel::Style::Instance().changed.connect([&] {
00140     _window_buttons->ComputeContentSize();
00141     layout_->SetLeftAndRightPadding(_window_buttons->GetContentWidth(), 0);
00142 
00143     Refresh(true);
00144     FullRedraw();
00145   });
00146 
00147   mouse_enter.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseEnter));
00148   mouse_leave.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseLeave));
00149   //mouse_move.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseMove));
00150 
00151   _titlebar_grab_area->mouse_enter.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseEnter));
00152   _titlebar_grab_area->mouse_leave.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseLeave));
00153 
00154   _ubus_manager.RegisterInterest(UBUS_SWITCHER_SHOWN, sigc::mem_fun(this, &PanelMenuView::OnSwitcherShown));
00155   _ubus_manager.RegisterInterest(UBUS_SWITCHER_SELECTION_CHANGED, sigc::mem_fun(this, &PanelMenuView::OnSwitcherSelectionChanged));
00156 
00157   _ubus_manager.RegisterInterest(UBUS_LAUNCHER_START_KEY_NAV, sigc::mem_fun(this, &PanelMenuView::OnLauncherKeyNavStarted));
00158   _ubus_manager.RegisterInterest(UBUS_LAUNCHER_END_KEY_NAV, sigc::mem_fun(this, &PanelMenuView::OnLauncherKeyNavEnded));
00159   _ubus_manager.RegisterInterest(UBUS_LAUNCHER_START_KEY_SWTICHER, sigc::mem_fun(this, &PanelMenuView::OnLauncherKeyNavStarted));
00160   _ubus_manager.RegisterInterest(UBUS_LAUNCHER_END_KEY_SWTICHER, sigc::mem_fun(this, &PanelMenuView::OnLauncherKeyNavEnded));
00161   _ubus_manager.RegisterInterest(UBUS_LAUNCHER_SELECTION_CHANGED, sigc::mem_fun(this, &PanelMenuView::OnLauncherSelectionChanged));
00162 
00163   _fade_in_animator.animation_updated.connect(sigc::mem_fun(this, &PanelMenuView::OnFadeInChanged));
00164   _fade_in_animator.animation_ended.connect(sigc::mem_fun(this, &PanelMenuView::FullRedraw));
00165   _fade_out_animator.animation_updated.connect(sigc::mem_fun(this, &PanelMenuView::OnFadeOutChanged));
00166   _fade_out_animator.animation_ended.connect(sigc::mem_fun(this, &PanelMenuView::FullRedraw));
00167 
00168   SetOpacity(0.0f);
00169   _window_buttons->SetOpacity(0.0f);
00170 
00171   Refresh();
00172   FullRedraw();
00173 }
00174 
00175 PanelMenuView::~PanelMenuView()
00176 {
00177   _style_changed_connection.disconnect();
00178   _window_buttons->UnParentObject();
00179   _titlebar_grab_area->UnParentObject();
00180 }
00181 
00182 void PanelMenuView::OverlayShown()
00183 {
00184   _overlay_showing = true;
00185   QueueDraw();
00186 }
00187 
00188 void PanelMenuView::OverlayHidden()
00189 {
00190   _overlay_showing = false;
00191   QueueDraw();
00192 }
00193 
00194 void PanelMenuView::AddIndicator(indicator::Indicator::Ptr const& indicator)
00195 {
00196   if (!GetIndicators().empty())
00197   {
00198     LOG_ERROR(logger) << "PanelMenuView has already an indicator!";
00199     return;
00200   }
00201 
00202   PanelIndicatorsView::AddIndicator(indicator);
00203 }
00204 
00205 void PanelMenuView::SetMenuShowTimings(int fadein, int fadeout, int discovery,
00206                                        int discovery_fadein, int discovery_fadeout)
00207 {
00208   if (fadein > -1)
00209   {
00210     _menus_fadein = fadein;
00211     _fade_in_animator.SetDuration(_menus_fadein);
00212   }
00213 
00214   if (fadeout > -1)
00215   {
00216     _menus_fadeout = fadeout;
00217     _fade_out_animator.SetDuration(_menus_fadeout);
00218   }
00219 
00220   if (discovery > -1)
00221     _menus_discovery = discovery;
00222 
00223   if (discovery_fadein > -1)
00224     _menus_discovery_fadein = discovery_fadein;
00225 
00226   if (discovery_fadeout > -1)
00227     _menus_discovery_fadeout = discovery_fadeout;
00228 }
00229 
00230 void PanelMenuView::FullRedraw()
00231 {
00232   QueueDraw();
00233   _window_buttons->QueueDraw();
00234 }
00235 
00236 nux::Area* PanelMenuView::FindAreaUnderMouse(const nux::Point& mouse_position, nux::NuxEventType event_type)
00237 {
00238   bool mouse_inside = TestMousePointerInclusionFilterMouseWheel(mouse_position, event_type);
00239 
00240   if (!mouse_inside)
00241     return nullptr;
00242 
00243   Area* found_area = nullptr;
00244 
00245   if (_overlay_showing)
00246   {
00247     if (_window_buttons)
00248       return _window_buttons->FindAreaUnderMouse(mouse_position, event_type);
00249   }
00250 
00251   if (!_we_control_active)
00252   {
00253     /* When the current panel is not active, it all behaves like a grab-area */
00254     if (GetAbsoluteGeometry().IsInside(mouse_position))
00255       return _titlebar_grab_area.GetPointer();
00256   }
00257 
00258   if (_is_maximized)
00259   {
00260     if (_window_buttons)
00261     {
00262       found_area = _window_buttons->FindAreaUnderMouse(mouse_position, event_type);
00263       NUX_RETURN_VALUE_IF_NOTNULL(found_area, found_area);
00264     }
00265   }
00266 
00267   if (_titlebar_grab_area && !_overlay_showing)
00268   {
00269     found_area = _titlebar_grab_area->FindAreaUnderMouse(mouse_position, event_type);
00270     NUX_RETURN_VALUE_IF_NOTNULL(found_area, found_area);
00271   }
00272 
00273   return PanelIndicatorsView::FindAreaUnderMouse(mouse_position, event_type);
00274 }
00275 
00276 void PanelMenuView::PreLayoutManagement()
00277 {
00278   PanelIndicatorsView::PreLayoutManagement();
00279   nux::Geometry const& geo = GetGeometry();
00280 
00281   _window_buttons->ComputeContentSize();
00282   int buttons_diff = geo.height - _window_buttons->GetContentHeight();
00283   _window_buttons->SetBaseY(buttons_diff > 0 ? std::ceil(buttons_diff/2.0f) : 0);
00284 
00285   layout_->ComputeContentSize();
00286   int layout_width = layout_->GetContentWidth();
00287 
00288   _titlebar_grab_area->SetBaseX(layout_width);
00289   _titlebar_grab_area->SetBaseHeight(geo.height);
00290   _titlebar_grab_area->SetMinimumWidth(geo.width - layout_width);
00291   _titlebar_grab_area->SetMaximumWidth(geo.width - layout_width);
00292 
00293   SetMaximumEntriesWidth(geo.width - _window_buttons->GetContentWidth());
00294 }
00295 
00296 void PanelMenuView::OnFadeInChanged(double opacity)
00297 {
00298   if (DrawMenus() && GetOpacity() != 1.0f)
00299     SetOpacity(opacity);
00300 
00301   if (DrawWindowButtons() && _window_buttons->GetOpacity() != 1.0f)
00302     _window_buttons->SetOpacity(opacity);
00303 
00304   QueueDraw();
00305 }
00306 
00307 void PanelMenuView::OnFadeOutChanged(double progress)
00308 {
00309   double opacity = CLAMP(1.0f - progress, 0.0f, 1.0f);
00310 
00311   if (!DrawMenus() && GetOpacity() != 0.0f)
00312     SetOpacity(opacity);
00313 
00314   if (!DrawWindowButtons() && _window_buttons->GetOpacity() != 0.0f)
00315     _window_buttons->SetOpacity(opacity);
00316 
00317   QueueDraw();
00318 }
00319 
00320 bool PanelMenuView::DrawMenus() const
00321 {
00322   auto wm = WindowManager::Default();
00323   bool screen_grabbed = (wm->IsExpoActive() || wm->IsScaleActive());
00324 
00325   if (_we_control_active && !_overlay_showing && !screen_grabbed &&
00326       !_switcher_showing && !_launcher_keynav)
00327   {
00328     if (_is_inside || _last_active_view || _show_now_activated || _new_application)
00329     {
00330       return true;
00331     }
00332   }
00333 
00334   return false;
00335 }
00336 
00337 bool PanelMenuView::DrawWindowButtons() const
00338 {
00339   auto wm = WindowManager::Default();
00340   bool screen_grabbed = (wm->IsExpoActive() || wm->IsScaleActive());
00341 
00342   if (_overlay_showing)
00343     return true;
00344 
00345   if (_we_control_active && _is_maximized && !screen_grabbed &&
00346       !_launcher_keynav && !_switcher_showing)
00347   {
00348     if (_is_inside || _show_now_activated || _new_application)
00349     {
00350       return true;
00351     }
00352   }
00353 
00354   return false;
00355 }
00356 
00357 void PanelMenuView::Draw(nux::GraphicsEngine& GfxContext, bool force_draw)
00358 {
00359   nux::Geometry const& geo = GetGeometry();
00360   int button_width = _window_buttons->GetContentWidth();
00361   const float factor = 4;
00362   button_width /= factor;
00363 
00364   if (geo != _last_geo)
00365   {
00366     _last_geo = geo;
00367     QueueRelayout();
00368     Refresh(true);
00369   }
00370 
00371   GfxContext.PushClippingRectangle(geo);
00372 
00373   /* "Clear" out the background */
00374   nux::ROPConfig rop;
00375   rop.Blend = true;
00376   rop.SrcBlend = GL_ONE;
00377   rop.DstBlend = GL_ONE_MINUS_SRC_ALPHA;
00378 
00379   nux::ColorLayer layer(nux::Color(0x00000000), true, rop);
00380   nux::GetPainter().PushDrawLayer(GfxContext, GetGeometry(), &layer);
00381 
00382   if (_title_texture)
00383   {
00384     guint blend_alpha = 0, blend_src = 0, blend_dest = 0;
00385     bool draw_menus = DrawMenus();
00386     bool draw_window_buttons = DrawWindowButtons();
00387     bool has_menu = false;
00388     bool draw_faded_title = false;
00389 
00390     GfxContext.GetRenderStates().GetBlend(blend_alpha, blend_src, blend_dest);
00391 
00392     for (auto entry : entries_)
00393     {
00394       if (entry.second->IsVisible())
00395       {
00396         has_menu = true;
00397         break;
00398       }
00399     }
00400 
00401     if (!draw_window_buttons && _we_control_active && has_menu &&
00402         (draw_menus || (GetOpacity() > 0.0f && _window_buttons->GetOpacity() == 0.0f)))
00403     {
00404       draw_faded_title = true;
00405     }
00406 
00407     if (draw_faded_title)
00408     {
00409       bool build_gradient = false;
00410       nux::SURFACE_LOCKED_RECT lockrect;
00411       lockrect.pBits = 0;
00412       bool locked = false;
00413 
00414       if (_gradient_texture.IsNull() || (_gradient_texture->GetWidth() != geo.width))
00415       {
00416         build_gradient = true;
00417       }
00418       else
00419       {
00420         if (_gradient_texture->LockRect(0, &lockrect, nullptr) != OGL_OK)
00421           build_gradient = true;
00422         else
00423           locked = true;
00424 
00425         if (!lockrect.pBits)
00426         {
00427           build_gradient = true;
00428 
00429           if (locked)
00430             _gradient_texture->UnlockRect(0);
00431         }
00432       }
00433 
00434       if (build_gradient)
00435       {
00436         nux::NTextureData texture_data(nux::BITFMT_R8G8B8A8, geo.width, 1, 1);
00437 
00438         _gradient_texture = nux::GetGraphicsDisplay()->GetGpuDevice()->
00439                             CreateSystemCapableDeviceTexture(texture_data.GetWidth(),
00440                             texture_data.GetHeight(), 1, texture_data.GetFormat());
00441         locked = (_gradient_texture->LockRect(0, &lockrect, nullptr) == OGL_OK);
00442       }
00443 
00444       BYTE* dest_buffer = (BYTE*) lockrect.pBits;
00445       int gradient_opacity = 255.0f * GetOpacity();
00446       int buttons_opacity = 255.0f * _window_buttons->GetOpacity();
00447 
00448       int first_step = button_width * (factor - 1);
00449       int second_step = button_width * factor;
00450 
00451       for (int x = 0; x < geo.width && dest_buffer && locked; x++)
00452       {
00453         BYTE r, g, b, a;
00454 
00455         r = 223;
00456         g = 219;
00457         b = 210;
00458 
00459         if (x < first_step)
00460         {
00461           int color_increment = (first_step - x) * 4;
00462 
00463           r = CLAMP(r + color_increment, r, 0xff);
00464           g = CLAMP(g + color_increment, g, 0xff);
00465           b = CLAMP(b + color_increment, b, 0xff);
00466           a = 0xff - buttons_opacity;
00467         }
00468         else if (x < second_step)
00469         {
00470           a = 0xff - gradient_opacity * (((float)x - (first_step)) /
00471                                          (float)(button_width));
00472         }
00473         else
00474         {
00475           if (!draw_menus)
00476           {
00477             a = 0xff - gradient_opacity;
00478           }
00479           else
00480           {
00481             // If we're fading-out the title, it's better to quickly hide
00482             // the transparent right-most area
00483             a = CLAMP(0xff - gradient_opacity - 0x55, 0x00, 0xff);
00484           }
00485         }
00486 
00487         *(dest_buffer + 4 * x + 0) = (r * a) / 0xff; //red
00488         *(dest_buffer + 4 * x + 1) = (g * a) / 0xff; //green
00489         *(dest_buffer + 4 * x + 2) = (b * a) / 0xff; //blue
00490         *(dest_buffer + 4 * x + 3) = a;
00491       }
00492 
00493       // FIXME Nux shouldn't make unity to crash if we try to unlock a wrong rect
00494       if (locked)
00495         _gradient_texture->UnlockRect(0);
00496 
00497       GfxContext.GetRenderStates().SetBlend(true, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
00498 
00499       nux::TexCoordXForm texxform0;
00500       nux::TexCoordXForm texxform1;
00501 
00502       // Modulate the checkboard and the gradient texture
00503       GfxContext.QRP_2TexMod(geo.x, geo.y,
00504                              geo.width, geo.height,
00505                              _gradient_texture, texxform0,
00506                              nux::color::White,
00507                              _title_texture->GetDeviceTexture(),
00508                              texxform1,
00509                              nux::color::White);
00510     }
00511     else if (!_overlay_showing)
00512     {
00513       double title_opacity = 0.0f;
00514 
00515       if (_we_control_active && _window_buttons->GetOpacity() == 0.0 &&
00516           (!has_menu || (has_menu && GetOpacity() == 0.0)))
00517       {
00518         title_opacity = 1.0f;
00519       }
00520       else
00521       {
00522         title_opacity = 1.0f;
00523 
00524         if (has_menu)
00525           title_opacity -= MAX(GetOpacity(), _window_buttons->GetOpacity());
00526         else
00527           title_opacity -= _window_buttons->GetOpacity();
00528 
00529         if (!draw_window_buttons && !draw_menus)
00530         {
00531           // If we're fading-out the buttons/menus, let's fade-in quickly the title
00532           title_opacity = CLAMP(title_opacity + 0.1f, 0.0f, 1.0f);
00533         }
00534         else
00535         {
00536           // If we're fading-in the buttons/menus, let's fade-out quickly the title
00537           title_opacity = CLAMP(title_opacity - 0.2f, 0.0f, 1.0f);
00538         }
00539       }
00540 
00541       if (title_opacity > 0.0f)
00542       {
00543         nux::TexCoordXForm texxform;
00544         GfxContext.QRP_1Tex(geo.x, geo.y, geo.width, geo.height,
00545                             _title_texture->GetDeviceTexture(), texxform,
00546                             nux::color::White * title_opacity);
00547       }
00548     }
00549 
00550     GfxContext.GetRenderStates().SetBlend(blend_alpha, blend_src, blend_dest);
00551   }
00552 
00553   nux::GetPainter().PopBackground();
00554 
00555   GfxContext.PopClippingRectangle();
00556 }
00557 
00558 void PanelMenuView::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw)
00559 {
00560   nux::Geometry const& geo = GetGeometry();
00561   bool draw_menus = DrawMenus();
00562   bool draw_buttons = DrawWindowButtons();
00563 
00564   GfxContext.PushClippingRectangle(geo);
00565 
00566   if (draw_menus)
00567   {
00568     for (auto entry : entries_)
00569       entry.second->SetDisabled(false);
00570 
00571     layout_->ProcessDraw(GfxContext, true);
00572 
00573     _fade_out_animator.Stop();
00574 
00575     if (_new_application && !_is_inside)
00576     {
00577       _fade_in_animator.Start(_menus_discovery_fadein, GetOpacity());
00578     }
00579     else
00580     {
00581       _fade_in_animator.Start(GetOpacity());
00582       _new_app_menu_shown = false;
00583     }
00584   }
00585   else
00586   {
00587     for (auto entry : entries_)
00588       entry.second->SetDisabled(true);
00589   }
00590 
00591   if (GetOpacity() != 0.0f && !draw_menus && !_overlay_showing)
00592   {
00593     layout_->ProcessDraw(GfxContext, true);
00594 
00595     _fade_in_animator.Stop();
00596 
00597     if (!_new_app_menu_shown)
00598     {
00599       _fade_out_animator.Start(1.0f - GetOpacity());
00600     }
00601     else
00602     {
00603       _fade_out_animator.Start(_menus_discovery_fadeout, 1.0f - GetOpacity());
00604     }
00605   }
00606 
00607   if (draw_buttons)
00608   {
00609     _window_buttons->ProcessDraw(GfxContext, true);
00610 
00611     if (_window_buttons->GetOpacity() != 1.0f)
00612     {
00613       _fade_out_animator.Stop();
00614       _fade_in_animator.Start(_window_buttons->GetOpacity());
00615     }
00616   }
00617 
00618   if (_window_buttons->GetOpacity() != 0.0f && !draw_buttons)
00619   {
00620     _window_buttons->ProcessDraw(GfxContext, true);
00621     _fade_in_animator.Stop();
00622 
00623     /* If we try to hide only the buttons, then use a faster fadeout */
00624     if (!_fade_out_animator.IsRunning())
00625     {
00626       _fade_out_animator.Start(_menus_fadeout/3, 1.0f - _window_buttons->GetOpacity());
00627     }
00628   }
00629 
00630   GfxContext.PopClippingRectangle();
00631 }
00632 
00633 std::string PanelMenuView::GetActiveViewName(bool use_appname) const
00634 {
00635   std::string label;
00636   BamfWindow* window;
00637 
00638   window = bamf_matcher_get_active_window(_matcher);
00639 
00640   if (BAMF_IS_WINDOW(window))
00641   {
00642     BamfView *view = reinterpret_cast<BamfView*>(window);
00643     std::vector<Window> const& our_xids = nux::XInputWindow::NativeHandleList();
00644     Window window_xid = bamf_window_get_xid(window);
00645 
00646     if (std::find(our_xids.begin(), our_xids.end(), window_xid) != our_xids.end())
00647     {
00648       /* If the active window is an unity window, we need to fallback to the
00649        * top one, anyway we should always avoid to focus unity internal windows */
00650       BamfWindow* top_win = GetBamfWindowForXid(GetTopWindow());
00651 
00652       if (top_win && top_win != window)
00653       {
00654         window = top_win;
00655       }
00656       else
00657       {
00658         return "";
00659       }
00660     }
00661 
00662     if (bamf_window_get_window_type(window) == BAMF_WINDOW_DESKTOP)
00663     {
00664       label = _desktop_name;
00665     }
00666     else if (!IsValidWindow(window_xid))
00667     {
00668        return "";
00669     }
00670 
00671     if (WindowManager::Default()->IsWindowMaximized(window_xid) && !use_appname)
00672     {
00673       label = glib::String(bamf_view_get_name(view)).Str();
00674     }
00675 
00676     if (label.empty())
00677     {
00678       BamfApplication* app;
00679       app = bamf_matcher_get_application_for_window(_matcher, window);
00680 
00681       if (BAMF_IS_APPLICATION(app))
00682       {
00683         view = reinterpret_cast<BamfView*>(app);
00684         label = glib::String(bamf_view_get_name(view)).Str();
00685       }
00686     }
00687 
00688     if (label.empty())
00689     {
00690       view = reinterpret_cast<BamfView*>(window);
00691       label = glib::String(bamf_view_get_name(view)).Str();
00692     }
00693   }
00694 
00695   return label;
00696 }
00697 
00698 void PanelMenuView::DrawTitle(cairo_t *cr_real, nux::Geometry const& geo, std::string const& label) const
00699 {
00700   using namespace panel;
00701   cairo_t* cr;
00702   cairo_pattern_t* linpat;
00703   const int fading_pixels = 35;
00704   int x = MAIN_LEFT_PADDING + TITLE_PADDING + geo.x;
00705   int y = geo.y;
00706 
00707   int text_width = 0;
00708   int text_height = 0;
00709   int text_space = 0;
00710 
00711   // Find out dimensions first
00712   GdkScreen* screen = gdk_screen_get_default();
00713   PangoContext* cxt;
00714   PangoRectangle log_rect;
00715   PangoFontDescription* desc;
00716 
00717   nux::CairoGraphics util_cg(CAIRO_FORMAT_ARGB32, 1, 1);
00718   cr = util_cg.GetContext();
00719 
00720   int dpi = Style::Instance().GetTextDPI();
00721 
00722   std::string font_description(Style::Instance().GetFontDescription(PanelItem::TITLE));
00723   desc = pango_font_description_from_string(font_description.c_str());
00724 
00725   glib::Object<PangoLayout> layout(pango_cairo_create_layout(cr));
00726   pango_layout_set_font_description(layout, desc);
00727   pango_layout_set_markup(layout, label.c_str(), -1);
00728 
00729   cxt = pango_layout_get_context(layout);
00730   pango_cairo_context_set_font_options(cxt, gdk_screen_get_font_options(screen));
00731   pango_cairo_context_set_resolution(cxt, dpi / static_cast<float>(PANGO_SCALE));
00732   pango_layout_context_changed(layout);
00733 
00734   pango_layout_get_extents(layout, nullptr, &log_rect);
00735   text_width = log_rect.width / PANGO_SCALE;
00736   text_height = log_rect.height / PANGO_SCALE;
00737 
00738   pango_font_description_free(desc);
00739   cairo_destroy(cr);
00740 
00741   // Draw the text
00742   GtkStyleContext* style_context = Style::Instance().GetStyleContext();
00743   text_space = geo.width - x;
00744   cr = cr_real;
00745   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00746 
00747   gtk_style_context_save(style_context);
00748 
00749   GtkWidgetPath* widget_path = gtk_widget_path_new();
00750   gtk_widget_path_append_type(widget_path, GTK_TYPE_MENU_BAR);
00751   gtk_widget_path_append_type(widget_path, GTK_TYPE_MENU_ITEM);
00752   gtk_widget_path_iter_set_name(widget_path, -1 , "UnityPanelWidget");
00753 
00754   gtk_style_context_set_path(style_context, widget_path);
00755   gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUBAR);
00756   gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_MENUITEM);
00757 
00758   y += (geo.height - text_height) / 2;
00759 
00760   pango_cairo_update_layout(cr, layout);
00761 
00762   if (text_width > text_space)
00763   {
00764     int out_pixels = text_width - text_space;
00765     int fading_width = out_pixels < fading_pixels ? out_pixels : fading_pixels;
00766 
00767     cairo_push_group(cr);
00768     gtk_render_layout(style_context, cr, x, y, layout);
00769     cairo_pop_group_to_source(cr);
00770 
00771     linpat = cairo_pattern_create_linear(geo.width - fading_width, y, geo.width, y);
00772     cairo_pattern_add_color_stop_rgba(linpat, 0, 0, 0, 0, 1);
00773     cairo_pattern_add_color_stop_rgba(linpat, 1, 0, 0, 0, 0);
00774     cairo_mask(cr, linpat);
00775 
00776     cairo_pattern_destroy(linpat);
00777   }
00778   else
00779   {
00780     gtk_render_layout(style_context, cr, x, y, layout);
00781   }
00782 
00783   x += text_width;
00784 
00785   gtk_widget_path_free(widget_path);
00786   gtk_style_context_restore(style_context);
00787 }
00788 
00789 void PanelMenuView::Refresh(bool force)
00790 {
00791   nux::Geometry const& geo = GetGeometry();
00792 
00793   // We can get into a race that causes the geometry to be wrong as there hasn't been a
00794   // layout cycle before the first callback. This is to protect from that.
00795   if (geo.width > _monitor_geo.width)
00796     return;
00797 
00798   auto win_manager = WindowManager::Default();
00799   std::string new_title;
00800 
00801   if (win_manager->IsScaleActive())
00802   {
00803     if (win_manager->IsScaleActiveForGroup())
00804       new_title = GetActiveViewName(true);
00805     else if (_we_control_active)
00806       new_title = _desktop_name;
00807   }
00808   else if (win_manager->IsExpoActive())
00809   {
00810     new_title = _desktop_name;
00811   }
00812   else if (!_we_control_active)
00813   {
00814     new_title = "";
00815   }
00816   else if (!_switcher_showing && !_launcher_keynav)
00817   {
00818     new_title = GetActiveViewName();
00819     _window_buttons->SetControlledWindow(_active_xid);
00820   }
00821 
00822   if (!_switcher_showing && !_launcher_keynav)
00823   {
00824     if (_panel_title != new_title)
00825     {
00826       _panel_title = new_title;
00827     }
00828     else if (!force && _last_geo == geo && _title_texture)
00829     {
00830       // No need to redraw the title, let's save some CPU time!
00831       return;
00832     }
00833   }
00834 
00835   if (_panel_title.empty())
00836   {
00837     _title_texture = nullptr;
00838     return;
00839   }
00840 
00841   nux::CairoGraphics cairo_graphics(CAIRO_FORMAT_ARGB32, geo.width, geo.height);
00842   cairo_t* cr = cairo_graphics.GetContext();
00843 
00844   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00845   cairo_paint(cr);
00846 
00847   glib::String escaped(g_markup_escape_text(_panel_title.c_str(), -1));
00848 
00849   std::ostringstream bold_label;
00850   bold_label << "<b>" << escaped.Str() << "</b>";
00851 
00852   DrawTitle(cr, geo, bold_label.str());
00853 
00854   cairo_destroy(cr);
00855 
00856   _title_texture = texture_ptr_from_cairo_graphics(cairo_graphics);
00857 }
00858 
00859 void PanelMenuView::OnActiveChanged(PanelIndicatorEntryView* view, bool is_active)
00860 {
00861   if (is_active)
00862   {
00863     _last_active_view = view;
00864   }
00865   else
00866   {
00867     if (_last_active_view == view)
00868     {
00869       _last_active_view = nullptr;
00870     }
00871   }
00872 
00873   Refresh();
00874   FullRedraw();
00875 }
00876 
00877 void PanelMenuView::OnEntryAdded(indicator::Entry::Ptr const& entry)
00878 {
00879   PanelIndicatorEntryView* view;
00880 
00881   view = new PanelIndicatorEntryView(entry, MENU_ENTRIES_PADDING, IndicatorEntryType::MENU);
00882   view->mouse_enter.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseEnter));
00883   view->mouse_leave.connect(sigc::mem_fun(this, &PanelMenuView::OnPanelViewMouseLeave));
00884 
00885   entry->show_now_changed.connect(sigc::mem_fun(this, &PanelMenuView::UpdateShowNow));
00886   view->active_changed.connect(sigc::mem_fun(this, &PanelMenuView::OnActiveChanged));
00887 
00888   AddEntryView(view, IndicatorEntryPosition::END);
00889 }
00890 
00891 void PanelMenuView::NotifyAllMenusClosed()
00892 {
00893   _last_active_view = nullptr;
00894 
00895   auto mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
00896   _is_inside = GetAbsoluteGeometry().IsInside(mouse);
00897   FullRedraw();
00898 }
00899 
00900 void PanelMenuView::OnNameChanged(BamfView* bamf_view, gchar* new_name, gchar* old_name)
00901 {
00902   Refresh();
00903   FullRedraw();
00904 }
00905 
00906 bool PanelMenuView::OnNewAppShow()
00907 {
00908   BamfApplication* active_app = bamf_matcher_get_active_application(_matcher);
00909   _new_application = glib::Object<BamfApplication>(active_app, glib::AddRef());
00910   QueueDraw();
00911 
00912   if (_sources.GetSource(NEW_APP_HIDE_TIMEOUT))
00913   {
00914     _new_app_menu_shown = false;
00915   }
00916 
00917   auto timeout = std::make_shared<glib::TimeoutSeconds>(_menus_discovery);
00918   _sources.Add(timeout, NEW_APP_HIDE_TIMEOUT);
00919   timeout->Run(sigc::mem_fun(this, &PanelMenuView::OnNewAppHide));
00920 
00921   return false;
00922 }
00923 
00924 bool PanelMenuView::OnNewAppHide()
00925 {
00926   OnApplicationClosed(_new_application);
00927   _new_app_menu_shown = true;
00928   QueueDraw();
00929 
00930   return false;
00931 }
00932 
00933 void PanelMenuView::OnViewOpened(BamfMatcher *matcher, BamfView *view)
00934 {
00935   /* FIXME: here we should also check for if the view is also user_visible
00936    * but it seems that BAMF doesn't handle this correctly after some
00937    * stress tests (repeated launches). */
00938   if (!BAMF_IS_APPLICATION(view))
00939     return;
00940 
00941   _new_apps.push_front(glib::Object<BamfApplication>(BAMF_APPLICATION(view), glib::AddRef()));
00942 }
00943 
00944 void PanelMenuView::OnApplicationClosed(BamfApplication* app)
00945 {
00946   if (BAMF_IS_APPLICATION(app))
00947   {
00948     if (std::find(_new_apps.begin(), _new_apps.end(), app) != _new_apps.end())
00949     {
00950       _new_apps.remove(glib::Object<BamfApplication>(app, glib::AddRef()));
00951     }
00952     else if (_new_apps.empty())
00953     {
00954       _new_application = nullptr;
00955     }
00956   }
00957 
00958   if (app == _new_application)
00959   {
00960     _new_application = nullptr;
00961   }
00962 }
00963 
00964 void PanelMenuView::OnViewClosed(BamfMatcher *matcher, BamfView *view)
00965 {
00966   if (reinterpret_cast<BamfView*>(_view_name_changed_signal.object()) == view)
00967   {
00968     _view_name_changed_signal.Disconnect();
00969   }
00970 
00971   if (BAMF_IS_APPLICATION(view))
00972   {
00973     OnApplicationClosed(reinterpret_cast<BamfApplication*>(view));
00974   }
00975   else if (reinterpret_cast<BamfApplication*>(view) == _new_application)
00976   {
00977     _new_application = nullptr;
00978   }
00979   else if (BAMF_IS_WINDOW(view))
00980   {
00981     /* FIXME, this can be removed when window_unmapped WindowManager signal
00982      * will emit the proper xid */
00983     Window xid = bamf_window_get_xid(reinterpret_cast<BamfWindow*>(view));
00984     OnWindowUnmapped(xid);
00985   }
00986 }
00987 
00988 void PanelMenuView::OnActiveAppChanged(BamfMatcher *matcher,
00989                                        BamfApplication* old_app,
00990                                        BamfApplication* new_app)
00991 {
00992   if (BAMF_IS_APPLICATION(new_app))
00993   {
00994     if (std::find(_new_apps.begin(), _new_apps.end(), new_app) != _new_apps.end())
00995     {
00996       if (_new_application != new_app)
00997       {
00998         /* Add a small delay before showing the menus, this is done both
00999          * to fix the issues with applications that takes some time to loads
01000          * menus and to show the menus only when an application has been
01001          * kept active for some time */
01002 
01003         auto timeout = std::make_shared<glib::Timeout>(300);
01004         _sources.Add(timeout, NEW_APP_SHOW_TIMEOUT);
01005         timeout->Run(sigc::mem_fun(this, &PanelMenuView::OnNewAppShow));
01006       }
01007     }
01008     else
01009     {
01010       _sources.Remove(NEW_APP_SHOW_TIMEOUT);
01011 
01012       if (_sources.GetSource(NEW_APP_HIDE_TIMEOUT))
01013       {
01014         _sources.Remove(NEW_APP_HIDE_TIMEOUT);
01015         _new_app_menu_shown = false;
01016       }
01017 
01018       if (_new_application)
01019         OnApplicationClosed(_new_application);
01020     }
01021   }
01022 }
01023 
01024 void PanelMenuView::OnActiveWindowChanged(BamfMatcher *matcher,
01025                                           BamfView* old_view,
01026                                           BamfView* new_view)
01027 {
01028   _show_now_activated = false;
01029   _is_maximized = false;
01030   _active_xid = 0;
01031 
01032   _sources.Remove(WINDOW_MOVED_TIMEOUT);
01033 
01034   if (BAMF_IS_WINDOW(new_view))
01035   {
01036     WindowManager *wm = WindowManager::Default();
01037     BamfWindow* window = reinterpret_cast<BamfWindow*>(new_view);
01038     guint32 xid = bamf_window_get_xid(window);
01039     _active_xid = xid;
01040     _is_maximized = wm->IsWindowMaximized(xid);
01041 
01042     if (bamf_window_get_window_type(window) == BAMF_WINDOW_DESKTOP)
01043       _we_control_active = true;
01044     else
01045       _we_control_active = IsWindowUnderOurControl(xid);
01046 
01047     if (_decor_map.find(xid) == _decor_map.end())
01048     {
01049       _decor_map[xid] = true;
01050 
01051       // if we've just started tracking this window and it is maximized, let's
01052       // make sure it's undecorated just in case it slipped by us earlier
01053       // (I'm looking at you, Chromium!)
01054       if (_is_maximized && wm->IsWindowDecorated(xid))
01055       {
01056         wm->Undecorate(xid);
01057         _maximized_set.insert(xid);
01058       }
01059     }
01060 
01061     // first see if we need to remove and old callback
01062     _view_name_changed_signal.Disconnect();
01063 
01064     // register callback for new view
01065     _view_name_changed_signal.Connect(new_view, "name-changed",
01066                                       sigc::mem_fun(this, &PanelMenuView::OnNameChanged));
01067 
01068     _window_buttons->SetControlledWindow(_is_maximized ? _active_xid : 0);
01069   }
01070 
01071   Refresh();
01072   FullRedraw();
01073 }
01074 
01075 void PanelMenuView::OnSpreadInitiate()
01076 {
01077   /*foreach (guint32 &xid, windows)
01078   {
01079     if (WindowManager::Default()->IsWindowMaximized(xid))
01080       WindowManager::Default()->Decorate(xid);
01081   }*/
01082 
01083   Refresh();
01084   QueueDraw();
01085 }
01086 
01087 void PanelMenuView::OnSpreadTerminate()
01088 {
01089   /*foreach (guint32 &xid, windows)
01090   {
01091     if (WindowManager::Default()->IsWindowMaximized(xid))
01092       WindowManager::Default()->Undecorate(xid);
01093   }*/
01094 
01095   Refresh();
01096   QueueDraw();
01097 }
01098 
01099 void PanelMenuView::OnExpoInitiate()
01100 {
01101   Refresh();
01102   QueueDraw();
01103 }
01104 
01105 void PanelMenuView::OnExpoTerminate()
01106 {
01107   Refresh();
01108   QueueDraw();
01109 }
01110 
01111 void PanelMenuView::OnWindowMinimized(guint32 xid)
01112 {
01113   if (WindowManager::Default()->IsWindowMaximized(xid))
01114   {
01115     WindowManager::Default()->Decorate(xid);
01116     _maximized_set.erase(xid);
01117 
01118     Refresh();
01119     QueueDraw();
01120   }
01121 }
01122 
01123 void PanelMenuView::OnWindowUnminimized(guint32 xid)
01124 {
01125   if (WindowManager::Default()->IsWindowMaximized(xid))
01126   {
01127     WindowManager::Default()->Undecorate(xid);
01128     _maximized_set.insert(xid);
01129 
01130     Refresh();
01131     QueueDraw();
01132   }
01133 }
01134 
01135 void PanelMenuView::OnWindowUnmapped(guint32 xid)
01136 {
01137   // FIXME: compiz doesn't give us a valid xid (is always 0 on unmap)
01138   // we need to do this again on BamfView closed signal.
01139   if (_maximized_set.find(xid) != _maximized_set.end())
01140   {
01141     WindowManager::Default()->Decorate(xid);
01142     _maximized_set.erase(xid);
01143     _decor_map.erase(xid);
01144 
01145     Refresh();
01146     QueueDraw();
01147   }
01148 }
01149 
01150 void PanelMenuView::OnWindowMapped(guint32 xid)
01151 {
01152   if (WindowManager::Default()->IsWindowMaximized(xid))
01153   {
01154     WindowManager::Default()->Undecorate(xid);
01155     _maximized_set.insert(xid);
01156 
01157     Refresh();
01158     QueueDraw();
01159   }
01160 }
01161 
01162 void PanelMenuView::OnWindowDecorated(guint32 xid)
01163 {
01164   _decor_map[xid] = true;
01165 
01166   if (_maximized_set.find(xid) != _maximized_set.end ())
01167   {
01168     WindowManager::Default()->Undecorate(xid);
01169   }
01170 }
01171 
01172 void PanelMenuView::OnWindowUndecorated(guint32 xid)
01173 {
01174   _decor_map[xid] = false;
01175 }
01176 
01177 void PanelMenuView::OnWindowMaximized(guint xid)
01178 {
01179   bool updated = false;
01180   bool is_active = (_active_xid == xid);
01181 
01182   if (is_active)
01183   {
01184     // We need to update the _is_inside state in the case of maximization by grab
01185     auto mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
01186     _is_inside = GetAbsoluteGeometry().IsInside(mouse);
01187 
01188     _is_maximized = true;
01189     updated = true;
01190   }
01191 
01192   // update the state of the window in the _decor_map
01193   _decor_map[xid] = WindowManager::Default()->IsWindowDecorated(xid);
01194 
01195   if (_decor_map[xid])
01196     WindowManager::Default()->Undecorate(xid);
01197 
01198   _maximized_set.insert(xid);
01199 
01200   if (updated)
01201   {
01202     Refresh();
01203     FullRedraw();
01204   }
01205 }
01206 
01207 void PanelMenuView::OnWindowRestored(guint xid)
01208 {
01209   if (_maximized_set.find(xid) == _maximized_set.end())
01210     return;
01211 
01212   if (_active_xid == xid)
01213   {
01214     _is_maximized = false;
01215     _is_grabbed = false;
01216   }
01217 
01218   if (_decor_map[xid])
01219     WindowManager::Default()->Decorate(xid);
01220 
01221   _maximized_set.erase(xid);
01222 
01223   Refresh();
01224   FullRedraw();
01225 }
01226 
01227 bool PanelMenuView::UpdateActiveWindowPosition()
01228 {
01229   bool we_control_window = IsWindowUnderOurControl(_active_xid);
01230 
01231   if (we_control_window != _we_control_active)
01232   {
01233     _we_control_active = we_control_window;
01234 
01235     Refresh();
01236     QueueDraw();
01237   }
01238 
01239   return false;
01240 }
01241 
01242 void PanelMenuView::OnWindowMoved(guint xid)
01243 {
01244   if (_active_xid == xid)
01245   {
01246     /* When moving the active window, if the current panel is controlling
01247      * the active window, then we postpone the timeout function every movement
01248      * that we have, setting a longer timeout.
01249      * Otherwise, if the moved window is not controlled by the current panel
01250      * every few millisecond we check the new window position */
01251 
01252     unsigned int timeout_length = 250;
01253 
01254     if (_we_control_active)
01255     {
01256       _sources.Remove(WINDOW_MOVED_TIMEOUT);
01257     }
01258     else
01259     {
01260       if (_sources.GetSource(WINDOW_MOVED_TIMEOUT))
01261         return;
01262 
01263       timeout_length = 60;
01264     }
01265 
01266     auto timeout = std::make_shared<glib::Timeout>(timeout_length);
01267     _sources.Add(timeout, WINDOW_MOVED_TIMEOUT);
01268     timeout->Run(sigc::mem_fun(this, &PanelMenuView::UpdateActiveWindowPosition));
01269   }
01270 }
01271 
01272 bool PanelMenuView::IsWindowUnderOurControl(Window xid) const
01273 {
01274   if (UScreen::GetDefault()->GetMonitors().size() > 1)
01275   {
01276     auto wm = WindowManager::Default();
01277     nux::Geometry const& window_geo = wm->GetWindowGeometry(xid);
01278     nux::Geometry const& intersect = _monitor_geo.Intersect(window_geo);
01279 
01280     /* We only care of the horizontal window portion */
01281     return (intersect.width > window_geo.width/2 && intersect.height > 0);
01282   }
01283 
01284   return true;
01285 }
01286 
01287 bool PanelMenuView::IsValidWindow(Window xid) const
01288 {
01289   auto wm = WindowManager::Default();
01290   std::vector<Window> const& our_xids = nux::XInputWindow::NativeHandleList();
01291 
01292   if (wm->IsWindowOnCurrentDesktop(xid) && !wm->IsWindowObscured(xid) &&
01293       wm->IsWindowVisible(xid) && IsWindowUnderOurControl(xid) &&
01294       std::find(our_xids.begin(), our_xids.end(), xid) == our_xids.end())
01295   {
01296     return true;
01297   }
01298 
01299   return false;
01300 }
01301 
01302 Window PanelMenuView::GetMaximizedWindow() const
01303 {
01304   Window window_xid = 0;
01305 
01306   // Find the front-most of the maximized windows we are controlling
01307   for (auto xid : _maximized_set)
01308   {
01309     // We can safely assume only the front-most is visible
01310     if (IsValidWindow(xid))
01311     {
01312       window_xid = xid;
01313       break;
01314     }
01315   }
01316 
01317   return window_xid;
01318 }
01319 
01320 Window PanelMenuView::GetTopWindow() const
01321 {
01322   Window window_xid = 0;
01323   GList* windows = bamf_matcher_get_window_stack_for_monitor(_matcher, _monitor);
01324 
01325   for (GList* l = windows; l; l = l->next)
01326   {
01327     if (!BAMF_IS_WINDOW(l->data))
01328       continue;
01329 
01330     Window xid = bamf_window_get_xid(static_cast<BamfWindow*>(l->data));
01331     bool visible = bamf_view_user_visible(static_cast<BamfView*>(l->data));
01332 
01333     if (visible && IsValidWindow(xid))
01334     {
01335       window_xid = xid;
01336     }
01337   }
01338 
01339   g_list_free(windows);
01340 
01341   return window_xid;
01342 }
01343 
01344 BamfWindow* PanelMenuView::GetBamfWindowForXid(Window xid) const
01345 {
01346   BamfWindow* window = nullptr;
01347 
01348   if (xid != 0)
01349   {
01350     GList* windows = bamf_matcher_get_windows(_matcher);
01351 
01352     for (GList* l = windows; l; l = l->next)
01353     {
01354       if (!BAMF_IS_WINDOW(l->data))
01355         continue;
01356 
01357       auto win = static_cast<BamfWindow*>(l->data);
01358 
01359       if (bamf_window_get_xid(win) == xid)
01360       {
01361         window = win;
01362         break;
01363       }
01364     }
01365 
01366     g_list_free(windows);
01367   }
01368 
01369   return window;
01370 }
01371 
01372 void PanelMenuView::OnMaximizedActivate(int x, int y)
01373 {
01374   Window maximized = GetMaximizedWindow();
01375 
01376   if (maximized != 0)
01377   {
01378     WindowManager::Default()->Activate(maximized);
01379   }
01380 }
01381 
01382 void PanelMenuView::OnMaximizedRestore(int x, int y)
01383 {
01384   if (_overlay_showing)
01385     return;
01386 
01387   Window maximized = GetMaximizedWindow();
01388 
01389   if (maximized != 0)
01390   {
01391     WindowManager::Default()->Restore(maximized);
01392     _is_inside = true;
01393   }
01394 }
01395 
01396 void PanelMenuView::OnMaximizedLower(int x, int y)
01397 {
01398   if (_overlay_showing)
01399     return;
01400 
01401   Window maximized = GetMaximizedWindow();
01402 
01403   if (maximized != 0)
01404   {
01405     WindowManager::Default()->Lower(maximized);
01406   }
01407 }
01408 
01409 void PanelMenuView::OnMaximizedGrabStart(int x, int y)
01410 {
01411   /* When Start dragging the panelmenu of a maximized window, change cursor
01412    * to simulate the dragging, waiting to go out of the panel area.
01413    *
01414    * This is a workaround to avoid that the grid plugin would be fired
01415    * showing the window shape preview effect. See bug #838923 */
01416 
01417   Window maximized = GetMaximizedWindow();
01418 
01419   if (maximized != 0)
01420   {
01421     /* Always activate the window in case it is on another monitor */
01422     WindowManager::Default()->Activate(maximized);
01423     _titlebar_grab_area->SetGrabbed(true);
01424   }
01425 }
01426 
01427 void PanelMenuView::OnMaximizedGrabMove(int x, int y)
01428 {
01429   auto panel = static_cast<nux::BaseWindow*>(GetTopLevelViewWindow());
01430 
01431   if (!panel)
01432     return;
01433 
01434   /* Adjusting the x, y coordinates to get the absolute values */
01435   x += _titlebar_grab_area->GetAbsoluteX();
01436   y += _titlebar_grab_area->GetAbsoluteY();
01437 
01438   Window maximized = GetMaximizedWindow();
01439 
01440   /* When the drag goes out from the Panel, start the real movement.
01441    *
01442    * This is a workaround to avoid that the grid plugin would be fired
01443    * showing the window shape preview effect. See bug #838923 */
01444   if (maximized != 0 && panel)
01445   {
01446     nux::Geometry const& panel_geo = panel->GetAbsoluteGeometry();
01447 
01448     if (!panel_geo.IsPointInside(x, y))
01449     {
01450       auto wm = WindowManager::Default();
01451       nux::Geometry const& restored_geo = wm->GetWindowSavedGeometry(maximized);
01452       nux::Geometry const& workarea_geo = wm->GetWorkAreaGeometry(maximized);
01453 
01454       /* By default try to restore the window horizontally-centered respect to the
01455        * pointer position, if it doesn't fit on that area try to keep it into the
01456        * current workarea as much as possible, but giving priority to the left border
01457        * that shouldn't be never put out of the workarea */
01458       int restore_x = x - (restored_geo.width * x / panel_geo.width);
01459       int restore_y = y;
01460 
01461       if (restore_x + restored_geo.width > workarea_geo.x + workarea_geo.width)
01462       {
01463         restore_x = workarea_geo.x + workarea_geo.width - restored_geo.width;
01464       }
01465 
01466       if (restore_x < workarea_geo.x)
01467       {
01468         restore_x = workarea_geo.x;
01469       }
01470 
01471       wm->Activate(maximized);
01472       wm->RestoreAt(maximized, restore_x, restore_y);
01473 
01474       _is_inside = true;
01475       _is_grabbed = true;
01476       Refresh();
01477       FullRedraw();
01478 
01479       /* Ungrab the pointer and start the X move, to make the decorator handle it */
01480       _titlebar_grab_area->SetGrabbed(false);
01481       wm->StartMove(maximized, x, y);
01482     }
01483   }
01484 }
01485 
01486 void PanelMenuView::OnMaximizedGrabEnd(int x, int y)
01487 {
01488   _titlebar_grab_area->SetGrabbed(false);
01489 
01490   x += _titlebar_grab_area->GetAbsoluteX();
01491   y += _titlebar_grab_area->GetAbsoluteY();
01492   _is_inside = GetAbsoluteGeometry().IsPointInside(x, y);
01493 
01494   if (!_is_inside)
01495     _is_grabbed = false;
01496 
01497   Refresh();
01498   FullRedraw();
01499 }
01500 
01501 // Introspectable
01502 std::string
01503 PanelMenuView::GetName() const
01504 {
01505   return "MenuView";
01506 }
01507 
01508 void PanelMenuView::AddProperties(GVariantBuilder* builder)
01509 {
01510   PanelIndicatorsView::AddProperties(builder);
01511 
01512   variant::BuilderWrapper(builder)
01513   .add("mouse_inside", _is_inside)
01514   .add("grabbed", _is_grabbed)
01515   .add("active_win_maximized", _is_maximized)
01516   .add("panel_title", _panel_title)
01517   .add("desktop_active", (_panel_title == _desktop_name))
01518   .add("monitor", _monitor)
01519   .add("active_window", _active_xid)
01520   .add("draw_menus", DrawMenus())
01521   .add("draw_window_buttons", DrawWindowButtons())
01522   .add("controls_active_window", _we_control_active)
01523   .add("fadein_duration", _menus_fadein)
01524   .add("fadeout_duration", _menus_fadeout)
01525   .add("discovery_duration", _menus_discovery)
01526   .add("discovery_fadein_duration", _menus_discovery_fadein)
01527   .add("discovery_fadeout_duration", _menus_discovery_fadeout);
01528 }
01529 
01530 void PanelMenuView::OnSwitcherShown(GVariant* data)
01531 {
01532   if (!data)
01533     return;
01534 
01535   gboolean switcher_shown;
01536   gint monitor;
01537   g_variant_get(data, "(bi)", &switcher_shown, &monitor);
01538 
01539   if (switcher_shown == _switcher_showing || monitor != _monitor)
01540     return;
01541 
01542   _switcher_showing = switcher_shown;
01543 
01544   if (!_switcher_showing)
01545   {
01546     auto mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
01547     _is_inside = GetAbsoluteGeometry().IsInside(mouse);
01548   }
01549   else
01550   {
01551     _show_now_activated = false;
01552   }
01553 
01554   Refresh();
01555   QueueDraw();
01556 }
01557 
01558 void PanelMenuView::OnSwitcherSelectionChanged(GVariant* data)
01559 {
01560   if (!data || !_switcher_showing)
01561     return;
01562 
01563   const gchar *title = g_variant_get_string(data, 0);
01564   _panel_title = (title ? title : "");
01565 
01566   Refresh();
01567   QueueDraw();
01568 }
01569 
01570 void PanelMenuView::OnLauncherKeyNavStarted(GVariant* data)
01571 {
01572   if (_launcher_keynav)
01573     return;
01574 
01575 
01576   if (!data || (data && g_variant_get_int32(data) == _monitor))
01577   {
01578     _launcher_keynav = true;
01579   }
01580 }
01581 
01582 void PanelMenuView::OnLauncherKeyNavEnded(GVariant* data)
01583 {
01584   if (!_launcher_keynav)
01585     return;
01586 
01587   _launcher_keynav = false;
01588 
01589   auto mouse = nux::GetGraphicsDisplay()->GetMouseScreenCoord();
01590   _is_inside = GetAbsoluteGeometry().IsInside(mouse);
01591 
01592   Refresh();
01593   QueueDraw();
01594 }
01595 
01596 void PanelMenuView::OnLauncherSelectionChanged(GVariant* data)
01597 {
01598   if (!data || !_launcher_keynav)
01599     return;
01600 
01601   const gchar *title = g_variant_get_string(data, 0);
01602   _panel_title = (title ? title : "");
01603 
01604   Refresh();
01605   QueueDraw();
01606 }
01607 
01608 bool PanelMenuView::UpdateShowNowWithDelay()
01609 {
01610   bool active = false;
01611 
01612   for (auto entry : entries_)
01613   {
01614     if (entry.second->GetShowNow())
01615     {
01616       active = true;
01617       break;
01618     }
01619   }
01620 
01621   if (active)
01622   {
01623     _show_now_activated = true;
01624     QueueDraw();
01625   }
01626 
01627   return false;
01628 }
01629 
01630 void PanelMenuView::UpdateShowNow(bool status)
01631 {
01632   /* When we get a show now event, if we are requested to show the menus,
01633    * we take the last incoming event and we wait for small delay (to avoid the
01634    * Alt+Tab conflict) then we check if any menuitem has requested to show.
01635    * If the status is false, we just check that the menus entries are hidden
01636    * and we remove any eventual delayed request */
01637 
01638    _sources.Remove(UPDATE_SHOW_NOW_TIMEOUT);
01639 
01640   if (!status && _show_now_activated)
01641   {
01642     _show_now_activated = false;
01643     QueueDraw();
01644     return;
01645   }
01646 
01647   if (status && !_show_now_activated)
01648   {
01649     auto timeout = std::make_shared<glib::Timeout>(180);
01650     _sources.Add(timeout, UPDATE_SHOW_NOW_TIMEOUT);
01651     timeout->Run(sigc::mem_fun(this, &PanelMenuView::UpdateShowNowWithDelay));
01652   }
01653 }
01654 
01655 void PanelMenuView::SetMonitor(int monitor)
01656 {
01657   _monitor = monitor;
01658   _monitor_geo = UScreen::GetDefault()->GetMonitorGeometry(_monitor);
01659 
01660   _maximized_set.clear();
01661   GList* windows = bamf_matcher_get_window_stack_for_monitor(_matcher, _monitor);
01662 
01663   for (GList* l = windows; l; l = l->next)
01664   {
01665     if (!BAMF_IS_WINDOW(l->data))
01666       continue;
01667 
01668     auto window = static_cast<BamfWindow*>(l->data);
01669     auto view = static_cast<BamfView*>(l->data);
01670 
01671     if (bamf_view_is_active(view))
01672     {
01673       _active_xid = bamf_window_get_xid(window);
01674     }
01675 
01676     if (bamf_window_maximized(window) == BAMF_WINDOW_MAXIMIZED)
01677     {
01678       Window xid = bamf_window_get_xid(window);
01679 
01680       _decor_map[xid] = WindowManager::Default()->IsWindowDecorated(xid);
01681 
01682       if (_decor_map[xid])
01683         WindowManager::Default()->Undecorate(xid);
01684 
01685       _maximized_set.insert(xid);
01686     }
01687   }
01688 
01689   Window maximized = GetMaximizedWindow();
01690   Window buttons_win = (maximized == _active_xid) ? maximized : 0;
01691 
01692   _window_buttons->SetMonitor(_monitor);
01693   _window_buttons->SetControlledWindow(buttons_win);
01694 
01695   g_list_free(windows);
01696 }
01697 
01698 bool PanelMenuView::GetControlsActive() const
01699 {
01700   return _we_control_active;
01701 }
01702 
01703 void PanelMenuView::OnPanelViewMouseEnter(int x, int y, unsigned long mouse_button_state, unsigned long special_keys_state)
01704 {
01705   if (!_is_inside)
01706   {
01707     if (_is_grabbed)
01708       _is_grabbed = false;
01709     else
01710       _is_inside = true;
01711 
01712     FullRedraw();
01713   }
01714 }
01715 
01716 void PanelMenuView::OnPanelViewMouseLeave(int x, int y, unsigned long mouse_button_state, unsigned long special_keys_state)
01717 {
01718   if (_is_inside)
01719   {
01720     _is_inside = false;
01721     FullRedraw();
01722   }
01723 }
01724 
01725 void PanelMenuView::OnPanelViewMouseMove(int x, int y, int dx, int dy, unsigned long mouse_button_state, unsigned long special_keys_state)
01726 {}
01727 
01728 void PanelMenuView::SetMousePosition(int x, int y)
01729 {
01730   if (_last_active_view ||
01731       (x >= 0 && y >= 0 && GetAbsoluteGeometry().IsPointInside(x, y)))
01732   {
01733     if (!_is_inside)
01734     {
01735       _is_inside = true;
01736       FullRedraw();
01737     }
01738   }
01739   else
01740   {
01741     if (_is_inside)
01742     {
01743       _is_inside = false;
01744       FullRedraw();
01745     }
01746   }
01747 }
01748 } // namespace unity