Back to index

unity  6.0.0
LauncherIcon.cpp
Go to the documentation of this file.
00001 // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
00002 /*
00003  * Copyright (C) 2010 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: Jason Smith <jason.smith@canonical.com>
00018  */
00019 
00020 #include <sys/time.h>
00021 
00022 #include <Nux/Nux.h>
00023 #include <Nux/VScrollBar.h>
00024 #include <Nux/HLayout.h>
00025 #include <Nux/VLayout.h>
00026 #include <Nux/MenuPage.h>
00027 #include <Nux/WindowCompositor.h>
00028 #include <Nux/BaseWindow.h>
00029 #include <Nux/MenuPage.h>
00030 #include <NuxCore/Color.h>
00031 #include <NuxCore/Logger.h>
00032 
00033 #include "unity-shared/CairoTexture.h"
00034 #include "LauncherIcon.h"
00035 #include "Launcher.h"
00036 #include "unity-shared/TimeUtil.h"
00037 
00038 #include "QuicklistManager.h"
00039 #include "QuicklistMenuItem.h"
00040 #include "QuicklistMenuItemLabel.h"
00041 #include "QuicklistMenuItemSeparator.h"
00042 #include "QuicklistMenuItemCheckmark.h"
00043 #include "QuicklistMenuItemRadio.h"
00044 
00045 #include "MultiMonitor.h"
00046 #include "unity-shared/WindowManager.h"
00047 
00048 #include "unity-shared/ubus-server.h"
00049 #include "unity-shared/UBusMessages.h"
00050 #include <UnityCore/GLibWrapper.h>
00051 #include <UnityCore/Variant.h>
00052 
00053 namespace unity
00054 {
00055 namespace launcher
00056 {
00057 
00058 namespace
00059 {
00060 nux::logging::Logger logger("unity.launcher");
00061 const std::string DEFAULT_ICON = "application-default-icon";
00062 const std::string MONO_TEST_ICON = "gnome-home";
00063 const std::string UNITY_THEME_NAME = "unity-icon-theme";
00064 
00065 const std::string CENTER_STABILIZE_TIMEOUT = "center-stabilize-timeout";
00066 const std::string PRESENT_TIMEOUT = "present-timeout";
00067 const std::string QUIRK_DELAY_TIMEOUT = "quirk-delay-timeout";
00068 }
00069 
00070 NUX_IMPLEMENT_OBJECT_TYPE(LauncherIcon);
00071 
00072 int LauncherIcon::_current_theme_is_mono = -1;
00073 glib::Object<GtkIconTheme> LauncherIcon::_unity_theme;
00074 
00075 LauncherIcon::LauncherIcon()
00076   : _remote_urgent(false)
00077   , _present_urgency(0)
00078   , _progress(0)
00079   , _sort_priority(0)
00080   , _last_monitor(0)
00081   , _background_color(nux::color::White)
00082   , _glow_color(nux::color::White)
00083   , _shortcut(0)
00084   , _icon_type(TYPE_NONE)
00085   , _center(max_num_monitors)
00086   , _has_visible_window(max_num_monitors)
00087   , _last_stable(max_num_monitors)
00088   , _parent_geo(max_num_monitors)
00089   , _saved_center(max_num_monitors)
00090 {
00091   for (int i = 0; i < QUIRK_LAST; i++)
00092   {
00093     _quirks[i] = false;
00094     _quirk_times[i].tv_sec = 0;
00095     _quirk_times[i].tv_nsec = 0;
00096   }
00097 
00098   _is_visible_on_monitor.resize(max_num_monitors);
00099 
00100   for (int i = 0; i < max_num_monitors; ++i)
00101     _is_visible_on_monitor[i] = true;
00102 
00103   tooltip_enabled = true;
00104   tooltip_enabled.changed.connect(sigc::mem_fun(this, &LauncherIcon::OnTooltipEnabledChanged));
00105 
00106   tooltip_text.SetSetterFunction(sigc::mem_fun(this, &LauncherIcon::SetTooltipText));
00107   tooltip_text = "blank";
00108 
00109   // FIXME: the abstraction is already broken, should be fixed for O
00110   // right now, hooking the dynamic quicklist the less ugly possible way
00111 
00112 
00113   mouse_enter.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseEnter));
00114   mouse_leave.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseLeave));
00115   mouse_down.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseDown));
00116   mouse_up.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseUp));
00117   mouse_click.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseClick));
00118 }
00119 
00120 LauncherIcon::~LauncherIcon()
00121 {
00122   SetQuirk(QUIRK_URGENT, false);
00123 
00124   // clean up the whole signal-callback mess
00125   if (needs_redraw_connection.connected())
00126     needs_redraw_connection.disconnect();
00127 
00128   if (on_icon_added_connection.connected())
00129     on_icon_added_connection.disconnect();
00130 
00131   if (on_icon_removed_connection.connected())
00132     on_icon_removed_connection.disconnect();
00133 
00134   if (on_order_changed_connection.connected())
00135     on_order_changed_connection.disconnect();
00136 
00137   if (_unity_theme)
00138   {
00139     _unity_theme = NULL;
00140   }
00141 }
00142 
00143 void LauncherIcon::LoadTooltip()
00144 {
00145   _tooltip = new Tooltip();
00146   AddChild(_tooltip.GetPointer());
00147 
00148   _tooltip->SetText(tooltip_text());
00149 }
00150 
00151 void LauncherIcon::LoadQuicklist()
00152 {
00153   _quicklist = new QuicklistView();
00154   AddChild(_quicklist.GetPointer());
00155 
00156   QuicklistManager::Default()->RegisterQuicklist(_quicklist.GetPointer());
00157 }
00158 
00159 const bool
00160 LauncherIcon::WindowVisibleOnMonitor(int monitor)
00161 {
00162   return _has_visible_window[monitor];
00163 }
00164 
00165 const bool LauncherIcon::WindowVisibleOnViewport()
00166 {
00167   for (int i = 0; i < max_num_monitors; ++i)
00168     if (_has_visible_window[i])
00169       return true;
00170 
00171   return false;
00172 }
00173 
00174 std::string
00175 LauncherIcon::GetName() const
00176 {
00177   return "LauncherIcon";
00178 }
00179 
00180 void
00181 LauncherIcon::AddProperties(GVariantBuilder* builder)
00182 {
00183   GVariantBuilder monitors_builder;
00184   g_variant_builder_init(&monitors_builder, G_VARIANT_TYPE ("ab"));
00185 
00186   for (int i = 0; i < max_num_monitors; ++i)
00187     g_variant_builder_add(&monitors_builder, "b", IsVisibleOnMonitor(i));
00188 
00189   unity::variant::BuilderWrapper(builder)
00190   .add("center_x", _center[0].x)
00191   .add("center_y", _center[0].y)
00192   .add("center_z", _center[0].z)
00193   .add("related_windows", static_cast<unsigned int>(Windows().size()))
00194   .add("icon_type", _icon_type)
00195   .add("tooltip_text", tooltip_text())
00196   .add("sort_priority", _sort_priority)
00197   .add("shortcut", _shortcut)
00198   .add("monitors_visibility", g_variant_builder_end(&monitors_builder))
00199   .add("active", GetQuirk(QUIRK_ACTIVE))
00200   .add("visible", GetQuirk(QUIRK_VISIBLE))
00201   .add("urgent", GetQuirk(QUIRK_URGENT))
00202   .add("running", GetQuirk(QUIRK_RUNNING))
00203   .add("starting", GetQuirk(QUIRK_STARTING))
00204   .add("desaturated", GetQuirk(QUIRK_DESAT))
00205   .add("presented", GetQuirk(QUIRK_PRESENTED));
00206 }
00207 
00208 void
00209 LauncherIcon::Activate(ActionArg arg)
00210 {
00211   /* Launcher Icons that handle spread will adjust the spread state
00212    * accordingly, for all other icons we should terminate spread */
00213   if (WindowManager::Default()->IsScaleActive() && !HandlesSpread ())
00214     WindowManager::Default()->TerminateScale();
00215 
00216   ActivateLauncherIcon(arg);
00217 
00218   UpdateQuirkTime(QUIRK_LAST_ACTION);
00219 }
00220 
00221 void
00222 LauncherIcon::OpenInstance(ActionArg arg)
00223 {
00224   if (WindowManager::Default()->IsScaleActive())
00225     WindowManager::Default()->TerminateScale();
00226 
00227   OpenInstanceLauncherIcon(arg);
00228 
00229   UpdateQuirkTime(QUIRK_LAST_ACTION);
00230 }
00231 
00232 nux::Color LauncherIcon::BackgroundColor() const
00233 {
00234   return _background_color;
00235 }
00236 
00237 nux::Color LauncherIcon::GlowColor()
00238 {
00239   return _glow_color;
00240 }
00241 
00242 nux::BaseTexture* LauncherIcon::TextureForSize(int size)
00243 {
00244   nux::BaseTexture* result = GetTextureForSize(size);
00245   return result;
00246 }
00247 
00248 void LauncherIcon::ColorForIcon(GdkPixbuf* pixbuf, nux::Color& background, nux::Color& glow)
00249 {
00250   unsigned int width = gdk_pixbuf_get_width(pixbuf);
00251   unsigned int height = gdk_pixbuf_get_height(pixbuf);
00252   unsigned int row_bytes = gdk_pixbuf_get_rowstride(pixbuf);
00253 
00254   long int rtotal = 0, gtotal = 0, btotal = 0;
00255   float total = 0.0f;
00256 
00257   guchar* img = gdk_pixbuf_get_pixels(pixbuf);
00258 
00259   for (unsigned int i = 0; i < width; i++)
00260   {
00261     for (unsigned int j = 0; j < height; j++)
00262     {
00263       guchar* pixels = img + (j * row_bytes + i * 4);
00264       guchar r = *(pixels + 0);
00265       guchar g = *(pixels + 1);
00266       guchar b = *(pixels + 2);
00267       guchar a = *(pixels + 3);
00268 
00269       float saturation = (MAX(r, MAX(g, b)) - MIN(r, MIN(g, b))) / 255.0f;
00270       float relevance = .1 + .9 * (a / 255.0f) * saturation;
00271 
00272       rtotal += (guchar)(r * relevance);
00273       gtotal += (guchar)(g * relevance);
00274       btotal += (guchar)(b * relevance);
00275 
00276       total += relevance * 255;
00277     }
00278   }
00279 
00280   nux::color::RedGreenBlue rgb(rtotal / total,
00281                                gtotal / total,
00282                                btotal / total);
00283   nux::color::HueSaturationValue hsv(rgb);
00284 
00285   if (hsv.saturation > 0.15f)
00286     hsv.saturation = 0.65f;
00287 
00288   hsv.value = 0.90f;
00289   background = nux::Color(nux::color::RedGreenBlue(hsv));
00290 
00291   hsv.value = 1.0f;
00292   glow = nux::Color(nux::color::RedGreenBlue(hsv));
00293 }
00294 
00295 /*
00296  * FIXME, all this code (and below), should be put in a facility for IconLoader
00297  * to share between launcher and places the same Icon loading logic and not look
00298  * having etoomanyimplementationofsamethings.
00299  */
00300 /* static */
00301 bool LauncherIcon::IsMonoDefaultTheme()
00302 {
00303 
00304   if (_current_theme_is_mono != -1)
00305     return (bool)_current_theme_is_mono;
00306 
00307   GtkIconTheme* default_theme;
00308   GtkIconInfo* info;
00309   int size = 48;
00310 
00311   default_theme = gtk_icon_theme_get_default();
00312 
00313   _current_theme_is_mono = (int)false;
00314   info = gtk_icon_theme_lookup_icon(default_theme, MONO_TEST_ICON.c_str(), size, (GtkIconLookupFlags)0);
00315 
00316   if (!info)
00317     return (bool)_current_theme_is_mono;
00318 
00319   // yeah, it's evil, but it's blessed upstream
00320   if (g_strrstr(gtk_icon_info_get_filename(info), "ubuntu-mono") != NULL)
00321     _current_theme_is_mono = (int)true;
00322 
00323   gtk_icon_info_free(info);
00324   return (bool)_current_theme_is_mono;
00325 
00326 }
00327 
00328 GtkIconTheme* LauncherIcon::GetUnityTheme()
00329 {
00330   // The theme object is invalid as soon as you add a new icon to change the theme.
00331   // invalidate the cache then and rebuild the theme the first time after a icon theme update.
00332   if (!GTK_IS_ICON_THEME(_unity_theme.RawPtr()))
00333   {
00334     _unity_theme =  gtk_icon_theme_new();
00335     gtk_icon_theme_set_custom_theme(_unity_theme, UNITY_THEME_NAME.c_str());
00336   }
00337   return _unity_theme;
00338 }
00339 
00340 nux::BaseTexture* LauncherIcon::TextureFromGtkTheme(std::string icon_name, int size, bool update_glow_colors)
00341 {
00342   GtkIconTheme* default_theme;
00343   nux::BaseTexture* result = NULL;
00344 
00345   if (icon_name.empty())
00346   {
00347     icon_name = DEFAULT_ICON;
00348   }
00349 
00350   default_theme = gtk_icon_theme_get_default();
00351 
00352   // FIXME: we need to create some kind of -unity postfix to see if we are looking to the unity-icon-theme
00353   // for dedicated unity icons, then remove the postfix and degrade to other icon themes if not found
00354   if (icon_name == "workspace-switcher" && IsMonoDefaultTheme())
00355     result = TextureFromSpecificGtkTheme(GetUnityTheme(), icon_name, size, update_glow_colors);
00356 
00357   if (!result)
00358     result = TextureFromSpecificGtkTheme(default_theme, icon_name, size, update_glow_colors, true);
00359 
00360   if (!result)
00361   {
00362     if (icon_name == "folder")
00363       result = NULL;
00364     else
00365       result = TextureFromSpecificGtkTheme(default_theme, "folder", size, update_glow_colors);
00366   }
00367 
00368   return result;
00369 
00370 }
00371 
00372 nux::BaseTexture* LauncherIcon::TextureFromSpecificGtkTheme(GtkIconTheme* theme,
00373                                                             std::string const& icon_name,
00374                                                             int size,
00375                                                             bool update_glow_colors,
00376                                                             bool is_default_theme)
00377 {
00378   GtkIconInfo* info;
00379   nux::BaseTexture* result = NULL;
00380   GIcon* icon;
00381   GtkIconLookupFlags flags = (GtkIconLookupFlags) 0;
00382 
00383   icon = g_icon_new_for_string(icon_name.c_str(), NULL);
00384 
00385   if (G_IS_ICON(icon))
00386   {
00387     info = gtk_icon_theme_lookup_by_gicon(theme, icon, size, flags);
00388     g_object_unref(icon);
00389   }
00390   else
00391   {
00392     info = gtk_icon_theme_lookup_icon(theme, icon_name.c_str(), size, flags);
00393   }
00394 
00395   if (!info && !is_default_theme)
00396     return NULL;
00397 
00398   if (!info)
00399   {
00400     info = gtk_icon_theme_lookup_icon(theme, DEFAULT_ICON.c_str(), size, flags);
00401   }
00402 
00403   if (gtk_icon_info_get_filename(info) == NULL)
00404   {
00405     gtk_icon_info_free(info);
00406     info = gtk_icon_theme_lookup_icon(theme, DEFAULT_ICON.c_str(), size, flags);
00407   }
00408 
00409   glib::Error error;
00410   glib::Object<GdkPixbuf> pbuf(gtk_icon_info_load_icon(info, &error));
00411   gtk_icon_info_free(info);
00412 
00413   if (GDK_IS_PIXBUF(pbuf.RawPtr()))
00414   {
00415     result = nux::CreateTexture2DFromPixbuf(pbuf, true);
00416 
00417     if (update_glow_colors)
00418       ColorForIcon(pbuf, _background_color, _glow_color);
00419   }
00420   else
00421   {
00422     LOG_WARN(logger) << "Unable to load '" << icon_name
00423                      <<  "' from icon theme: " << error;
00424   }
00425 
00426   return result;
00427 }
00428 
00429 nux::BaseTexture* LauncherIcon::TextureFromPath(std::string const& icon_name, int size, bool update_glow_colors)
00430 {
00431   nux::BaseTexture* result;
00432 
00433   if (icon_name.empty())
00434     return TextureFromGtkTheme(DEFAULT_ICON, size, update_glow_colors);
00435 
00436   glib::Error error;
00437   glib::Object<GdkPixbuf> pbuf(gdk_pixbuf_new_from_file_at_size(icon_name.c_str(), size, size, &error));
00438 
00439   if (GDK_IS_PIXBUF(pbuf.RawPtr()))
00440   {
00441     result = nux::CreateTexture2DFromPixbuf(pbuf, true);
00442 
00443     if (update_glow_colors)
00444       ColorForIcon(pbuf, _background_color, _glow_color);
00445   }
00446   else
00447   {
00448     LOG_WARN(logger) << "Unable to load '" << icon_name
00449                      <<  "' icon: " << error;
00450 
00451     result = TextureFromGtkTheme(DEFAULT_ICON, size, update_glow_colors);
00452   }
00453 
00454   return result;
00455 }
00456 
00457 bool LauncherIcon::SetTooltipText(std::string& target, std::string const& value)
00458 {
00459   bool result = false;
00460 
00461   gchar* esc = g_markup_escape_text(value.c_str(), -1);
00462   std::string escaped = esc;
00463   g_free(esc);
00464 
00465   if (escaped != target)
00466   {
00467     target = escaped;
00468     if (_tooltip)
00469       _tooltip->SetText(target);
00470     result = true;
00471   }
00472 
00473   return result;
00474 }
00475 
00476 void LauncherIcon::OnTooltipEnabledChanged(bool value)
00477 {
00478   if (!value)
00479     HideTooltip();
00480 }
00481 
00482 void
00483 LauncherIcon::SetShortcut(guint64 shortcut)
00484 {
00485   // only relocate a digit with a digit (don't overwrite other shortcuts)
00486   if ((!_shortcut || (g_ascii_isdigit((gchar)_shortcut)))
00487       || !(g_ascii_isdigit((gchar) shortcut)))
00488     _shortcut = shortcut;
00489 }
00490 
00491 guint64
00492 LauncherIcon::GetShortcut()
00493 {
00494   return _shortcut;
00495 }
00496 
00497 void
00498 LauncherIcon::ShowTooltip()
00499 {
00500   if (!tooltip_enabled || (_quicklist && _quicklist->IsVisible()))
00501     return;
00502 
00503   int tip_x = 100;
00504   int tip_y = 100;
00505   if (_last_monitor >= 0)
00506   {
00507     nux::Geometry geo = _parent_geo[_last_monitor];
00508     tip_x = geo.x + geo.width - 4 * geo.width / 48;
00509     tip_y = _center[_last_monitor].y;
00510   }
00511 
00512   if (!_tooltip)
00513     LoadTooltip();
00514   _tooltip->ShowTooltipWithTipAt(tip_x, tip_y);
00515   _tooltip->ShowWindow(!tooltip_text().empty());
00516   tooltip_visible.emit(_tooltip);
00517 }
00518 
00519 void
00520 LauncherIcon::RecvMouseEnter(int monitor)
00521 {
00522   _last_monitor = monitor;
00523   if (QuicklistManager::Default()->Current())
00524   {
00525     // A quicklist is active
00526     return;
00527   }
00528 
00529   ShowTooltip();
00530 }
00531 
00532 void LauncherIcon::RecvMouseLeave(int monitor)
00533 {
00534   _last_monitor = -1;
00535 
00536   if (_tooltip)
00537     _tooltip->ShowWindow(false);
00538   tooltip_visible.emit(nux::ObjectPtr<nux::View>(nullptr));
00539 }
00540 
00541 bool LauncherIcon::OpenQuicklist(bool select_first_item, int monitor)
00542 {
00543   std::list<DbusmenuMenuitem*> menus = Menus();
00544 
00545   if (!_quicklist)
00546     LoadQuicklist();
00547 
00548   if (menus.empty())
00549     return false;
00550 
00551   if (_tooltip)
00552     _tooltip->ShowWindow(false);
00553   _quicklist->RemoveAllMenuItem();
00554 
00555   for (auto menu_item : menus)
00556   {
00557     QuicklistMenuItem* ql_item;
00558 
00559     const gchar* type = dbusmenu_menuitem_property_get(menu_item, DBUSMENU_MENUITEM_PROP_TYPE);
00560     const gchar* toggle_type = dbusmenu_menuitem_property_get(menu_item, DBUSMENU_MENUITEM_PROP_TOGGLE_TYPE);
00561     gboolean prop_visible = dbusmenu_menuitem_property_get_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE);
00562 
00563     // Skip this item, it is invisible right now.
00564     if (!prop_visible)
00565       continue;
00566 
00567     if (g_strcmp0(type, DBUSMENU_CLIENT_TYPES_SEPARATOR) == 0)
00568     {
00569       ql_item = new QuicklistMenuItemSeparator(menu_item, NUX_TRACKER_LOCATION);
00570     }
00571     else if (g_strcmp0(toggle_type, DBUSMENU_MENUITEM_TOGGLE_CHECK) == 0)
00572     {
00573       ql_item = new QuicklistMenuItemCheckmark(menu_item, NUX_TRACKER_LOCATION);
00574     }
00575     else if (g_strcmp0(toggle_type, DBUSMENU_MENUITEM_TOGGLE_RADIO) == 0)
00576     {
00577       ql_item = new QuicklistMenuItemRadio(menu_item, NUX_TRACKER_LOCATION);
00578     }
00579     else //(g_strcmp0 (type, DBUSMENU_MENUITEM_PROP_LABEL) == 0)
00580     {
00581       ql_item = new QuicklistMenuItemLabel(menu_item, NUX_TRACKER_LOCATION);
00582     }
00583 
00584     _quicklist->AddMenuItem(ql_item);
00585   }
00586 
00587   if (select_first_item)
00588     _quicklist->SelectFirstItem();
00589 
00590   if (monitor < 0)
00591   {
00592     if (_last_monitor >= 0)
00593       monitor = _last_monitor;
00594     else
00595       monitor = 0;
00596   }
00597 
00598   nux::Geometry geo = _parent_geo[monitor];
00599   int tip_x = geo.x + geo.width - 4 * geo.width / 48;
00600   int tip_y = _center[monitor].y;
00601 
00602   auto win_manager = WindowManager::Default();
00603 
00604   if (win_manager->IsScaleActive())
00605     win_manager->TerminateScale();
00606 
00607   /* If the expo plugin is active, we need to wait it to be termated, before
00608    * shwing the icon quicklist. */
00609   if (win_manager->IsExpoActive())
00610   {
00611     on_expo_terminated_connection = win_manager->terminate_expo.connect([&, tip_x, tip_y]() {
00612         QuicklistManager::Default()->ShowQuicklist(_quicklist.GetPointer(), tip_x, tip_y);
00613         on_expo_terminated_connection.disconnect();
00614     });
00615   }
00616   else
00617   {
00618     QuicklistManager::Default()->ShowQuicklist(_quicklist.GetPointer(), tip_x, tip_y);
00619   }
00620 
00621   return true;
00622 }
00623 
00624 void LauncherIcon::RecvMouseDown(int button, int monitor, unsigned long key_flags)
00625 {
00626   if (button == 3)
00627     OpenQuicklist();
00628 }
00629 
00630 void LauncherIcon::RecvMouseUp(int button, int monitor, unsigned long key_flags)
00631 {
00632   if (button == 3)
00633   {
00634     if (_quicklist && _quicklist->IsVisible())
00635       _quicklist->CaptureMouseDownAnyWhereElse(true);
00636   }
00637 }
00638 
00639 void LauncherIcon::RecvMouseClick(int button, int monitor, unsigned long key_flags)
00640 {
00641   ActionArg arg(ActionArg::LAUNCHER, button);
00642   arg.monitor = monitor;
00643 
00644   bool shift_pressed = nux::GetKeyModifierState(key_flags, nux::NUX_STATE_SHIFT);
00645 
00646   // Click without shift
00647   if (button == 1 && !shift_pressed)
00648     Activate(arg);
00649   // Middle click or click with shift
00650   else if ((button == 2) || (button == 1 && shift_pressed))
00651     OpenInstance(arg);
00652 }
00653 
00654 void LauncherIcon::HideTooltip()
00655 {
00656   if (_tooltip)
00657     _tooltip->ShowWindow(false);
00658   tooltip_visible.emit(nux::ObjectPtr<nux::View>(nullptr));
00659 }
00660 
00661 bool
00662 LauncherIcon::OnCenterStabilizeTimeout()
00663 {
00664   if (!std::equal(_center.begin(), _center.end(), _last_stable.begin()))
00665   {
00666     OnCenterStabilized(_center);
00667     _last_stable = _center;
00668   }
00669 
00670   return false;
00671 }
00672 
00673 void
00674 LauncherIcon::SetCenter(nux::Point3 center, int monitor, nux::Geometry geo)
00675 {
00676   center.x += geo.x;
00677   center.y += geo.y;
00678   _center[monitor] = center;
00679   _parent_geo[monitor] = geo;
00680 
00681   if (monitor == _last_monitor)
00682   {
00683     int tip_x, tip_y;
00684     tip_x = geo.x + geo.width - 4 * geo.width / 48;
00685     tip_y = _center[monitor].y;
00686 
00687     if (_quicklist && _quicklist->IsVisible())
00688       QuicklistManager::Default()->ShowQuicklist(_quicklist.GetPointer(), tip_x, tip_y);
00689     else if (_tooltip && _tooltip->IsVisible())
00690       _tooltip->ShowTooltipWithTipAt(tip_x, tip_y);
00691   }
00692 
00693   auto timeout = std::make_shared<glib::Timeout>(500);
00694   _source_manager.Add(timeout, CENTER_STABILIZE_TIMEOUT);
00695   timeout->Run(sigc::mem_fun(this, &LauncherIcon::OnCenterStabilizeTimeout));
00696 }
00697 
00698 nux::Point3
00699 LauncherIcon::GetCenter(int monitor)
00700 {
00701   return _center[monitor];
00702 }
00703 
00704 nux::Point3
00705 LauncherIcon::GetSavedCenter(int monitor)
00706 {
00707   return _saved_center[monitor];
00708 }
00709 
00710 std::vector<nux::Point3> LauncherIcon::GetCenters()
00711 {
00712   return _center;
00713 }
00714 
00715 void
00716 LauncherIcon::SaveCenter()
00717 {
00718   _saved_center = _center;
00719   UpdateQuirkTime(QUIRK_CENTER_SAVED);
00720 }
00721 
00722 void
00723 LauncherIcon::SetWindowVisibleOnMonitor(bool val, int monitor)
00724 {
00725   if (_has_visible_window[monitor] == val)
00726     return;
00727 
00728   _has_visible_window[monitor] = val;
00729   EmitNeedsRedraw();
00730 }
00731 
00732 void
00733 LauncherIcon::SetVisibleOnMonitor(int monitor, bool visible)
00734 {
00735   if (_is_visible_on_monitor[monitor] == visible)
00736     return;
00737 
00738   _is_visible_on_monitor[monitor] = visible;
00739   EmitNeedsRedraw();
00740 }
00741 
00742 bool
00743 LauncherIcon::IsVisibleOnMonitor(int monitor) const
00744 {
00745   return _is_visible_on_monitor[monitor];
00746 }
00747 
00748 bool
00749 LauncherIcon::OnPresentTimeout()
00750 {
00751   if (!GetQuirk(QUIRK_PRESENTED))
00752     return false;
00753 
00754   Unpresent();
00755 
00756   return false;
00757 }
00758 
00759 float LauncherIcon::PresentUrgency()
00760 {
00761   return _present_urgency;
00762 }
00763 
00764 void
00765 LauncherIcon::Present(float present_urgency, int length)
00766 {
00767   if (GetQuirk(QUIRK_PRESENTED))
00768     return;
00769 
00770   if (length >= 0)
00771   {
00772     auto timeout = std::make_shared<glib::Timeout>(length);
00773     _source_manager.Add(timeout, PRESENT_TIMEOUT);
00774     timeout->Run(sigc::mem_fun(this, &LauncherIcon::OnPresentTimeout));
00775   }
00776 
00777   _present_urgency = CLAMP(present_urgency, 0.0f, 1.0f);
00778   SetQuirk(QUIRK_PRESENTED, true);
00779 }
00780 
00781 void
00782 LauncherIcon::Unpresent()
00783 {
00784   if (!GetQuirk(QUIRK_PRESENTED))
00785     return;
00786 
00787   _source_manager.Remove(PRESENT_TIMEOUT);
00788   SetQuirk(QUIRK_PRESENTED, false);
00789 }
00790 
00791 void
00792 LauncherIcon::Remove()
00793 {
00794   if (_quicklist && _quicklist->IsVisible())
00795       _quicklist->Hide();
00796 
00797   SetQuirk(QUIRK_VISIBLE, false);
00798   EmitRemove();
00799 }
00800 
00801 void
00802 LauncherIcon::SetIconType(IconType type)
00803 {
00804   _icon_type = type;
00805 }
00806 
00807 void
00808 LauncherIcon::SetSortPriority(int priority)
00809 {
00810   _sort_priority = priority;
00811 }
00812 
00813 int
00814 LauncherIcon::SortPriority()
00815 {
00816   return _sort_priority;
00817 }
00818 
00819 LauncherIcon::IconType
00820 LauncherIcon::GetIconType()
00821 {
00822   return _icon_type;
00823 }
00824 
00825 bool
00826 LauncherIcon::GetQuirk(LauncherIcon::Quirk quirk) const
00827 {
00828   return _quirks[quirk];
00829 }
00830 
00831 void
00832 LauncherIcon::SetQuirk(LauncherIcon::Quirk quirk, bool value)
00833 {
00834   if (_quirks[quirk] == value)
00835     return;
00836 
00837   _quirks[quirk] = value;
00838   if (quirk == QUIRK_VISIBLE)
00839     TimeUtil::SetTimeStruct(&(_quirk_times[quirk]), &(_quirk_times[quirk]), Launcher::ANIM_DURATION_SHORT);
00840   else
00841     clock_gettime(CLOCK_MONOTONIC, &(_quirk_times[quirk]));
00842   EmitNeedsRedraw();
00843 
00844   // Present on urgent as a general policy
00845   if (quirk == QUIRK_VISIBLE && value)
00846     Present(0.5f, 1500);
00847   if (quirk == QUIRK_URGENT)
00848   {
00849     if (value)
00850     {
00851       Present(0.5f, 1500);
00852     }
00853 
00854     UBusServer* ubus = ubus_server_get_default();
00855     ubus_server_send_message(ubus, UBUS_LAUNCHER_ICON_URGENT_CHANGED, g_variant_new_boolean(value));
00856   }
00857 
00858   if (quirk == QUIRK_VISIBLE)
00859   {
00860      visibility_changed.emit();
00861   }
00862 }
00863 
00864 void
00865 LauncherIcon::UpdateQuirkTimeDelayed(guint ms, LauncherIcon::Quirk quirk)
00866 {
00867   auto timeout = std::make_shared<glib::Timeout>(ms);
00868   _source_manager.Add(timeout, QUIRK_DELAY_TIMEOUT);
00869   timeout->Run([&, quirk] {
00870     UpdateQuirkTime(quirk);
00871     return false;
00872   });
00873 }
00874 
00875 void
00876 LauncherIcon::UpdateQuirkTime(LauncherIcon::Quirk quirk)
00877 {
00878   clock_gettime(CLOCK_MONOTONIC, &(_quirk_times[quirk]));
00879   EmitNeedsRedraw();
00880 }
00881 
00882 void
00883 LauncherIcon::ResetQuirkTime(LauncherIcon::Quirk quirk)
00884 {
00885   _quirk_times[quirk].tv_sec = 0;
00886   _quirk_times[quirk].tv_nsec = 0;
00887 }
00888 
00889 struct timespec
00890 LauncherIcon::GetQuirkTime(LauncherIcon::Quirk quirk)
00891 {
00892   return _quirk_times[quirk];
00893 }
00894 
00895 void
00896 LauncherIcon::SetProgress(float progress)
00897 {
00898   if (progress == _progress)
00899     return;
00900 
00901   _progress = progress;
00902   EmitNeedsRedraw();
00903 }
00904 
00905 float
00906 LauncherIcon::GetProgress()
00907 {
00908   return _progress;
00909 }
00910 
00911 std::list<DbusmenuMenuitem*> LauncherIcon::Menus()
00912 {
00913   return GetMenus();
00914 }
00915 
00916 std::list<DbusmenuMenuitem*> LauncherIcon::GetMenus()
00917 {
00918   std::list<DbusmenuMenuitem*> result;
00919   return result;
00920 }
00921 
00922 nux::BaseTexture*
00923 LauncherIcon::Emblem()
00924 {
00925   return _emblem.GetPointer();
00926 }
00927 
00928 void
00929 LauncherIcon::SetEmblem(LauncherIcon::BaseTexturePtr const& emblem)
00930 {
00931   _emblem = emblem;
00932   EmitNeedsRedraw();
00933 }
00934 
00935 void
00936 LauncherIcon::SetEmblemIconName(std::string const& name)
00937 {
00938   BaseTexturePtr emblem;
00939 
00940   if (name.at(0) == '/')
00941     emblem = TextureFromPath(name, 22, false);
00942   else
00943     emblem = TextureFromGtkTheme(name, 22, false);
00944 
00945   SetEmblem(emblem);
00946   // Ownership isn't taken, but shared, so we need to unref here.
00947   emblem->UnReference();
00948 }
00949 
00950 void
00951 LauncherIcon::SetEmblemText(std::string const& text)
00952 {
00953   PangoLayout*          layout     = NULL;
00954 
00955   PangoContext*         pangoCtx   = NULL;
00956   PangoFontDescription* desc       = NULL;
00957   GdkScreen*            screen     = gdk_screen_get_default();    // not ref'ed
00958   GtkSettings*          settings   = gtk_settings_get_default();  // not ref'ed
00959   gchar*                fontName   = NULL;
00960 
00961   int width = 32;
00962   int height = 8 * 2;
00963   int font_height = height - 5;
00964 
00965 
00966   nux::CairoGraphics cg(CAIRO_FORMAT_ARGB32, width, height);
00967   cairo_t* cr = cg.GetInternalContext();
00968 
00969   cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR);
00970   cairo_paint(cr);
00971 
00972   cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
00973 
00974 
00975   layout = pango_cairo_create_layout(cr);
00976 
00977   g_object_get(settings, "gtk-font-name", &fontName, NULL);
00978   desc = pango_font_description_from_string(fontName);
00979   pango_font_description_set_absolute_size(desc, pango_units_from_double(font_height));
00980 
00981   pango_layout_set_font_description(layout, desc);
00982   pango_font_description_free(desc);
00983 
00984   pango_layout_set_width(layout, pango_units_from_double(width - 4.0f));
00985   pango_layout_set_wrap(layout, PANGO_WRAP_WORD_CHAR);
00986   pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE);
00987   pango_layout_set_markup_with_accel(layout, text.c_str(), -1, '_', NULL);
00988 
00989   pangoCtx = pango_layout_get_context(layout);  // is not ref'ed
00990   pango_cairo_context_set_font_options(pangoCtx,
00991                                        gdk_screen_get_font_options(screen));
00992 
00993   PangoRectangle logical_rect, ink_rect;
00994   pango_layout_get_extents(layout, &logical_rect, &ink_rect);
00995 
00996   /* DRAW OUTLINE */
00997   float radius = height / 2.0f - 1.0f;
00998   float inset = radius + 1.0f;
00999 
01000   cairo_move_to(cr, inset, height - 1.0f);
01001   cairo_arc(cr, inset, inset, radius, 0.5 * M_PI, 1.5 * M_PI);
01002   cairo_arc(cr, width - inset, inset, radius, 1.5 * M_PI, 0.5 * M_PI);
01003   cairo_line_to(cr, inset, height - 1.0f);
01004 
01005   cairo_set_source_rgba(cr, 0.35f, 0.35f, 0.35f, 1.0f);
01006   cairo_fill_preserve(cr);
01007 
01008   cairo_set_source_rgba(cr, 1.0f, 1.0f, 1.0f, 1.0f);
01009   cairo_set_line_width(cr, 2.0f);
01010   cairo_stroke(cr);
01011 
01012   cairo_set_line_width(cr, 1.0f);
01013 
01014   /* DRAW TEXT */
01015   cairo_move_to(cr,
01016                 (int)((width - pango_units_to_double(logical_rect.width)) / 2.0f),
01017                 (int)((height - pango_units_to_double(logical_rect.height)) / 2.0f - pango_units_to_double(logical_rect.y)));
01018   pango_cairo_show_layout(cr, layout);
01019 
01020   SetEmblem(texture_ptr_from_cairo_graphics(cg));
01021 
01022   // clean up
01023   g_object_unref(layout);
01024   g_free(fontName);
01025 }
01026 
01027 void
01028 LauncherIcon::DeleteEmblem()
01029 {
01030   SetEmblem(BaseTexturePtr());
01031 }
01032 
01033 void
01034 LauncherIcon::InsertEntryRemote(LauncherEntryRemote::Ptr const& remote)
01035 {
01036   if (std::find(_entry_list.begin(), _entry_list.end(), remote) != _entry_list.end())
01037     return;
01038 
01039   _entry_list.push_front(remote);
01040   AddChild(remote.get());
01041 
01042   remote->emblem_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteEmblemChanged));
01043   remote->count_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteCountChanged));
01044   remote->progress_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteProgressChanged));
01045   remote->quicklist_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteQuicklistChanged));
01046 
01047   remote->emblem_visible_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteEmblemVisibleChanged));
01048   remote->count_visible_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteCountVisibleChanged));
01049   remote->progress_visible_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteProgressVisibleChanged));
01050 
01051   remote->urgent_changed.connect(sigc::mem_fun(this, &LauncherIcon::OnRemoteUrgentChanged));
01052 
01053 
01054   if (remote->EmblemVisible())
01055     OnRemoteEmblemVisibleChanged(remote.get());
01056 
01057   if (remote->CountVisible())
01058     OnRemoteCountVisibleChanged(remote.get());
01059 
01060   if (remote->ProgressVisible())
01061     OnRemoteProgressVisibleChanged(remote.get());
01062 
01063   if (remote->Urgent())
01064     OnRemoteUrgentChanged(remote.get());
01065 
01066   OnRemoteQuicklistChanged(remote.get());
01067 }
01068 
01069 void
01070 LauncherIcon::RemoveEntryRemote(LauncherEntryRemote::Ptr const& remote)
01071 {
01072   if (std::find(_entry_list.begin(), _entry_list.end(), remote) == _entry_list.end())
01073     return;
01074 
01075   _entry_list.remove(remote);
01076   RemoveChild(remote.get());
01077 
01078   DeleteEmblem();
01079   SetQuirk(QUIRK_PROGRESS, false);
01080 
01081   if (_remote_urgent)
01082     SetQuirk(QUIRK_URGENT, false);
01083 
01084   _menuclient_dynamic_quicklist = nullptr;
01085 }
01086 
01087 void
01088 LauncherIcon::OnRemoteUrgentChanged(LauncherEntryRemote* remote)
01089 {
01090   _remote_urgent = remote->Urgent();
01091   SetQuirk(QUIRK_URGENT, remote->Urgent());
01092 }
01093 
01094 void
01095 LauncherIcon::OnRemoteEmblemChanged(LauncherEntryRemote* remote)
01096 {
01097   if (!remote->EmblemVisible())
01098     return;
01099 
01100   SetEmblemIconName(remote->Emblem());
01101 }
01102 
01103 void
01104 LauncherIcon::OnRemoteCountChanged(LauncherEntryRemote* remote)
01105 {
01106   if (!remote->CountVisible())
01107     return;
01108 
01109   std::string text;
01110   if (remote->Count() > 9999)
01111     text = "****";
01112   else
01113     text = std::to_string(remote->Count());
01114 
01115   SetEmblemText(text);
01116 }
01117 
01118 void
01119 LauncherIcon::OnRemoteProgressChanged(LauncherEntryRemote* remote)
01120 {
01121   if (!remote->ProgressVisible())
01122     return;
01123 
01124   SetProgress(remote->Progress());
01125 }
01126 
01127 void
01128 LauncherIcon::OnRemoteQuicklistChanged(LauncherEntryRemote* remote)
01129 {
01130   _menuclient_dynamic_quicklist = remote->Quicklist();
01131 }
01132 
01133 void
01134 LauncherIcon::OnRemoteEmblemVisibleChanged(LauncherEntryRemote* remote)
01135 {
01136   if (remote->EmblemVisible())
01137     SetEmblemIconName(remote->Emblem());
01138   else
01139     DeleteEmblem();
01140 }
01141 
01142 void
01143 LauncherIcon::OnRemoteCountVisibleChanged(LauncherEntryRemote* remote)
01144 {
01145   if (remote->CountVisible())
01146   {
01147     SetEmblemText(std::to_string(remote->Count()));
01148   }
01149   else
01150   {
01151     DeleteEmblem();
01152   }
01153 }
01154 
01155 void
01156 LauncherIcon::OnRemoteProgressVisibleChanged(LauncherEntryRemote* remote)
01157 {
01158   SetQuirk(QUIRK_PROGRESS, remote->ProgressVisible());
01159 
01160   if (remote->ProgressVisible())
01161     SetProgress(remote->Progress());
01162 }
01163 
01164 void LauncherIcon::EmitNeedsRedraw()
01165 {
01166   if (OwnsTheReference() && GetReferenceCount() > 0)
01167     needs_redraw.emit(AbstractLauncherIcon::Ptr(this));
01168 }
01169 
01170 void LauncherIcon::EmitRemove()
01171 {
01172   if (OwnsTheReference() && GetReferenceCount() > 0)
01173     remove.emit(AbstractLauncherIcon::Ptr(this));
01174 }
01175 
01176 
01177 } // namespace launcher
01178 } // namespace unity