diff options
77 files changed, 3746 insertions, 987 deletions
| diff --git a/UnityCore/DesktopUtilities.cpp b/UnityCore/DesktopUtilities.cpp index 46f5b0165..a627c191b 100644 --- a/UnityCore/DesktopUtilities.cpp +++ b/UnityCore/DesktopUtilities.cpp @@ -19,8 +19,10 @@  */  #include <algorithm> +#include <memory>  #include <glib.h> +#include <gio/gdesktopappinfo.h>  #include "DesktopUtilities.h"  #include "GLibWrapper.h" @@ -119,23 +121,41 @@ std::string DesktopUtilities::GetDesktopID(std::string const& desktop_path)  return GetDesktopID(data_dirs, desktop_path);  } +std::string DesktopUtilities::GetDesktopPathById(std::string const& desktop_id) +{ + if (desktop_id.empty()) + return ""; + + glib::Object<GDesktopAppInfo> info; + + if (desktop_id.find(G_DIR_SEPARATOR_S) != std::string::npos) + info = g_desktop_app_info_new_from_filename(desktop_id.c_str()); + else + info = g_desktop_app_info_new(desktop_id.c_str()); + + if (info) + { + const char* filename = g_desktop_app_info_get_filename(info); + + if (filename) + return filename; + } + + return ""; +}  std::string DesktopUtilities::GetBackgroundColor(std::string const& desktop_path)  { - GKeyFile* key_file = g_key_file_new(); + std::shared_ptr<GKeyFile> key_file(g_key_file_new(), g_key_file_free);  glib::Error error; - g_key_file_load_from_file(key_file, desktop_path.c_str(), static_cast<GKeyFileFlags>(0), &error); + g_key_file_load_from_file(key_file.get(), desktop_path.c_str(), static_cast<GKeyFileFlags>(0), &error);  if (error) - { - g_key_file_free(key_file);  return ""; - } - glib::String value(g_key_file_get_string(key_file, "Desktop Entry", "X-Unity-IconBackgroundColor", &error)); + glib::String value(g_key_file_get_string(key_file.get(), "Desktop Entry", "X-Unity-IconBackgroundColor", &error)); - g_key_file_free(key_file);  return value.Str();  } diff --git a/UnityCore/DesktopUtilities.h b/UnityCore/DesktopUtilities.h index 7a6b38402..072b40c80 100644 --- a/UnityCore/DesktopUtilities.h +++ b/UnityCore/DesktopUtilities.h @@ -36,6 +36,7 @@ public:  static std::string GetDesktopID(std::string const& desktop_path);  static std::string GetDesktopID(std::vector<std::string> const& default_paths,  std::string const& desktop_path); + static std::string GetDesktopPathById(std::string const& desktop_id);  static std::string GetBackgroundColor(std::string const& desktop_path);  }; diff --git a/UnityCore/GLibDBusProxy.cpp b/UnityCore/GLibDBusProxy.cpp index da0ac00fd..bd086574b 100644 --- a/UnityCore/GLibDBusProxy.cpp +++ b/UnityCore/GLibDBusProxy.cpp @@ -90,7 +90,6 @@ public:  glib::Object<GDBusProxy> proxy_;  glib::Object<GCancellable> cancellable_; - guint watcher_id_;  bool connected_;  glib::Signal<void, GDBusProxy*, char*, char*, GVariant*> g_signal_connection_; @@ -113,7 +112,6 @@ DBusProxy::Impl::Impl(DBusProxy* owner,  , bus_type_(bus_type)  , flags_(flags)  , cancellable_(g_cancellable_new()) - , watcher_id_(0)  , connected_(false)  {  StartReconnectionTimeout(); @@ -122,8 +120,6 @@ DBusProxy::Impl::Impl(DBusProxy* owner,  DBusProxy::Impl::~Impl()  {  g_cancellable_cancel(cancellable_); - if (watcher_id_) - g_bus_unwatch_name(watcher_id_);  }  void DBusProxy::Impl::StartReconnectionTimeout() diff --git a/com.canonical.Unity.gschema.xml b/com.canonical.Unity.gschema.xml index 7656110a5..66d4563a6 100644 --- a/com.canonical.Unity.gschema.xml +++ b/com.canonical.Unity.gschema.xml @@ -44,9 +44,9 @@  </schema>  <schema path="/com/canonical/unity/launcher/" id="com.canonical.Unity.Launcher" gettext-domain="unity">  <key type="as" name="favorites"> - <default>[ 'ubiquity-gtkui.desktop', 'nautilus-home.desktop', 'firefox.desktop', 'libreoffice-writer.desktop', 'libreoffice-calc.desktop', 'libreoffice-impress.desktop', 'ubuntu-software-center.desktop', 'ubuntuone-installer.desktop', 'gnome-control-center.desktop' ]</default> - <summary>List of desktop file ids for favorites on the launcher.</summary> - <description>These applications are shown in the Launcher by default.</description> + <default>[ 'application://ubiquity-gtkui.desktop', 'application://nautilus-home.desktop', 'application://firefox.desktop', 'application://libreoffice-writer.desktop', 'application://libreoffice-calc.desktop', 'application://libreoffice-impress.desktop', 'application://ubuntu-software-center.desktop', 'application://ubuntuone-installer.desktop', 'application://gnome-control-center.desktop', 'unity://running-apps', 'unity://expo-icon', 'unity://devices' ]</default> + <summary>List of items that should be shown by default in the launcher</summary> + <description>These items can be application://desktop-id.desktop, device://uiid and unity://special-id (including unity://running-apps that specifies the position of the ran applications, unity://devices the position of the attached devices, unity://expo-icon the position of the workspace switcher and unity://show-desktop-icon the position of the show-desktop icon); the order of this list determines the launcher items position.</description>  </key>  <key type="s" name="favorite-migration">  <default>''</default> diff --git a/launcher/AbstractLauncherIcon.h b/launcher/AbstractLauncherIcon.h index 178796b2e..78252c90e 100644 --- a/launcher/AbstractLauncherIcon.h +++ b/launcher/AbstractLauncherIcon.h @@ -93,6 +93,7 @@ public:  DESKTOP,  PLACE,  DEVICE, + SPACER,  TRASH,  END  }; @@ -117,10 +118,18 @@ public:  LAST  }; + enum class Position + { + BEGIN, + FLOATING, + END + }; +  virtual ~AbstractLauncherIcon() {}  nux::Property<std::string> tooltip_text;  nux::Property<bool> tooltip_enabled; + nux::Property<Position> position;  virtual void HideTooltip() = 0; @@ -158,8 +167,6 @@ public:  virtual const bool WindowVisibleOnViewport() = 0; - virtual bool IsSpacer() = 0; -  virtual float PresentUrgency() = 0;  virtual float GetProgress() = 0; @@ -211,6 +218,11 @@ public:  virtual void UnStick() = 0; + static int DefaultPriority(IconType type) + { + return static_cast<int>(type) * 1000; + } +  sigc::signal<void, int, int, unsigned long> mouse_down;  sigc::signal<void, int, int, unsigned long> mouse_up;  sigc::signal<void, int, int, unsigned long> mouse_click; @@ -221,6 +233,8 @@ public:  sigc::signal<void, AbstractLauncherIcon::Ptr> remove;  sigc::signal<void, nux::ObjectPtr<nux::View>> tooltip_visible;  sigc::signal<void> visibility_changed; + sigc::signal<void> position_saved; + sigc::signal<void> position_forgot;  sigc::connection needs_redraw_connection;  sigc::connection on_icon_added_connection; diff --git a/launcher/BFBLauncherIcon.cpp b/launcher/BFBLauncherIcon.cpp index 54c05b5ed..d512fbad4 100644 --- a/launcher/BFBLauncherIcon.cpp +++ b/launcher/BFBLauncherIcon.cpp @@ -36,6 +36,7 @@ BFBLauncherIcon::BFBLauncherIcon(LauncherHideMode hide_mode)  {  tooltip_text = _("Dash Home");  icon_name = PKGDATADIR"/launcher_bfb.png"; + position = Position::BEGIN;  SetQuirk(Quirk::VISIBLE, true);  SetQuirk(Quirk::RUNNING, false); diff --git a/launcher/BamfLauncherIcon.cpp b/launcher/BamfLauncherIcon.cpp index f088b99d5..f24529f30 100644 --- a/launcher/BamfLauncherIcon.cpp +++ b/launcher/BamfLauncherIcon.cpp @@ -74,11 +74,7 @@ BamfLauncherIcon::BamfLauncherIcon(BamfApplication* app)  tooltip_text = BamfName();  icon_name = (icon ? icon.Str() : DEFAULT_ICON); - if (IsSticky()) - SetQuirk(Quirk::VISIBLE, true); - else - SetQuirk(Quirk::VISIBLE, bamf_view_user_visible(bamf_view)); - + SetQuirk(Quirk::VISIBLE, bamf_view_user_visible(bamf_view));  SetQuirk(Quirk::ACTIVE, bamf_view_is_active(bamf_view));  SetQuirk(Quirk::RUNNING, bamf_view_is_running(bamf_view)); @@ -95,7 +91,7 @@ BamfLauncherIcon::BamfLauncherIcon(BamfApplication* app)  sig = new glib::Signal<void, BamfView*, BamfView*>(bamf_view, "child-removed",  [&] (BamfView*, BamfView*) { EnsureWindowState(); });  _gsignals.Add(sig); -  +  sig = new glib::Signal<void, BamfView*, BamfView*>(bamf_view, "child-moved",  [&] (BamfView *, BamfView *) {  EnsureWindowState(); @@ -173,9 +169,12 @@ BamfLauncherIcon::BamfLauncherIcon(BamfApplication* app)  BamfLauncherIcon::~BamfLauncherIcon()  { - if (_bamf_app) + if (_bamf_app.IsType(BAMF_TYPE_APPLICATION)) + { + bamf_view_set_sticky(BAMF_VIEW(_bamf_app.RawPtr()), FALSE);  g_object_set_qdata(G_OBJECT(_bamf_app.RawPtr()),  g_quark_from_static_string("unity-seen"), nullptr); + }  }  void BamfLauncherIcon::Remove() @@ -201,11 +200,6 @@ bool BamfLauncherIcon::IsSticky() const  return bamf_view_is_sticky(BAMF_VIEW(_bamf_app.RawPtr()));  } -bool BamfLauncherIcon::IsVisible() const -{ - return GetQuirk(Quirk::VISIBLE); -} -  bool BamfLauncherIcon::IsActive() const  {  return GetQuirk(Quirk::ACTIVE); @@ -235,7 +229,6 @@ void BamfLauncherIcon::ActivateLauncherIcon(ActionArg arg)  wm->Activate(arg.target);  return;  } -   /* We should check each child to see if there is  * an unmapped (!= minimized) window around and @@ -638,22 +631,22 @@ std::vector<Window> BamfLauncherIcon::GetFocusableWindows(ActionArg arg, bool &a  GList* children;  BamfView *focusable_child = BAMF_VIEW (bamf_application_get_focusable_child (_bamf_app.RawPtr())); -  +  if (focusable_child != NULL)  {  Window xid; -  +  if (BAMF_IS_WINDOW (focusable_child))  xid = bamf_window_get_xid (BAMF_WINDOW(focusable_child));  else if (BAMF_IS_TAB (focusable_child))  {  BamfTab *focusable_tab = BAMF_TAB (focusable_child); -  +  xid = bamf_tab_get_xid (focusable_tab); -  +  bamf_tab_raise (focusable_tab);  } -  +  windows.push_back(xid);  return windows;  } @@ -662,7 +655,7 @@ std::vector<Window> BamfLauncherIcon::GetFocusableWindows(ActionArg arg, bool &a  if (g_strcmp0 (bamf_application_get_application_type (_bamf_app.RawPtr()), "webapp") == 0)  {  OpenInstanceLauncherIcon(arg); -  +  return windows;  }  } @@ -762,13 +755,13 @@ void BamfLauncherIcon::EnsureWindowState()  {  /* BamfTab does not support the monitor interface...so a bit of a nasty hack here. */  xid = bamf_tab_get_xid (static_cast<BamfTab*>(l->data)); -  +  if (WindowManager::Default()->IsWindowOnCurrentDesktop(xid) == false)  continue; -  +  for (int j = 0; j < max_num_monitors; j++)  monitors[j] = true; -  +  continue;  } @@ -939,30 +932,28 @@ void BamfLauncherIcon::Quit()  void BamfLauncherIcon::Stick(bool save)  { + SimpleLauncherIcon::Stick(save); +  if (IsSticky())  return; - std::string const& desktop_file = DesktopFile();  bamf_view_set_sticky(BAMF_VIEW(_bamf_app.RawPtr()), true); - - if (save && !desktop_file.empty()) - FavoriteStore::Instance().AddFavorite(desktop_file, -1);  }  void BamfLauncherIcon::UnStick()  { + SimpleLauncherIcon::UnStick(); +  if (!IsSticky())  return; - std::string const& desktop_file = DesktopFile();  BamfView* view = BAMF_VIEW(_bamf_app.RawPtr());  bamf_view_set_sticky(view, false); - if (bamf_view_is_closed(view) || !bamf_view_user_visible(view)) - Remove(); + SetQuirk(Quirk::VISIBLE, bamf_view_user_visible(view)); - if (!desktop_file.empty()) - FavoriteStore::Instance().RemoveFavorite(desktop_file); + if (bamf_view_is_closed(view)) + Remove();  }  void BamfLauncherIcon::ToggleSticky() @@ -1227,12 +1218,11 @@ std::string BamfLauncherIcon::GetRemoteUri()  {  if (_remote_uri.empty())  { - const std::string prefix = "application://";  std::string const& desktop_id = GetDesktopID();  if (!desktop_id.empty())  { - _remote_uri = prefix + desktop_id; + _remote_uri = FavoriteStore::URI_PREFIX_APP + desktop_id;  }  } @@ -1384,32 +1374,25 @@ void BamfLauncherIcon::FillSupportedTypes()  if (desktop_file.empty())  return; - GKeyFile* key_file = g_key_file_new(); + std::shared_ptr<GKeyFile> key_file(g_key_file_new(), g_key_file_free);  glib::Error error; - g_key_file_load_from_file(key_file, desktop_file.c_str(), (GKeyFileFlags) 0, &error); + g_key_file_load_from_file(key_file.get(), desktop_file.c_str(), (GKeyFileFlags) 0, &error);  if (error) - { - g_key_file_free(key_file);  return; - } - char** mimes = g_key_file_get_string_list(key_file, "Desktop Entry", "MimeType", nullptr, nullptr); + std::shared_ptr<char*> mimes(g_key_file_get_string_list(key_file.get(), "Desktop Entry", "MimeType", nullptr, nullptr), + g_strfreev); +  if (!mimes) - { - g_key_file_free(key_file);  return; - } - for (int i = 0; mimes[i]; i++) + for (int i = 0; mimes.get()[i]; i++)  { - unity::glib::String super_type(g_content_type_from_mime_type(mimes[i])); + unity::glib::String super_type(g_content_type_from_mime_type(mimes.get()[i]));  _supported_types.insert(super_type.Str());  } - - g_key_file_free(key_file); - g_strfreev(mimes);  }  } diff --git a/launcher/BamfLauncherIcon.h b/launcher/BamfLauncherIcon.h index b18a10a38..4ee7bb3f7 100644 --- a/launcher/BamfLauncherIcon.h +++ b/launcher/BamfLauncherIcon.h @@ -48,7 +48,6 @@ public:  std::string DesktopFile();  bool IsSticky() const; - bool IsVisible() const;  bool IsActive() const;  bool IsRunning() const;  bool IsUrgent() const; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 28bb87dde..1107e19bc 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -42,6 +42,7 @@ set (LAUNCHER_SOURCES  DevicesSettingsImp.cpp  DndData.cpp  EdgeBarrierController.cpp + ExpoLauncherIcon.cpp  FavoriteStore.cpp  FavoriteStoreGSettings.cpp  FavoriteStorePrivate.cpp diff --git a/launcher/DesktopLauncherIcon.cpp b/launcher/DesktopLauncherIcon.cpp index 712763060..50ec49624 100644 --- a/launcher/DesktopLauncherIcon.cpp +++ b/launcher/DesktopLauncherIcon.cpp @@ -19,6 +19,7 @@  #include "DesktopLauncherIcon.h"  #include "unity-shared/WindowManager.h" +#include "FavoriteStore.h"  #include <glib/gi18n-lib.h> @@ -28,17 +29,14 @@ namespace launcher  {  DesktopLauncherIcon::DesktopLauncherIcon() - : SimpleLauncherIcon(IconType::DESKTOP) - , show_in_switcher_(true) + : SimpleLauncherIcon(IconType::DESKTOP) + , show_in_switcher_(true)  {  tooltip_text = _("Show Desktop");  icon_name = "desktop";  SetQuirk(Quirk::VISIBLE, true);  SetQuirk(Quirk::RUNNING, false); -} - -DesktopLauncherIcon::~DesktopLauncherIcon() -{ + SetShortcut('d');  }  void @@ -53,5 +51,20 @@ std::string DesktopLauncherIcon::GetName() const  return "DesktopLauncherIcon";  } +std::string DesktopLauncherIcon::GetRemoteUri() +{ + return FavoriteStore::URI_PREFIX_UNITY + "desktop-icon"; +} + +void DesktopLauncherIcon::SetShowInSwitcher(bool show_in_switcher) +{ + show_in_switcher_ = show_in_switcher; +} + +bool DesktopLauncherIcon::ShowInSwitcher(bool current) +{ + return show_in_switcher_; +} +  } // namespace launcher  } // namespace unity diff --git a/launcher/DesktopLauncherIcon.h b/launcher/DesktopLauncherIcon.h index de1ee83a8..987ca3df9 100644 --- a/launcher/DesktopLauncherIcon.h +++ b/launcher/DesktopLauncherIcon.h @@ -29,24 +29,16 @@ namespace launcher  class DesktopLauncherIcon : public SimpleLauncherIcon  { -  public:  DesktopLauncherIcon(); - ~DesktopLauncherIcon(); - - void SetShowInSwitcher(bool show_in_switcher) - { - show_in_switcher_ = show_in_switcher; - } - bool ShowInSwitcher(bool current) - { - return show_in_switcher_; - } + void SetShowInSwitcher(bool show_in_switcher); + bool ShowInSwitcher(bool current);  protected:  void ActivateLauncherIcon(ActionArg arg);  std::string GetName() const; + std::string GetRemoteUri();  private:  bool show_in_switcher_; diff --git a/launcher/DeviceLauncherSection.cpp b/launcher/DeviceLauncherSection.cpp index b618f0cb9..b46e34f7c 100644 --- a/launcher/DeviceLauncherSection.cpp +++ b/launcher/DeviceLauncherSection.cpp @@ -38,10 +38,7 @@ DeviceLauncherSection::DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr v  monitor_->volume_added.connect(sigc::mem_fun(this, &DeviceLauncherSection::OnVolumeAdded));  monitor_->volume_removed.connect(sigc::mem_fun(this, &DeviceLauncherSection::OnVolumeRemoved)); - device_populate_idle_.Run([this] () { - PopulateEntries(); - return false; - }); + PopulateEntries();  }  void DeviceLauncherSection::PopulateEntries() @@ -60,11 +57,11 @@ void DeviceLauncherSection::TryToCreateAndAddIcon(glib::Object<GVolume> volume)  if (map_.find(volume) != map_.end())  return; - VolumeLauncherIcon::Ptr icon(new VolumeLauncherIcon(std::make_shared<VolumeImp>(volume, file_manager_opener_, device_notification_display_), - devices_settings_)); + auto vol = std::make_shared<VolumeImp>(volume, file_manager_opener_, device_notification_display_); + VolumeLauncherIcon::Ptr icon(new VolumeLauncherIcon(vol, devices_settings_));  map_[volume] = icon; - IconAdded.emit(icon); + icon_added.emit(icon);  }  void DeviceLauncherSection::OnVolumeRemoved(glib::Object<GVolume> const& volume) @@ -76,5 +73,15 @@ void DeviceLauncherSection::OnVolumeRemoved(glib::Object<GVolume> const& volume)  map_.erase(volume_it);  } +std::vector<VolumeLauncherIcon::Ptr> DeviceLauncherSection::GetIcons() const +{ + std::vector<VolumeLauncherIcon::Ptr> icons; + + for (auto const& it : map_) + icons.push_back(it.second); + + return icons; +} +  }  } diff --git a/launcher/DeviceLauncherSection.h b/launcher/DeviceLauncherSection.h index 551fa4a2a..9df9148e5 100644 --- a/launcher/DeviceLauncherSection.h +++ b/launcher/DeviceLauncherSection.h @@ -23,8 +23,6 @@  #include <map>  #include <memory> -#include <UnityCore/GLibSource.h> -  #include "AbstractVolumeMonitorWrapper.h"  #include "DevicesSettings.h"  #include "DeviceNotificationDisplay.h" @@ -42,7 +40,9 @@ public:  DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr volume_monitor,  DevicesSettings::Ptr devices_settings); - sigc::signal<void, AbstractLauncherIcon::Ptr> IconAdded; + std::vector<VolumeLauncherIcon::Ptr> GetIcons() const; + + sigc::signal<void, AbstractLauncherIcon::Ptr> icon_added;  private:  void PopulateEntries(); @@ -55,8 +55,6 @@ private:  DevicesSettings::Ptr devices_settings_;  FileManagerOpener::Ptr file_manager_opener_;  DeviceNotificationDisplay::Ptr device_notification_display_; - - glib::Idle device_populate_idle_;  };  } diff --git a/launcher/DndData.cpp b/launcher/DndData.cpp index 63cfd485e..9482e45f5 100644 --- a/launcher/DndData.cpp +++ b/launcher/DndData.cpp @@ -27,12 +27,12 @@  #include <UnityCore/GLibWrapper.h>  namespace unity { -  -void DndData::Fill(char* uris) -{  + +void DndData::Fill(const char* uris) +{  Reset(); -  - char* pch = strtok (uris, "\r\n"); + + const char* pch = strtok (const_cast<char*>(uris), "\r\n");  while (pch)  {  glib::String content_type(g_content_type_guess(pch, diff --git a/launcher/DndData.h b/launcher/DndData.h index e714d8bcf..48c5014bc 100644 --- a/launcher/DndData.h +++ b/launcher/DndData.h @@ -32,7 +32,7 @@ public:  /**  * Fills the object given a list of uris.  **/ - void Fill(char* uris); + void Fill(const char* uris);  /**  * Resets the object. Call this function when no longer need data diff --git a/launcher/ExpoLauncherIcon.cpp b/launcher/ExpoLauncherIcon.cpp new file mode 100644 index 000000000..6c15ca1e0 --- /dev/null +++ b/launcher/ExpoLauncherIcon.cpp @@ -0,0 +1,64 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2012 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Marco Trevisan <marco.trevisan@canonical.com> + */ + +#include "ExpoLauncherIcon.h" +#include "unity-shared/WindowManager.h" +#include "FavoriteStore.h" + +#include <glib/gi18n-lib.h> + +namespace unity +{ +namespace launcher +{ + +ExpoLauncherIcon::ExpoLauncherIcon() + : SimpleLauncherIcon(IconType::EXPO) +{ + tooltip_text = _("Workspace Switcher"); + icon_name = "workspace-switcher"; + SetQuirk(Quirk::VISIBLE, false); + SetQuirk(Quirk::RUNNING, false); + SetShortcut('s'); +} + +void ExpoLauncherIcon::ActivateLauncherIcon(ActionArg arg) +{ + SimpleLauncherIcon::ActivateLauncherIcon(arg); + WindowManager::Default()->InitiateExpo(); +} + +void ExpoLauncherIcon::Stick(bool save) +{ + SimpleLauncherIcon::Stick(save); + SetQuirk(Quirk::VISIBLE, (WindowManager::Default()->WorkspaceCount() > 1)); +} + +std::string ExpoLauncherIcon::GetName() const +{ + return "ExpoLauncherIcon"; +} + +std::string ExpoLauncherIcon::GetRemoteUri() +{ + return FavoriteStore::URI_PREFIX_UNITY + "expo-icon"; +} + +} // namespace launcher +} // namespace unity diff --git a/launcher/ExpoLauncherIcon.h b/launcher/ExpoLauncherIcon.h new file mode 100644 index 000000000..b4a335675 --- /dev/null +++ b/launcher/ExpoLauncherIcon.h @@ -0,0 +1,45 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2012 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * Authored by: Marco Trevisan <marco.trevisan@canonical.com> + */ + +#ifndef EXPO_LAUNCHER_ICON_H +#define EXPO_LAUNCHER_ICON_H + +#include "SimpleLauncherIcon.h" + +namespace unity +{ +namespace launcher +{ + +class ExpoLauncherIcon : public SimpleLauncherIcon +{ +public: + ExpoLauncherIcon(); + void Stick(bool save); + +protected: + void ActivateLauncherIcon(ActionArg arg); + std::string GetName() const; + std::string GetRemoteUri(); +}; + +} +} + +#endif // EXPO_LAUNCHER_ICON_H diff --git a/launcher/FavoriteStore.cpp b/launcher/FavoriteStore.cpp index aca71f8e3..8977da5ac 100644 --- a/launcher/FavoriteStore.cpp +++ b/launcher/FavoriteStore.cpp @@ -1,6 +1,6 @@  // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-  /* -* Copyright (C) 2010 Canonical Ltd +* Copyright (C) 2010-2012 Canonical Ltd  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License version 3 as @@ -15,19 +15,29 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> +* Marco Trevisan <marco.trevisan@canonical.com>  */  #include <NuxCore/Logger.h> -#include "FavoriteStoreGSettings.h" +#include <glib.h> + +#include "FavoriteStore.h" +#include "FavoriteStorePrivate.h"  namespace unity  {  namespace  { - nux::logging::Logger logger("unity.favouritestore"); - FavoriteStore* favoritestore_instance = nullptr; +nux::logging::Logger logger("unity.favorite.store"); +FavoriteStore* favoritestore_instance = nullptr; +const std::string PREFIX_SEPARATOR = "://";  } +const std::string FavoriteStore::URI_PREFIX_APP = "application://"; +const std::string FavoriteStore::URI_PREFIX_FILE = "file://"; +const std::string FavoriteStore::URI_PREFIX_DEVICE = "device://"; +const std::string FavoriteStore::URI_PREFIX_UNITY = "unity://"; +  FavoriteStore::FavoriteStore()  {  if (favoritestore_instance) @@ -56,4 +66,83 @@ FavoriteStore& FavoriteStore::Instance()  return *favoritestore_instance;  } +bool FavoriteStore::IsValidFavoriteUri(std::string const& uri) +{ + if (uri.empty()) + return false; + + if (uri.find(URI_PREFIX_APP) == 0 || uri.find(URI_PREFIX_FILE) == 0) + { + return internal::impl::IsDesktopFilePath(uri); + } + else if (uri.find(URI_PREFIX_DEVICE) == 0) + { + return uri.length() > URI_PREFIX_DEVICE.length(); + } + else if (uri.find(URI_PREFIX_UNITY) == 0) + { + return uri.length() > URI_PREFIX_UNITY.length(); + } + + return false; +} + +std::string FavoriteStore::ParseFavoriteFromUri(std::string const& uri) const +{ + if (uri.empty()) + return ""; + + std::string fav = uri; + auto prefix_pos = fav.find(PREFIX_SEPARATOR); + + if (prefix_pos == std::string::npos) + { + // We assume that favorites with no prefix, but with a .desktop suffix are applications + if (internal::impl::IsDesktopFilePath(uri)) + { + fav = URI_PREFIX_APP + fav; + prefix_pos = URI_PREFIX_APP.length(); + } + } + else + { + prefix_pos += PREFIX_SEPARATOR.length(); + } + + // Matches application://desktop-id.desktop or application:///path/to/file.desktop + if (fav.find(URI_PREFIX_APP) == 0 || fav.find(URI_PREFIX_FILE) == 0) + { + std::string const& fav_value = fav.substr(prefix_pos); + + if (fav_value.empty()) + { + LOG_WARNING(logger) << "Unable to load Favorite for uri '" << fav << "'"; + return ""; + } + + if (fav_value[0] == '/' || fav.find(URI_PREFIX_FILE) == 0) + { + if (g_file_test(fav_value.c_str(), G_FILE_TEST_EXISTS)) + { + return fav; + } + else + { + LOG_WARNING(logger) << "Unable to load desktop file: " << fav_value; + } + } + else + { + return URI_PREFIX_APP + fav_value; + } + } + else if (IsValidFavoriteUri(fav)) + { + return fav; + } + + LOG_WARNING(logger) << "Unable to load Favorite for uri '" << fav << "'"; + return ""; +} +  } diff --git a/launcher/FavoriteStore.h b/launcher/FavoriteStore.h index 95675b29e..b31c989ad 100644 --- a/launcher/FavoriteStore.h +++ b/launcher/FavoriteStore.h @@ -1,6 +1,6 @@  // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-  /* -* Copyright (C) 2010 Canonical Ltd +* Copyright (C) 2010-2012 Canonical Ltd  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License version 3 as @@ -15,6 +15,7 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> +* Marco Trevisan <marco.trevisan@canonical.com>  */  #ifndef UNITY_FAVORITE_STORE_H @@ -42,26 +43,38 @@ public:  static FavoriteStore& Instance(); - virtual FavoriteList const& GetFavorites() = 0; + virtual FavoriteList const& GetFavorites() const = 0;  // These will NOT emit the relevant signals, so bare that in mind  // i.e. don't hope that you can add stuff and hook the view up to  // favorite_added events to update the view. The signals only emit if  // there has been a change on the GSettings object from an external  // source - virtual void AddFavorite(std::string const& desktop_path, int position) = 0; - virtual void RemoveFavorite(std::string const& desktop_path) = 0; - virtual void MoveFavorite(std::string const& desktop_path, int position) = 0; - virtual void SetFavorites(FavoriteList const& desktop_paths) = 0; + virtual void AddFavorite(std::string const& icon_uri, int position) = 0; + virtual void RemoveFavorite(std::string const& icon_uri) = 0; + virtual void MoveFavorite(std::string const& icon_uri, int position) = 0; + virtual bool IsFavorite(std::string const& icon_uri) const = 0; + virtual int FavoritePosition(std::string const& icon_uri) const = 0; + virtual void SetFavorites(FavoriteList const& icon_uris) = 0;  // Signals  // These only emit if something has changed the GSettings object externally - //desktop_path, position, before/after + //icon_uri, position, before/after  sigc::signal<void, std::string const&, std::string const&, bool> favorite_added; - //desktop_path + //icon_uri  sigc::signal<void, std::string const&> favorite_removed;  sigc::signal<void> reordered; + + static const std::string URI_PREFIX_APP; + static const std::string URI_PREFIX_FILE; + static const std::string URI_PREFIX_DEVICE; + static const std::string URI_PREFIX_UNITY; + + static bool IsValidFavoriteUri(std::string const& uri); + +protected: + std::string ParseFavoriteFromUri(std::string const& uri) const;  };  } diff --git a/launcher/FavoriteStoreGSettings.cpp b/launcher/FavoriteStoreGSettings.cpp index 087e9bcea..102ba2ff2 100644 --- a/launcher/FavoriteStoreGSettings.cpp +++ b/launcher/FavoriteStoreGSettings.cpp @@ -1,6 +1,6 @@  // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-  /* -* Copyright (C) 2010 Canonical Ltd +* Copyright (C) 2010-2012 Canonical Ltd  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License version 3 as @@ -15,9 +15,9 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> +* Marco Trevisan <marco.trevisan@canonical.com>  */ -#include <gio/gdesktopappinfo.h>  #include <NuxCore/Logger.h>  #include <UnityCore/DesktopUtilities.h> @@ -40,7 +40,7 @@ namespace internal  namespace  { -nux::logging::Logger logger("unity.favorites"); +nux::logging::Logger logger("unity.favorite.store.gsettings");  const std::string SETTINGS_NAME = "com.canonical.Unity.Launcher";  const std::string SETTINGS_KEY = "favorites";  } @@ -59,117 +59,89 @@ FavoriteStoreGSettings::FavoriteStoreGSettings()  void FavoriteStoreGSettings::Refresh()  { - FillList(favorites_); + FillList();  } -void FavoriteStoreGSettings::FillList(FavoriteList& list) +void FavoriteStoreGSettings::FillList()  { - list.clear(); + favorites_.clear(); + std::shared_ptr<gchar*> favs(g_settings_get_strv(settings_, SETTINGS_KEY.c_str())); - gchar** favs = g_settings_get_strv(settings_, SETTINGS_KEY.c_str()); - - for (int i = 0; favs[i] != NULL; ++i) + for (int i = 0; favs.get()[i]; ++i)  { - // We will be storing either full /path/to/desktop/files or foo.desktop id's - if (favs[i][0] == '/') - { - if (g_file_test(favs[i], G_FILE_TEST_EXISTS)) - { - list.push_back(favs[i]); - } - else - { - LOG_WARNING(logger) << "Unable to load desktop file: " - << favs[i]; - } - } - else - { - glib::Object<GDesktopAppInfo> info(g_desktop_app_info_new(favs[i])); - const char* filename = 0; - if (info) - filename = g_desktop_app_info_get_filename(info); - - if (filename) - { - list.push_back(filename); - } - else - { - LOG_WARNING(logger) << "Unable to load GDesktopAppInfo for '" << favs[i] << "'"; - } - } - } + std::string const& fav = ParseFavoriteFromUri(favs.get()[i]); - g_strfreev(favs); + if (!fav.empty()) + favorites_.push_back(fav); + }  } -FavoriteList const& FavoriteStoreGSettings::GetFavorites() +FavoriteList const& FavoriteStoreGSettings::GetFavorites() const  {  return favorites_;  } -void FavoriteStoreGSettings::AddFavorite(std::string const& desktop_path, int position) +void FavoriteStoreGSettings::AddFavorite(std::string const& icon_uri, int position)  { - int size = favorites_.size(); - if (desktop_path.empty() || position > size) + std::string const& fav = ParseFavoriteFromUri(icon_uri); + + if (fav.empty() || position > static_cast<int>(favorites_.size()))  return;  if (position < 0)  {  // It goes on the end. - favorites_.push_back(desktop_path); + favorites_.push_back(fav);  }  else  {  FavoriteList::iterator pos = favorites_.begin();  std::advance(pos, position); - favorites_.insert(pos, desktop_path); + favorites_.insert(pos, fav);  }  SaveFavorites(favorites_);  Refresh();  } -void FavoriteStoreGSettings::RemoveFavorite(std::string const& desktop_path) +void FavoriteStoreGSettings::RemoveFavorite(std::string const& icon_uri)  { - if (desktop_path.empty() || desktop_path[0] != '/') + std::string const& fav = ParseFavoriteFromUri(icon_uri); + + if (fav.empty())  return; - FavoriteList::iterator pos = std::find(favorites_.begin(), favorites_.end(), desktop_path); + FavoriteList::iterator pos = std::find(favorites_.begin(), favorites_.end(), fav);  if (pos == favorites_.end()) - {  return; - }  favorites_.erase(pos);  SaveFavorites(favorites_);  Refresh();  } -void FavoriteStoreGSettings::MoveFavorite(std::string const& desktop_path, int position) +void FavoriteStoreGSettings::MoveFavorite(std::string const& icon_uri, int position)  { - int size = favorites_.size(); - if (desktop_path.empty() || position > size) + std::string const& fav = ParseFavoriteFromUri(icon_uri); + + if (fav.empty() || position > static_cast<int>(favorites_.size()))  return; - FavoriteList::iterator pos = std::find(favorites_.begin(), favorites_.end(), desktop_path); + FavoriteList::iterator pos = std::find(favorites_.begin(), favorites_.end(), fav);  if (pos == favorites_.end()) - {  return; - }  favorites_.erase(pos);  if (position < 0)  {  // It goes on the end. - favorites_.push_back(desktop_path); + favorites_.push_back(fav);  }  else  {  FavoriteList::iterator insert_pos = favorites_.begin();  std::advance(insert_pos, position); - favorites_.insert(insert_pos, desktop_path); + favorites_.insert(insert_pos, fav);  }  SaveFavorites(favorites_); @@ -186,26 +158,34 @@ void FavoriteStoreGSettings::SaveFavorites(FavoriteList const& favorites, bool i  {  const int size = favorites.size();  const char* favs[size + 1]; - favs[size] = NULL; - int index = 0;  // Since we don't always save the full path, we store the values we are  // actually going to save in a different list. - auto system_dirs = DesktopUtilities::GetDataDirectories();  FavoriteList values; - for (FavoriteList::const_iterator i = favorites.begin(), end = favorites.end(); - i != end; ++i, ++index) + int index = 0; + + for (auto const& fav_uri : favorites)  { + std::string const& fav = ParseFavoriteFromUri(fav_uri); + if (fav.empty()) + { + LOG_WARNING(logger) << "Impossible to add favorite '" << fav_uri << "' to store"; + continue; + } +  // By using insert we get the iterator to the newly inserted string value.  // That way we can use the c_str() method to access the const char* for  // the string that we are going to save. This way we know that the pointer  // is valid for the lifetime of the favs array usage in the method call to  // set the settings, and that we aren't referencing a temporary. - std::string const& desktop_id = DesktopUtilities::GetDesktopID(system_dirs, *i); - FavoriteList::iterator iter = values.insert(values.end(), desktop_id); + FavoriteList::iterator iter = values.insert(values.end(), fav);  favs[index] = iter->c_str(); + ++index;  } + for (int i = index; i <= size; ++i) + favs[i] = nullptr; +  ignore_signals_ = ignore;  if (!g_settings_set_strv(settings_, SETTINGS_KEY.c_str(), favs))  { @@ -220,7 +200,7 @@ void FavoriteStoreGSettings::Changed()  return;  FavoriteList old(favorites_); - FillList(favorites_); + FillList();  auto newbies = impl::GetNewbies(old, favorites_); @@ -243,7 +223,26 @@ void FavoriteStoreGSettings::Changed()  if (impl::NeedToBeReordered(old, favorites_))  reordered.emit(); +} + +bool FavoriteStoreGSettings::IsFavorite(std::string const& icon_uri) const +{ + return std::find(favorites_.begin(), favorites_.end(), icon_uri) != favorites_.end(); +} + +int FavoriteStoreGSettings::FavoritePosition(std::string const& icon_uri) const +{ + int index = 0; + + for (auto const& fav : favorites_) + { + if (fav == icon_uri) + return index; + + ++index; + } + return -1;  }  } // namespace internal diff --git a/launcher/FavoriteStoreGSettings.h b/launcher/FavoriteStoreGSettings.h index 5fd084b90..2250fef82 100644 --- a/launcher/FavoriteStoreGSettings.h +++ b/launcher/FavoriteStoreGSettings.h @@ -1,6 +1,6 @@  // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-  /* -* Copyright (C) 2010 Canonical Ltd +* Copyright (C) 2010-2012 Canonical Ltd  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License version 3 as @@ -15,6 +15,7 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Neil Jagdish Patel <neil.patel@canonical.com> +* Marco Trevisan <marco.trevisan@canonical.com>  */  #ifndef FAVORITE_STORE_GSETTINGS_H @@ -38,17 +39,19 @@ class FavoriteStoreGSettings : public FavoriteStore  public:  FavoriteStoreGSettings(); - virtual FavoriteList const& GetFavorites(); + virtual FavoriteList const& GetFavorites() const;  virtual void AddFavorite(std::string const& desktop_path, int position);  virtual void RemoveFavorite(std::string const& desktop_path);  virtual void MoveFavorite(std::string const& desktop_path, int position); - void SaveFavorites(FavoriteList const& favorites, bool ignore = true); + virtual bool IsFavorite(std::string const& icon_uri) const; + virtual int FavoritePosition(std::string const& icon_uri) const;  virtual void SetFavorites(FavoriteList const& desktop_paths); + void SaveFavorites(FavoriteList const& favorites, bool ignore = true);  private:  void Refresh();  void Changed(); - void FillList(FavoriteList& list); + void FillList();  FavoriteList favorites_;  bool ignore_signals_; diff --git a/launcher/FavoriteStorePrivate.cpp b/launcher/FavoriteStorePrivate.cpp index 5478fa0d5..b4d1d94f4 100644 --- a/launcher/FavoriteStorePrivate.cpp +++ b/launcher/FavoriteStorePrivate.cpp @@ -124,6 +124,20 @@ bool NeedToBeReordered(std::list<std::string> const& old, std::list<std::string>  return false;  } +bool IsDesktopFilePath(std::string const& path) +{ + static const std::string desktop_ext = ".desktop"; + auto path_len = path.length(); + auto desktop_length = desktop_ext.length(); + + if (path_len > desktop_length) + { + return path.compare(path_len - desktop_length, desktop_length, desktop_ext) == 0; + } + + return false; +} +  } // namespace impl  } // namespace internal diff --git a/launcher/FavoriteStorePrivate.h b/launcher/FavoriteStorePrivate.h index 196751452..3edad2357 100644 --- a/launcher/FavoriteStorePrivate.h +++ b/launcher/FavoriteStorePrivate.h @@ -32,13 +32,15 @@ namespace impl  std::vector<std::string> GetNewbies(std::list<std::string> const& old, std::list<std::string> const& fresh); -void GetSignalAddedInfo(std::list<std::string> const& favs, std::vector<std::string> const& newbies,  +void GetSignalAddedInfo(std::list<std::string> const& favs, std::vector<std::string> const& newbies,  std::string const& path, std::string& position, bool& before);  std::vector<std::string> GetRemoved(std::list<std::string> const& old, std::list<std::string> const& fresh);  bool NeedToBeReordered(std::list<std::string> const& old, std::list<std::string> const& fresh); +bool IsDesktopFilePath(std::string const& path); +  } // namespace impl  } // namespace internal diff --git a/launcher/HudLauncherIcon.cpp b/launcher/HudLauncherIcon.cpp index 2c24957a0..9f617d475 100644 --- a/launcher/HudLauncherIcon.cpp +++ b/launcher/HudLauncherIcon.cpp @@ -45,6 +45,7 @@ HudLauncherIcon::HudLauncherIcon(LauncherHideMode hide_mode)  {  tooltip_text = _("HUD");  icon_name = PKGDATADIR"/launcher_bfb.png"; + position = Position::BEGIN;  SetQuirk(Quirk::VISIBLE, false);  SetQuirk(Quirk::RUNNING, false);  SetQuirk(Quirk::ACTIVE, true); @@ -121,7 +122,7 @@ nux::Color HudLauncherIcon::GlowColor()  void HudLauncherIcon::ActivateLauncherIcon(ActionArg arg)  { - if (GetQuirk(Quirk::VISIBLE)) + if (IsVisible())  {  ubus_manager_.SendMessage(UBUS_HUD_CLOSE_REQUEST);  } diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index 742759240..56d1fc05a 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -54,6 +54,7 @@  #include <UnityCore/GLibWrapper.h>  #include <UnityCore/Variant.h> +#include <boost/algorithm/string.hpp>  #include <sigc++/sigc++.h>  namespace unity @@ -106,6 +107,7 @@ Launcher::Launcher(nux::BaseWindow* parent,  nux::ObjectPtr<DNDCollectionWindow> const& collection_window,  NUX_FILE_LINE_DECL)  : View(NUX_FILE_LINE_PARAM) + , display(nux::GetGraphicsDisplay()->GetX11Display())  , monitor(0)  , _parent(parent)  , _active_quicklist(nullptr) @@ -463,9 +465,8 @@ bool Launcher::AnimationInProgress() const  return true;  // animations happening on specific icons - LauncherModel::iterator it; - for (it = _model->begin(); it != _model->end(); ++it) - if (IconNeedsAnimation(*it, current)) + for (auto const &icon : *_model) + if (IconNeedsAnimation(icon, current))  return true;  return false; @@ -496,10 +497,10 @@ float Launcher::IconVisibleProgress(AbstractLauncherIcon::Ptr icon, struct times  if (icon->GetIconType() == AbstractLauncherIcon::IconType::HUD)  { - return (icon->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)) ? 1.0f : 0.0f; + return icon->IsVisible() ? 1.0f : 0.0f;  } - if (icon->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)) + if (icon->IsVisible())  {  struct timespec icon_visible_time = icon->GetQuirkTime(AbstractLauncherIcon::Quirk::VISIBLE);  int enter_ms = unity::TimeUtil::TimeDelta(¤t, &icon_visible_time); @@ -892,8 +893,11 @@ void Launcher::FillRenderArg(AbstractLauncherIcon::Ptr icon,  if (GetActionState() == ACTION_DRAG_ICON ||  (_drag_window && _drag_window->Animating()) || - icon->IsSpacer()) + icon->GetIconType() == AbstractLauncherIcon::IconType::SPACER) + {  arg.skip = true; + } +  size_modifier *= DragThresholdProgress(current);  } @@ -933,11 +937,12 @@ void Launcher::FillRenderArg(AbstractLauncherIcon::Ptr icon,  icon->SetCenter(nux::Point3(roundf(center.x), roundf(center.y), roundf(center.z)), monitor, parent_abs_geo); - // FIXME: this is a hack, we should have a look why SetAnimationTarget is necessary in SetAnimationTarget - // we should ideally just need it at start to set the target + // FIXME: this is a hack, to avoid that we set the target to the end of the icon  if (!_initial_drag_animation && icon == _drag_icon && _drag_window && _drag_window->Animating()) - _drag_window->SetAnimationTarget(static_cast<int>(_drag_icon->GetCenter(monitor).x), - static_cast<int>(_drag_icon->GetCenter(monitor).y)); + { + auto const& icon_center = _drag_icon->GetCenter(monitor); + _drag_window->SetAnimationTarget(icon_center.x, icon_center.y); + }  center.y += (half_size * size_modifier) + spacing; // move to end  } @@ -954,10 +959,10 @@ float Launcher::DragLimiter(float x)  nux::Color FullySaturateColor (nux::Color color)  {  float max = std::max<float>(color.red, std::max<float>(color.green, color.blue)); +  if (max > 0.0f) - {  color = color * (1.0f / max); - } +  return color;  } @@ -1397,8 +1402,8 @@ void Launcher::DndTimeoutSetup()  void Launcher::OnPluginStateChanged()  { - _hide_machine.SetQuirk (LauncherHideMachine::EXPO_ACTIVE, WindowManager::Default ()->IsExpoActive ()); - _hide_machine.SetQuirk (LauncherHideMachine::SCALE_ACTIVE, WindowManager::Default ()->IsScaleActive ()); + _hide_machine.SetQuirk(LauncherHideMachine::EXPO_ACTIVE, WindowManager::Default()->IsExpoActive()); + _hide_machine.SetQuirk(LauncherHideMachine::SCALE_ACTIVE, WindowManager::Default()->IsScaleActive());  }  LauncherHideMode Launcher::GetHideMode() const @@ -1670,11 +1675,6 @@ LauncherModel::Ptr Launcher::GetModel() const  return _model;  } -void Launcher::SetDevicesSettings(DevicesSettings::Ptr devices_settings) -{ - devices_settings_ = devices_settings; -} -  void Launcher::EnsureIconOnScreen(AbstractLauncherIcon::Ptr selection)  {  nux::Geometry const& geo = GetGeometry(); @@ -1682,7 +1682,7 @@ void Launcher::EnsureIconOnScreen(AbstractLauncherIcon::Ptr selection)  int natural_y = 0;  for (auto icon : *_model)  { - if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE) || !icon->IsVisibleOnMonitor(monitor)) + if (!icon->IsVisible() || !icon->IsVisibleOnMonitor(monitor))  continue;  if (icon == selection) @@ -1954,7 +1954,7 @@ void Launcher::OnDragWindowAnimCompleted()  EnsureAnimation();  } -bool Launcher::StartIconDragTimeout() +bool Launcher::StartIconDragTimeout(int x, int y)  {  // if we are still waiting…  if (GetActionState() == ACTION_NONE) @@ -1965,7 +1965,7 @@ bool Launcher::StartIconDragTimeout()  _icon_under_mouse = nullptr;  }  _initial_drag_animation = true; - StartIconDragRequest(GetMouseX(), GetMouseY()); + StartIconDragRequest(x, y);  }  return false; @@ -1973,26 +1973,27 @@ bool Launcher::StartIconDragTimeout()  void Launcher::StartIconDragRequest(int x, int y)  { - nux::Geometry geo = GetAbsoluteGeometry(); - AbstractLauncherIcon::Ptr drag_icon = MouseIconIntersection((int)(GetGeometry().width / 2.0f), y); - - x += geo.x; - y += geo.y; - + nux::Geometry const& abs_geo = GetAbsoluteGeometry(); + AbstractLauncherIcon::Ptr drag_icon = MouseIconIntersection(abs_geo.width / 2.0f, y);  // FIXME: nux doesn't give nux::GetEventButton (button_flags) there, relying  // on an internal Launcher property then - bool can_drag = (_model->IconHasSister(drag_icon) || drag_icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE); - if (drag_icon && _last_button_press == 1 && can_drag) + if (drag_icon && _last_button_press == 1 && drag_icon->position() == AbstractLauncherIcon::Position::FLOATING)  { + auto const& icon_center = drag_icon->GetCenter(monitor); + x += abs_geo.x; + y += abs_geo.y; +  SetActionState(ACTION_DRAG_ICON);  StartIconDrag(drag_icon); - UpdateDragWindowPosition(drag_icon->GetCenter(monitor).x, drag_icon->GetCenter(monitor).y); + UpdateDragWindowPosition(icon_center.x, icon_center.y); +  if (_initial_drag_animation)  {  _drag_window->SetAnimationTarget(x, y);  _drag_window->StartAnimation();  } +  EnsureAnimation();  }  else @@ -2009,6 +2010,7 @@ void Launcher::StartIconDrag(AbstractLauncherIcon::Ptr icon)  _hide_machine.SetQuirk(LauncherHideMachine::INTERNAL_DND_ACTIVE, true);  _drag_icon = icon; + _drag_icon_position = _model->IconIndex(icon);  HideDragWindow();  _offscreen_drag_texture = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture(_icon_size, _icon_size, 1, nux::BITFMT_R8G8B8A8); @@ -2032,15 +2034,20 @@ void Launcher::EndIconDrag()  {  hovered_icon->SetQuirk(AbstractLauncherIcon::Quirk::PULSE_ONCE, true); - launcher_removerequest.emit(_drag_icon); + remove_request.emit(_drag_icon);  HideDragWindow();  EnsureAnimation();  }  else  { - if (!_drag_window->Cancelled()) + if (!_drag_window->Cancelled() && _model->IconIndex(_drag_icon) != _drag_icon_position) + { + if (_drag_icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) + _drag_icon->Stick(false); +  _model->Save(); + }  if (_drag_window->on_anim_completed.connected())  _drag_window->on_anim_completed.disconnect(); @@ -2104,15 +2111,11 @@ void Launcher::UpdateDragWindowPosition(int x, int y)  return;  auto const& launcher_geo = GetGeometry(); - auto hovered_icon = MouseIconIntersection((launcher_geo.x + launcher_geo.width) / 2.0, y - GetAbsoluteY()); + auto const& hovered_icon = MouseIconIntersection((launcher_geo.x + launcher_geo.width) / 2.0, y - GetAbsoluteY());  struct timespec current;  clock_gettime(CLOCK_MONOTONIC, ¤t);  float progress = DragThresholdProgress(current); - // Icons of different types can't be mixed, so let's avoid this. - if (hovered_icon && hovered_icon->GetIconType() != _drag_icon->GetIconType()) - hovered_icon = nullptr; -  if (hovered_icon && _drag_icon != hovered_icon)  {  if (progress >= 1.0f) @@ -2131,12 +2134,8 @@ void Launcher::UpdateDragWindowPosition(int x, int y)  {  auto const& icon = *it; - if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE) || - !icon->IsVisibleOnMonitor(monitor) || - icon->GetIconType() != _drag_icon->GetIconType()) - { + if (!icon->IsVisible() || !icon->IsVisibleOnMonitor(monitor))  continue; - }  if (y >= icon->GetCenter(monitor).y)  { @@ -2217,7 +2216,9 @@ void Launcher::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_  }  else  { - StartIconDragRequest(x, y); + // We we can safely start the icon drag, from the original mouse-down position + sources_.Remove(START_DRAGICON_DURATION); + StartIconDragRequest(x - _dnd_delta_x, y - _dnd_delta_y);  }  }  else if (GetActionState() == ACTION_DRAG_LAUNCHER) @@ -2227,7 +2228,7 @@ void Launcher::RecvMouseDrag(int x, int y, int dx, int dy, unsigned long button_  }  else if (GetActionState() == ACTION_DRAG_ICON)  { - nux::Geometry geo = GetAbsoluteGeometry(); + nux::Geometry const& geo = GetAbsoluteGeometry();  UpdateDragWindowPosition(geo.x + x, geo.y + y);  } @@ -2406,7 +2407,7 @@ void Launcher::MouseDownLogic(int x, int y, unsigned long button_flags, unsigned  {  _icon_mouse_down = launcher_icon;  // if MouseUp after the time ended -> it's an icon drag, otherwise, it's starting an app - auto cb_func = sigc::mem_fun(this, &Launcher::StartIconDragTimeout); + auto cb_func = sigc::bind(sigc::mem_fun(this, &Launcher::StartIconDragTimeout), x, y);  sources_.AddTimeout(START_DRAGICON_DURATION, cb_func, START_DRAGICON_TIMEOUT);  launcher_icon->mouse_down.emit(nux::GetEventButton(button_flags), monitor, key_flags); @@ -2453,7 +2454,7 @@ AbstractLauncherIcon::Ptr Launcher::MouseIconIntersection(int x, int y)  for (it = _model->begin(); it != _model->end(); ++it)  { - if (!(*it)->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE) || !(*it)->IsVisibleOnMonitor(monitor)) + if (!(*it)->IsVisible() || !(*it)->IsVisibleOnMonitor(monitor))  continue;  nux::Point2 screen_coord [4]; @@ -2538,35 +2539,41 @@ void Launcher::RestoreSystemRenderTarget()  nux::GetWindowCompositor().RestoreRenderingSurface();  } +bool Launcher::DndIsSpecialRequest(std::string const& uri) const +{ + return (boost::algorithm::ends_with(uri, ".desktop") || uri.find("device://") == 0); +} +  void Launcher::OnDNDDataCollected(const std::list<char*>& mimes)  {  _dnd_data.Reset(); - unity::glib::String uri_list_const(g_strdup("text/uri-list")); + const std::string uri_list = "text/uri-list"; + auto& display = nux::GetWindowThread()->GetGraphicsDisplay(); - for (auto it : mimes) + for (auto const& mime : mimes)  { - if (!g_str_equal(it, uri_list_const.Value())) + if (mime != uri_list)  continue; - _dnd_data.Fill(nux::GetWindowThread()->GetGraphicsDisplay().GetDndData(uri_list_const.Value())); + _dnd_data.Fill(display.GetDndData(const_cast<char*>(uri_list.c_str())));  break;  }  _hide_machine.SetQuirk(LauncherHideMachine::EXTERNAL_DND_ACTIVE, true); - for (auto it : _dnd_data.Uris()) + auto const& uris = _dnd_data.Uris(); + if (std::find_if(uris.begin(), uris.end(), [this] (std::string const& uri) + {return DndIsSpecialRequest(uri);}) != uris.end())  { - if (g_str_has_suffix(it.c_str(), ".desktop") || g_str_has_prefix(it.c_str(), "device://")) - { - _steal_drag = true; - break; - } - } + _steal_drag = true; - if (!_steal_drag) + if (IsOverlayOpen()) + SaturateIcons(); + } + else  { - for (auto it : *_model) + for (auto const& it : *_model)  {  if (it->ShouldHighlightOnDrag(_dnd_data))  { @@ -2580,11 +2587,6 @@ void Launcher::OnDNDDataCollected(const std::list<char*>& mimes)  }  }  } - else - { - if (IsOverlayOpen()) - SaturateIcons(); - }  }  void Launcher::ProcessDndEnter() @@ -2652,32 +2654,29 @@ void Launcher::ProcessDndLeave()  void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes)  { - nux::Area* parent = GetToplevel(); - unity::glib::String uri_list_const(g_strdup("text/uri-list")); -  if (!_data_checked)  { + const std::string uri_list = "text/uri-list";  _data_checked = true;  _dnd_data.Reset(); + auto& display = nux::GetWindowThread()->GetGraphicsDisplay();  // get the data - for (auto it : mimes) + for (auto const& mime : mimes)  { - if (!g_str_equal(it, uri_list_const.Value())) + if (mime != uri_list)  continue; - _dnd_data.Fill(nux::GetWindowThread()->GetGraphicsDisplay().GetDndData(uri_list_const.Value())); + _dnd_data.Fill(display.GetDndData(const_cast<char*>(uri_list.c_str())));  break;  }  // see if the launcher wants this one - for (auto it : _dnd_data.Uris()) + auto const& uris = _dnd_data.Uris(); + if (std::find_if(uris.begin(), uris.end(), [this] (std::string const& uri) + {return DndIsSpecialRequest(uri);}) != uris.end())  { - if (g_str_has_suffix(it.c_str(), ".desktop") || g_str_has_prefix(it.c_str(), "device://")) - { - _steal_drag = true; - break; - } + _steal_drag = true;  }  // only set hover once we know our first x/y @@ -2686,7 +2685,7 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes)  if (!_steal_drag)  { - for (auto it : *_model) + for (auto const& it : *_model)  {  if (it->ShouldHighlightOnDrag(_dnd_data))  it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false); @@ -2696,7 +2695,7 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes)  }  } - SetMousePosition(x - parent->GetGeometry().x, y - parent->GetGeometry().y); + SetMousePosition(x - _parent->GetGeometry().x, y - _parent->GetGeometry().y);  if (!IsOverlayOpen() && _mouse_position.x == 0 && _mouse_position.y <= (_parent->GetGeometry().height - _icon_size - 2 * _space_between_icons) && !_drag_edge_touching)  { @@ -2723,7 +2722,7 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes)  if (hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH)  _steal_drag = false; - if (hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION || hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::EXPO) + if (hovered_icon->position() == AbstractLauncherIcon::Position::FLOATING)  hovered_icon_is_appropriate = true;  } @@ -2733,7 +2732,6 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes)  if (!_dnd_hovered_icon && hovered_icon_is_appropriate)  {  _dnd_hovered_icon = new SpacerLauncherIcon(monitor()); - _dnd_hovered_icon->SetSortPriority(G_MAXINT);  _model->AddIcon(_dnd_hovered_icon);  _model->ReorderBefore(_dnd_hovered_icon, hovered_icon, true);  } @@ -2788,34 +2786,10 @@ void Launcher::ProcessDndDrop(int x, int y)  {  if (_steal_drag)  { - for (auto it : _dnd_data.Uris()) + for (auto const& uri : _dnd_data.Uris())  { - if (g_str_has_suffix(it.c_str(), ".desktop")) - { - char* path = nullptr; - - if (g_str_has_prefix(it.c_str(), "application://")) - { - const char* tmp = it.c_str() + strlen("application://"); - unity::glib::String tmp2(g_strdup_printf("file:///usr/share/applications/%s", tmp)); - path = g_filename_from_uri(tmp2.Value(), NULL, NULL); - } - else if (g_str_has_prefix(it.c_str(), "file://")) - { - path = g_filename_from_uri(it.c_str(), NULL, NULL); - } - - if (path) - { - launcher_addrequest.emit(path, _dnd_hovered_icon); - g_free(path); - } - } - else if (devices_settings_ && g_str_has_prefix(it.c_str(), "device://")) - { - const gchar* uuid = it.c_str() + 9; - devices_settings_->TryToUnblacklist(uuid); - } + if (DndIsSpecialRequest(uri)) + add_request.emit(uri, _dnd_hovered_icon);  }  }  else if (_dnd_hovered_icon && _drag_action != nux::DNDACTION_NONE) diff --git a/launcher/Launcher.h b/launcher/Launcher.h index c23489c4d..d9759ba78 100644 --- a/launcher/Launcher.h +++ b/launcher/Launcher.h @@ -83,8 +83,6 @@ public:  void SetModel(LauncherModel::Ptr model);  LauncherModel::Ptr GetModel() const; - void SetDevicesSettings(DevicesSettings::Ptr devices_settings); -  void StartKeyShowLauncher();  void EndKeyShowLauncher(); @@ -121,9 +119,8 @@ public:  int GetDragDelta() const;  void SetHover(bool hovered); - sigc::signal<void, char*, AbstractLauncherIcon::Ptr> launcher_addrequest; - sigc::signal<void, AbstractLauncherIcon::Ptr> launcher_removerequest; - sigc::signal<void, AbstractLauncherIcon::Ptr> icon_animation_complete; + sigc::signal<void, std::string const&, AbstractLauncherIcon::Ptr> add_request; + sigc::signal<void, AbstractLauncherIcon::Ptr> remove_request;  sigc::signal<void> selection_change;  sigc::signal<void> hidden_changed;  sigc::signal<void> sc_launcher_icon_animation; @@ -199,7 +196,7 @@ private:  void OnSelectionChanged(AbstractLauncherIcon::Ptr selection);  bool StrutHack(); - bool StartIconDragTimeout(); + bool StartIconDragTimeout(int x, int y);  bool OnScrollTimeout();  bool OnUpdateDragManagerTimeout(); @@ -319,6 +316,7 @@ private:  void DndReset();  void DndHoveredIconReset();  void DndTimeoutSetup(); + bool DndIsSpecialRequest(std::string const& uri) const;  LauncherModel::Ptr _model;  nux::BaseWindow* _parent; @@ -365,6 +363,7 @@ private:  int _launcher_drag_delta_min;  int _enter_y;  int _last_button_press; + int _drag_icon_position;  float _drag_out_delta_x;  bool _drag_gesture_ongoing;  float _last_reveal_progress; @@ -389,8 +388,6 @@ private:  ui::AbstractIconRenderer::Ptr icon_renderer;  BackgroundEffectHelper bg_effect_helper_; - DevicesSettings::Ptr devices_settings_; -  UBusManager ubus_;  glib::SourceManager sources_; diff --git a/launcher/LauncherController.cpp b/launcher/LauncherController.cpp index bcc59bdca..c365dd96f 100644 --- a/launcher/LauncherController.cpp +++ b/launcher/LauncherController.cpp @@ -1,6 +1,6 @@  // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-  /* - * Copyright (C) 2010, 2011 Canonical Ltd + * Copyright (C) 2010-2012 Canonical Ltd  *  * This program is free software: you can redistribute it and/or modify  * it under the terms of the GNU General Public License version 3 as @@ -16,6 +16,7 @@  *  * Authored by: Jason Smith <jason.smith@canonical.com>  * Tim Penhey <tim.penhey@canonical.com> + * Marco Trevisan <marco.trevisan@canonical.com>  */  #include <glib/gi18n-lib.h> @@ -25,6 +26,7 @@  #include <Nux/HLayout.h>  #include <Nux/BaseWindow.h>  #include <NuxCore/Logger.h> +#include <UnityCore/DesktopUtilities.h>  #include "LauncherOptions.h"  #include "BamfLauncherIcon.h" @@ -35,6 +37,7 @@  #include "LauncherController.h"  #include "LauncherControllerPrivate.h"  #include "SoftwareCenterLauncherIcon.h" +#include "ExpoLauncherIcon.h"  #include "unity-shared/WindowManager.h"  #include "TrashLauncherIcon.h"  #include "BFBLauncherIcon.h" @@ -82,63 +85,45 @@ namespace  const std::string KEYPRESS_TIMEOUT = "keypress-timeout";  const std::string LABELS_TIMEOUT = "label-show-timeout";  const std::string HIDE_TIMEOUT = "hide-timeout"; -} -} + const std::string SOFTWARE_CENTER_AGENT = "software-center-agent"; + const std::string RUNNING_APPS_URI = FavoriteStore::URI_PREFIX_UNITY + "running-apps"; + const std::string DEVICES_URI = FavoriteStore::URI_PREFIX_UNITY + "devices"; +} +}  GDBusInterfaceVTable Controller::Impl::interface_vtable =  { Controller::Impl::OnDBusMethodCall, NULL, NULL}; -Controller::Impl::Impl(Display* display, Controller* parent) + +Controller::Impl::Impl(Controller* parent)  : parent_(parent) - , model_(new LauncherModel()) - , sort_priority_(0) - , volume_monitor_(new VolumeMonitorWrapper) - , devices_settings_(new DevicesSettingsImp) - , device_section_(volume_monitor_, devices_settings_) - , show_desktop_icon_(false) - , display_(display) + , model_(std::make_shared<LauncherModel>())  , matcher_(bamf_matcher_get_default()) + , device_section_(std::make_shared<VolumeMonitorWrapper>(), std::make_shared<DevicesSettingsImp>()) + , expo_icon_(new ExpoLauncherIcon()) + , desktop_icon_(new DesktopLauncherIcon()) + , sort_priority_(AbstractLauncherIcon::DefaultPriority(AbstractLauncherIcon::IconType::APPLICATION)) + , launcher_open(false) + , launcher_keynav(false) + , launcher_grabbed(false) + , reactivate_keynav(false) + , keynav_restore_window_(true) + , launcher_key_press_time_(0) + , dbus_owner_(g_bus_own_name(G_BUS_TYPE_SESSION, DBUS_NAME.c_str(), G_BUS_NAME_OWNER_FLAGS_NONE, + OnBusAcquired, nullptr, nullptr, this, nullptr))  {  edge_barriers_.options = parent_->options();  UScreen* uscreen = UScreen::GetDefault(); - auto monitors = uscreen->GetMonitors(); - int primary = uscreen->GetPrimaryMonitor(); + EnsureLaunchers(uscreen->GetPrimaryMonitor(), uscreen->GetMonitors()); - launcher_open = false; - launcher_keynav = false; - launcher_grabbed = false; - reactivate_keynav = false; - keynav_restore_window_ = true; - - EnsureLaunchers(primary, monitors); - - launcher_ = launchers[0]; - device_section_.IconAdded.connect(sigc::mem_fun(this, &Impl::OnIconAdded)); - - num_workspaces_ = WindowManager::Default()->WorkspaceCount(); - if (num_workspaces_ > 1) - { - InsertExpoAction(); - } - - // Insert the "Show Desktop" launcher icon in the launcher... - if (show_desktop_icon_) - InsertDesktopIcon(); - - InsertTrash(); - - sources_.AddTimeout(500, [&] { SetupBamf(); return false; }); + SetupIcons();  remote_model_.entry_added.connect(sigc::mem_fun(this, &Impl::OnLauncherEntryRemoteAdded));  remote_model_.entry_removed.connect(sigc::mem_fun(this, &Impl::OnLauncherEntryRemoteRemoved)); - FavoriteStore::Instance().favorite_added.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteAdded)); - FavoriteStore::Instance().favorite_removed.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteRemoved)); - FavoriteStore::Instance().reordered.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreReordered)); -  LauncherHideMode hide_mode = parent_->options()->hide_mode;  BFBLauncherIcon* bfb = new BFBLauncherIcon(hide_mode);  RegisterIcon(AbstractLauncherIcon::Ptr(bfb)); @@ -146,35 +131,27 @@ Controller::Impl::Impl(Display* display, Controller* parent)  HudLauncherIcon* hud = new HudLauncherIcon(hide_mode);  RegisterIcon(AbstractLauncherIcon::Ptr(hud)); - parent_->options()->hide_mode.changed.connect([bfb,hud](LauncherHideMode mode) { + TrashLauncherIcon* trash = new TrashLauncherIcon(); + RegisterIcon(AbstractLauncherIcon::Ptr(trash)); + + parent_->options()->hide_mode.changed.connect([bfb, hud](LauncherHideMode mode) {  bfb->SetHideMode(mode);  hud->SetHideMode(mode);  }); - desktop_icon_ = AbstractLauncherIcon::Ptr(new DesktopLauncherIcon()); -  uscreen->changed.connect(sigc::mem_fun(this, &Controller::Impl::OnScreenChanged));  WindowManager& plugin_adapter = *(WindowManager::Default()); - plugin_adapter.window_focus_changed.connect (sigc::mem_fun (this, &Controller::Impl::OnWindowFocusChanged)); - - launcher_key_press_time_ = 0; + plugin_adapter.window_focus_changed.connect(sigc::mem_fun(this, &Controller::Impl::OnWindowFocusChanged));  ubus.RegisterInterest(UBUS_QUICKLIST_END_KEY_NAV, [&](GVariant * args) {  if (reactivate_keynav)  parent_->KeyNavGrab(); - model_->SetSelection(reactivate_index); - }); - parent_->AddChild(model_.get()); - - uscreen->resuming.connect([&]() -> void { - for (auto launcher : launchers) - launcher->QueueDraw(); + model_->SetSelection(reactivate_index);  }); - dbus_owner_ = g_bus_own_name(G_BUS_TYPE_SESSION, DBUS_NAME.c_str(), G_BUS_NAME_OWNER_FLAGS_NONE, - OnBusAcquired, nullptr, nullptr, this, nullptr); + parent_->AddChild(model_.get());  }  Controller::Impl::~Impl() @@ -182,9 +159,9 @@ Controller::Impl::~Impl()  // Since the launchers are in a window which adds a reference to the  // launcher, we need to make sure the base windows are unreferenced  // otherwise the launchers never die. - for (auto launcher_ptr : launchers) + for (auto const& launcher_ptr : launchers)  { - if (launcher_ptr.IsValid()) + if (launcher_ptr)  launcher_ptr->GetParent()->UnReference();  } @@ -198,7 +175,7 @@ void Controller::Impl::EnsureLaunchers(int primary, std::vector<nux::Geometry> c  unsigned int launchers_size = launchers.size();  unsigned int last_launcher = 0; - for (unsigned int i = 0; i < num_launchers; i++, last_launcher++) + for (unsigned int i = 0; i < num_launchers; ++i, ++last_launcher)  {  if (i >= launchers_size)  { @@ -232,6 +209,7 @@ void Controller::Impl::EnsureLaunchers(int primary, std::vector<nux::Geometry> c  }  } + launcher_ = launchers[0];  launchers.resize(num_launchers);  } @@ -240,7 +218,7 @@ void Controller::Impl::OnScreenChanged(int primary_monitor, std::vector<nux::Geo  EnsureLaunchers(primary_monitor, monitors);  } -void Controller::Impl::OnWindowFocusChanged (guint32 xid) +void Controller::Impl::OnWindowFocusChanged(guint32 xid)  {  static bool keynav_first_focus = false; @@ -264,11 +242,9 @@ Launcher* Controller::Impl::CreateLauncher(int monitor)  nux::BaseWindow* launcher_window = new nux::BaseWindow(TEXT("LauncherWindow"));  Launcher* launcher = new Launcher(launcher_window, nux::ObjectPtr<DNDCollectionWindow>(new DNDCollectionWindow)); - launcher->display = display_;  launcher->monitor = monitor;  launcher->options = parent_->options();  launcher->SetModel(model_); - launcher->SetDevicesSettings(devices_settings_);  nux::HLayout* layout = new nux::HLayout(NUX_TRACKER_LOCATION);  layout->AddView(launcher, 1); @@ -283,58 +259,103 @@ Launcher* Controller::Impl::CreateLauncher(int monitor)  launcher_window->InputWindowEnableStruts(parent_->options()->hide_mode == LAUNCHER_HIDE_NEVER);  launcher_window->SetEnterFocusInputArea(launcher); - launcher->launcher_addrequest.connect(sigc::mem_fun(this, &Impl::OnLauncherAddRequest)); - launcher->launcher_removerequest.connect(sigc::mem_fun(this, &Impl::OnLauncherRemoveRequest)); - - launcher->icon_animation_complete.connect(sigc::mem_fun(this, &Impl::OnSCIconAnimationComplete)); + launcher->add_request.connect(sigc::mem_fun(this, &Impl::OnLauncherAddRequest)); + launcher->remove_request.connect(sigc::mem_fun(this, &Impl::OnLauncherRemoveRequest));  parent_->AddChild(launcher);  return launcher;  } -void Controller::Impl::OnLauncherAddRequest(char* path, AbstractLauncherIcon::Ptr before) +void Controller::Impl::OnLauncherAddRequest(std::string const& icon_uri, AbstractLauncherIcon::Ptr icon_before)  { - for (auto it : model_->GetSublist<BamfLauncherIcon> ()) + std::string app_uri; + + if (icon_uri.find(FavoriteStore::URI_PREFIX_FILE) == 0)  { - if (path && path == it->DesktopFile()) - { - it->Stick(); - model_->ReorderBefore(it, before, false); - Save(); - return; - } + auto const& desktop_path = icon_uri.substr(FavoriteStore::URI_PREFIX_FILE.length()); + app_uri = FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(desktop_path);  } - AbstractLauncherIcon::Ptr result = CreateFavorite(path); - if (result) + auto const& icon = GetIconByUri(app_uri.empty() ? icon_uri : app_uri); + + if (icon)  { - RegisterIcon(result); - if (before) - model_->ReorderBefore(result, before, false); + icon->Stick(false); + model_->ReorderAfter(icon, icon_before); + } + else + { + if (icon_before) + RegisterIcon(CreateFavoriteIcon(icon_uri), icon_before->SortPriority()); + else + RegisterIcon(CreateFavoriteIcon(icon_uri));  } - Save(); + SaveIconsOrder();  } -void Controller::Impl::Save() +void Controller::Impl::AddFavoriteKeepingOldPosition(FavoriteList& icons, std::string const& icon_uri) const  { - unity::FavoriteList desktop_paths; + auto const& favorites = FavoriteStore::Instance().GetFavorites(); + auto it = std::find(favorites.rbegin(), favorites.rend(), icon_uri); + + FavoriteList::reverse_iterator icons_it = icons.rbegin(); - // Updates gsettings favorites. - auto launchers = model_->GetSublist<BamfLauncherIcon> (); - for (auto icon : launchers) + while (it != favorites.rend()) + { + icons_it = std::find(icons.rbegin(), icons.rend(), *it); + + if (icons_it != icons.rend()) + break; + + ++it; + } + + icons.insert(icons_it.base(), icon_uri); +} + +void Controller::Impl::SaveIconsOrder() +{ + FavoriteList icons; + bool found_first_running_app = false; + bool found_first_device = false; + + for (auto const& icon : *model_)  {  if (!icon->IsSticky()) + { + if (!icon->IsVisible()) + continue; + + if (!found_first_running_app && icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION) + { + found_first_running_app = true; + icons.push_back(local::RUNNING_APPS_URI); + } + + if (!found_first_device && icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) + { + found_first_device = true; + icons.push_back(local::DEVICES_URI); + } +  continue; + } - std::string const& desktop_file = icon->DesktopFile(); + std::string const& remote_uri = icon->RemoteUri(); - if (!desktop_file.empty()) - desktop_paths.push_back(desktop_file); + if (!remote_uri.empty()) + icons.push_back(remote_uri);  } - unity::FavoriteStore::Instance().SetFavorites(desktop_paths); + if (!found_first_running_app) + AddFavoriteKeepingOldPosition(icons, local::RUNNING_APPS_URI); + + if (!found_first_device) + AddFavoriteKeepingOldPosition(icons, local::DEVICES_URI); + + FavoriteStore::Instance().SetFavorites(icons);  }  void @@ -345,64 +366,53 @@ Controller::Impl::OnLauncherAddRequestSpecial(std::string const& path,  int icon_y,  int icon_size)  { - auto bamf_icons = model_->GetSublist<BamfLauncherIcon>(); - for (auto icon : bamf_icons) - { - if (icon->DesktopFile() == path) - return; - } -  // Check if desktop file was supplied, or if it's set to SC's agent  // See https://bugs.launchpad.net/unity/+bug/1002440 - if (path.empty() || path == "software-center-agent") + if (path.empty() || path == local::SOFTWARE_CENTER_AGENT)  return; - SoftwareCenterLauncherIcon::Ptr result = CreateSCLauncherIcon(path, aptdaemon_trans_id, icon_path); + auto const& icon = std::find_if(model_->begin(), model_->end(), + [&path](AbstractLauncherIcon::Ptr const& i) { return (i->DesktopFile() == path); }); - CurrentLauncher()->ForceReveal(true); + if (icon != model_->end()) + return; + + auto const& result = CreateSCLauncherIcon(path, aptdaemon_trans_id, icon_path);  if (result)  { - result->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); - result->Animate(CurrentLauncher(), icon_x, icon_y, icon_size); - RegisterIcon(result); - Save(); + // Setting the icon position and adding it to the model, makes the launcher + // to compute its center + RegisterIcon(result, GetLastIconPriority<BamfLauncherIcon>("", true)); + + // This will ensure that the center of the new icon is set, so that + // the animation could be done properly. + sources_.AddIdle([this, icon_x, icon_y, result] { + result->Animate(CurrentLauncher(), icon_x, icon_y); + return false; + });  }  } -void Controller::Impl::OnSCIconAnimationComplete(AbstractLauncherIcon::Ptr icon) -{ - icon->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); - launcher_->ForceReveal(false); -} -  void Controller::Impl::SortAndUpdate()  { - gint shortcut = 1; + unsigned shortcut = 1; - auto launchers = model_->GetSublist<BamfLauncherIcon> (); - for (auto icon : launchers) + for (auto const& icon : model_->GetSublist<BamfLauncherIcon>())  {  if (shortcut <= 10 && icon->IsVisible())  { - std::stringstream shortcut_string; - shortcut_string << (shortcut % 10); - icon->SetShortcut(shortcut_string.str()[0]); + icon->SetShortcut(std::to_string(shortcut % 10)[0]);  ++shortcut;  } - // reset shortcut  else  { + // reset shortcut  icon->SetShortcut(0);  }  }  } -void Controller::Impl::OnIconAdded(AbstractLauncherIcon::Ptr icon) -{ - this->RegisterIcon(icon); -} -  void Controller::Impl::OnIconRemoved(AbstractLauncherIcon::Ptr icon)  {  SortAndUpdate(); @@ -428,10 +438,13 @@ void Controller::Impl::OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr icon)  {  auto device_icon = dynamic_cast<VolumeLauncherIcon*>(icon.GetPointer()); - if (device_icon && device_icon->CanEject()) - device_icon->EjectAndShowNotification(); - else if (device_icon && device_icon->CanStop()) - device_icon->StopDrive(); + if (device_icon) + { + if (device_icon->CanEject()) + device_icon->EjectAndShowNotification(); + else if (device_icon->CanStop()) + device_icon->StopDrive(); + }  break;  } @@ -442,63 +455,63 @@ void Controller::Impl::OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr icon)  void Controller::Impl::OnLauncherEntryRemoteAdded(LauncherEntryRemote::Ptr const& entry)  { - for (auto icon : *model_) - { - if (!icon || icon->RemoteUri().empty()) - continue; + if (entry->AppUri().empty()) + return; - if (entry->AppUri() == icon->RemoteUri()) - { - icon->InsertEntryRemote(entry); - } - } + auto const& apps_icons = model_->GetSublist<BamfLauncherIcon>(); + auto const& icon = std::find_if(apps_icons.begin(), apps_icons.end(), + [&entry](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == entry->AppUri()); }); + + if (icon != apps_icons.end()) + (*icon)->InsertEntryRemote(entry);  }  void Controller::Impl::OnLauncherEntryRemoteRemoved(LauncherEntryRemote::Ptr const& entry)  { - for (auto icon : *model_) - { + for (auto const& icon : *model_)  icon->RemoveEntryRemote(entry); - }  }  void Controller::Impl::OnFavoriteStoreFavoriteAdded(std::string const& entry, std::string const& pos, bool before)  { - auto bamf_list = model_->GetSublist<BamfLauncherIcon>(); - AbstractLauncherIcon::Ptr other; - if (bamf_list.size() > 0) - other = *(bamf_list.begin()); + if (entry == local::RUNNING_APPS_URI || entry == local::DEVICES_URI) + { + // Since the running apps and the devices are always shown, when added to + // the model, we only have to re-order them + ResetIconPriorities(); + return; + } + + AbstractLauncherIcon::Ptr other = *(model_->begin());  if (!pos.empty())  { - for (auto it : bamf_list) + for (auto const& it : *model_)  { - if (it->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE) && pos == it->DesktopFile()) + if (it->IsVisible() && pos == it->RemoteUri())  other = it;  }  } - for (auto it : bamf_list) + AbstractLauncherIcon::Ptr const& fav = GetIconByUri(entry); + if (fav)  { - if (entry == it->DesktopFile()) - { - it->Stick(false); - if (!before) - model_->ReorderAfter(it, other); - else - model_->ReorderBefore(it, other, false); - return; - } - } + fav->Stick(false); - AbstractLauncherIcon::Ptr result = CreateFavorite(entry.c_str()); - if (result) + if (before) + model_->ReorderBefore(fav, other, false); + else + model_->ReorderAfter(fav, other); + } + else  { + AbstractLauncherIcon::Ptr const& result = CreateFavoriteIcon(entry);  RegisterIcon(result); - if (!before) - model_->ReorderAfter(result, other); - else + + if (before)  model_->ReorderBefore(result, other, false); + else + model_->ReorderAfter(result, other);  }  SortAndUpdate(); @@ -506,104 +519,132 @@ void Controller::Impl::OnFavoriteStoreFavoriteAdded(std::string const& entry, st  void Controller::Impl::OnFavoriteStoreFavoriteRemoved(std::string const& entry)  { - for (auto icon : model_->GetSublist<BamfLauncherIcon> ()) + if (entry == local::RUNNING_APPS_URI || entry == local::DEVICES_URI)  { - if (icon->DesktopFile() == entry) - { - icon->UnStick(); - break; - } + // Since the running apps and the devices are always shown, when added to + // the model, we only have to re-order them + ResetIconPriorities(); + return; + } + + auto const& icon = GetIconByUri(entry); + if (icon) + { + icon->UnStick(); + + // When devices are removed from favorites, they should be re-ordered (not removed) + if (icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) + ResetIconPriorities();  }  } -void Controller::Impl::OnFavoriteStoreReordered() +void Controller::Impl::ResetIconPriorities()  {  FavoriteList const& favs = FavoriteStore::Instance().GetFavorites(); - auto bamf_list = model_->GetSublist<BamfLauncherIcon>(); + auto const& apps_icons = model_->GetSublist<BamfLauncherIcon>(); + auto const& volumes_icons = model_->GetSublist<VolumeLauncherIcon>(); + bool running_apps_found = false; + bool volumes_found = false; - int i = 0; - for (auto it : favs) + for (auto const& fav : favs)  { - auto icon = std::find_if(bamf_list.begin(), bamf_list.end(), - [&it](AbstractLauncherIcon::Ptr x) { return (x->DesktopFile() == it); }); + if (fav == local::RUNNING_APPS_URI) + { + for (auto const& ico : apps_icons) + { + if (!ico->IsSticky()) + ico->SetSortPriority(++sort_priority_); + } - if (icon != bamf_list.end()) + running_apps_found = true; + continue; + } + else if (fav == local::DEVICES_URI)  { - (*icon)->SetSortPriority(i++); + for (auto const& ico : volumes_icons) + { + if (!ico->IsSticky()) + ico->SetSortPriority(++sort_priority_); + } + + volumes_found = true; + continue;  } + + auto const& icon = GetIconByUri(fav); + + if (icon) + icon->SetSortPriority(++sort_priority_);  } - for (auto it : bamf_list) + if (!running_apps_found)  { - if (!it->IsSticky()) - it->SetSortPriority(i++); + for (auto const& ico : apps_icons) + { + if (!ico->IsSticky()) + ico->SetSortPriority(++sort_priority_); + }  } - model_->Sort(); -} - -void Controller::Impl::OnExpoActivated() -{ - WindowManager::Default()->InitiateExpo(); -} + if (!volumes_found) + { + for (auto const& ico : volumes_icons) + { + if (!ico->IsSticky()) + ico->SetSortPriority(++sort_priority_); + } + } -void Controller::Impl::InsertTrash() -{ - AbstractLauncherIcon::Ptr icon(new TrashLauncherIcon()); - RegisterIcon(icon); + model_->Sort();  }  void Controller::Impl::UpdateNumWorkspaces(int workspaces)  { - if ((num_workspaces_ == 0) && (workspaces > 0)) + bool visible = expo_icon_->IsVisible(); + bool wp_enabled = (workspaces > 1); + + if (wp_enabled && !visible)  { - InsertExpoAction(); + if (FavoriteStore::Instance().IsFavorite(expo_icon_->RemoteUri())) + { + expo_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); + }  } - else if ((num_workspaces_ > 0) && (workspaces == 0)) + else if (!wp_enabled && visible)  { - RemoveExpoAction(); + expo_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false);  } - - num_workspaces_ = workspaces;  } -void Controller::Impl::InsertExpoAction() +void Controller::Impl::RegisterIcon(AbstractLauncherIcon::Ptr icon, int priority)  { - expo_icon_ = AbstractLauncherIcon::Ptr(new SimpleLauncherIcon(AbstractLauncherIcon::IconType::EXPO)); - - SimpleLauncherIcon* icon = static_cast<SimpleLauncherIcon*>(expo_icon_.GetPointer()); - icon->tooltip_text = _("Workspace Switcher"); - icon->icon_name = "workspace-switcher"; - icon->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); - icon->SetQuirk(AbstractLauncherIcon::Quirk::RUNNING, false); - icon->SetShortcut('s'); - - on_expoicon_activate_connection_ = icon->activate.connect(sigc::mem_fun(this, &Impl::OnExpoActivated)); - + if (!icon || model_->IconIndex(icon) >= 0) + return; - RegisterIcon(expo_icon_); -} + if (priority != std::numeric_limits<int>::min()) + icon->SetSortPriority(priority); -void Controller::Impl::RemoveExpoAction() -{ - if (on_expoicon_activate_connection_) - on_expoicon_activate_connection_.disconnect(); - model_->RemoveIcon(expo_icon_); -} + icon->position_saved.connect([this] { + // These calls must be done in order: first we save the new sticky icons + // then we re-order the model so that there won't be two icons with the same + // priority + SaveIconsOrder(); + ResetIconPriorities(); + }); -void Controller::Impl::InsertDesktopIcon() -{ - RegisterIcon(desktop_icon_); -} + std::string const& icon_uri = icon->RemoteUri(); + icon->position_forgot.connect([this, icon_uri] { + FavoriteStore::Instance().RemoveFavorite(icon_uri); + }); -void Controller::Impl::RemoveDesktopIcon() -{ - model_->RemoveIcon(desktop_icon_); -} + if (icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION) + { + icon->visibility_changed.connect(sigc::mem_fun(this, &Impl::SortAndUpdate)); + SortAndUpdate(); + } -void Controller::Impl::RegisterIcon(AbstractLauncherIcon::Ptr icon) -{  model_->AddIcon(icon); +  std::string const& path = icon->DesktopFile();  if (!path.empty()) @@ -615,7 +656,59 @@ void Controller::Impl::RegisterIcon(AbstractLauncherIcon::Ptr icon)  }  } -/* static private */ +template<typename IconType> +int Controller::Impl::GetLastIconPriority(std::string const& favorite_uri, bool sticky) +{ + auto const& icons = model_->GetSublist<IconType>(); + int icon_prio = std::numeric_limits<int>::min(); + + AbstractLauncherIcon::Ptr last_icon; + + // Get the last (non)-sticky icon position (if available) + for (auto it = icons.rbegin(); it != icons.rend(); ++it) + { + auto const& icon = *it; + bool update_last_icon = ((!last_icon && !sticky) || sticky); + + if (update_last_icon || icon->IsSticky() == sticky) + { + last_icon = icon; + + if (icon->IsSticky() == sticky) + break; + } + } + + if (last_icon) + { + icon_prio = last_icon->SortPriority(); + + if (sticky && last_icon->IsSticky() != sticky) + icon_prio -= 1; + } + else if (!favorite_uri.empty()) + { + // If we have no applications opened, we must guess it position by favorites + for (auto const& fav : FavoriteStore::Instance().GetFavorites()) + { + if (fav == favorite_uri) + { + if (icon_prio == std::numeric_limits<int>::min()) + icon_prio = (*model_->begin())->SortPriority() - 1; + + break; + } + + auto const& icon = GetIconByUri(fav); + + if (icon) + icon_prio = icon->SortPriority(); + } + } + + return icon_prio; +} +  void Controller::Impl::OnViewOpened(BamfMatcher* matcher, BamfView* view)  {  if (!BAMF_IS_APPLICATION(view)) @@ -630,35 +723,111 @@ void Controller::Impl::OnViewOpened(BamfMatcher* matcher, BamfView* view)  }  AbstractLauncherIcon::Ptr icon(new BamfLauncherIcon(app)); - icon->visibility_changed.connect(sigc::mem_fun(this, &Impl::SortAndUpdate)); - icon->SetSortPriority(sort_priority_++); - RegisterIcon(icon); - SortAndUpdate(); + RegisterIcon(icon, GetLastIconPriority<BamfLauncherIcon>(local::RUNNING_APPS_URI));  } -AbstractLauncherIcon::Ptr Controller::Impl::CreateFavorite(const char* file_path) +void Controller::Impl::OnDeviceIconAdded(AbstractLauncherIcon::Ptr icon) +{ + RegisterIcon(icon, GetLastIconPriority<VolumeLauncherIcon>(local::DEVICES_URI)); +} + +AbstractLauncherIcon::Ptr Controller::Impl::CreateFavoriteIcon(std::string const& icon_uri)  { - BamfApplication* app;  AbstractLauncherIcon::Ptr result; - app = bamf_matcher_get_application_for_desktop_file(matcher_, file_path, true); - if (!app) + if (!FavoriteStore::IsValidFavoriteUri(icon_uri)) + { + LOG_WARNING(logger) << "Ignoring favorite '" << icon_uri << "'.";  return result; + } - if (g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen"))) + std::string desktop_id; + + if (icon_uri.find(FavoriteStore::URI_PREFIX_APP) == 0)  { - bamf_view_set_sticky(BAMF_VIEW(app), true); - return result; + desktop_id = icon_uri.substr(FavoriteStore::URI_PREFIX_APP.size()); + } + else if (icon_uri.find(FavoriteStore::URI_PREFIX_FILE) == 0) + { + desktop_id = icon_uri.substr(FavoriteStore::URI_PREFIX_FILE.size());  } - bamf_view_set_sticky(BAMF_VIEW(app), true); - AbstractLauncherIcon::Ptr icon (new BamfLauncherIcon(app)); - icon->SetSortPriority(sort_priority_++); - result = icon; + if (!desktop_id.empty()) + { + BamfApplication* app; + std::string const& desktop_path = DesktopUtilities::GetDesktopPathById(desktop_id); + + app = bamf_matcher_get_application_for_desktop_file(matcher_, desktop_path.c_str(), true); + + if (!app) + return result; + + if (g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen"))) + { + bamf_view_set_sticky(BAMF_VIEW(app), true); + return result; + } + + result = AbstractLauncherIcon::Ptr(new BamfLauncherIcon(app)); + } + else if (icon_uri.find(FavoriteStore::URI_PREFIX_DEVICE) == 0) + { + auto const& devices = device_section_.GetIcons(); + auto const& icon = std::find_if(devices.begin(), devices.end(), + [&icon_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == icon_uri); }); + + if (icon == devices.end()) + { + // Using an idle to remove the favorite, not to erase while iterating + sources_.AddIdle([this, icon_uri] { + FavoriteStore::Instance().RemoveFavorite(icon_uri); + return false; + }); + + return result; + } + + result = *icon; + } + else if (desktop_icon_->RemoteUri() == icon_uri) + { + result = desktop_icon_; + } + else if (expo_icon_->RemoteUri() == icon_uri) + { + result = expo_icon_; + } + + if (result) + { + if (!result->IsSticky()) + result->Stick(false); + else + { + LOG_ERROR(logger) << "Ignoring favorite '" << icon_uri << "': it's already on the launcher!"; + result = nullptr; + } + }  return result;  } +AbstractLauncherIcon::Ptr Controller::Impl::GetIconByUri(std::string const& icon_uri) +{ + if (icon_uri.empty()) + return AbstractLauncherIcon::Ptr(); + + auto const& icon = std::find_if(model_->begin(), model_->end(), + [&icon_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == icon_uri); }); + + if (icon != model_->end()) + { + return *icon; + } + + return AbstractLauncherIcon::Ptr(); +} +  SoftwareCenterLauncherIcon::Ptr Controller::Impl::CreateSCLauncherIcon(std::string const& file_path,  std::string const& aptdaemon_trans_id,  std::string const& icon_path) @@ -678,50 +847,79 @@ SoftwareCenterLauncherIcon::Ptr Controller::Impl::CreateSCLauncherIcon(std::stri  bamf_view_set_sticky(BAMF_VIEW(app), true);  result = new SoftwareCenterLauncherIcon(app, aptdaemon_trans_id, icon_path); - result->SetSortPriority(sort_priority_++);  return result;  } -void Controller::Impl::SetupBamf() +void Controller::Impl::AddRunningApps()  { - GList* apps, *l; - BamfApplication* app; - - FavoriteList const& favs = FavoriteStore::Instance().GetFavorites(); + std::shared_ptr<GList> apps(bamf_matcher_get_applications(matcher_), g_list_free); - for (FavoriteList::const_iterator i = favs.begin(), end = favs.end(); - i != end; ++i) + for (GList *l = apps.get(); l; l = l->next)  { - AbstractLauncherIcon::Ptr fav = CreateFavorite(i->c_str()); + if (!BAMF_IS_APPLICATION(l->data)) + continue; - if (fav) - { - fav->SetSortPriority(sort_priority_++); - RegisterIcon(fav); - } - } + BamfApplication* app = BAMF_APPLICATION(l->data); - apps = bamf_matcher_get_applications(matcher_); - view_opened_signal_.Connect(matcher_, "view-opened", sigc::mem_fun(this, &Impl::OnViewOpened)); + if (g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen"))) + continue; - for (l = apps; l; l = l->next) + AbstractLauncherIcon::Ptr icon(new BamfLauncherIcon(app)); + RegisterIcon(icon, ++sort_priority_); + } +} + +void Controller::Impl::AddDevices() +{ + auto& fav_store = FavoriteStore::Instance(); + for (auto const& icon : device_section_.GetIcons())  { - app = BAMF_APPLICATION(l->data); + if (!icon->IsSticky() && !fav_store.IsFavorite(icon->RemoteUri())) + RegisterIcon(icon, ++sort_priority_); + } +} - if (g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen"))) +void Controller::Impl::SetupIcons() +{ + auto& favorite_store = FavoriteStore::Instance(); + FavoriteList const& favs = favorite_store.GetFavorites(); + bool running_apps_added = false; + bool devices_added = false; + + for (auto const& fav_uri : favs) + { + if (fav_uri == local::RUNNING_APPS_URI) + { + AddRunningApps(); + running_apps_added = true;  continue; + } + else if (fav_uri == local::DEVICES_URI) + { + AddDevices(); + devices_added = true; + continue; + } - AbstractLauncherIcon::Ptr icon(new BamfLauncherIcon(app)); - icon->SetSortPriority(sort_priority_++); - RegisterIcon(icon); + RegisterIcon(CreateFavoriteIcon(fav_uri), ++sort_priority_);  } - g_list_free(apps); - SortAndUpdate(); + + if (!running_apps_added) + AddRunningApps(); + + if (!devices_added) + AddDevices(); + + view_opened_signal_.Connect(matcher_, "view-opened", sigc::mem_fun(this, &Impl::OnViewOpened)); + device_section_.icon_added.connect(sigc::mem_fun(this, &Impl::OnDeviceIconAdded)); + favorite_store.favorite_added.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteAdded)); + favorite_store.favorite_removed.connect(sigc::mem_fun(this, &Impl::OnFavoriteStoreFavoriteRemoved)); + favorite_store.reordered.connect(sigc::mem_fun(this, &Impl::ResetIconPriorities));  model_->order_changed.connect(sigc::mem_fun(this, &Impl::SortAndUpdate));  model_->icon_removed.connect(sigc::mem_fun(this, &Impl::OnIconRemoved)); - model_->saved.connect(sigc::mem_fun(this, &Impl::Save)); + model_->saved.connect(sigc::mem_fun(this, &Impl::SaveIconsOrder));  }  void Controller::Impl::SendHomeActivationRequest() @@ -729,10 +927,10 @@ void Controller::Impl::SendHomeActivationRequest()  ubus.SendMessage(UBUS_PLACE_ENTRY_ACTIVATE_REQUEST, g_variant_new("(sus)", "home.lens", dash::NOT_HANDLED, ""));  } -Controller::Controller(Display* display) +Controller::Controller()  : options(Options::Ptr(new Options()))  , multiple_launchers(true) - , pimpl(new Impl(display, this)) + , pimpl(new Impl(this))  {  multiple_launchers.changed.connect([&](bool value) -> void {  UScreen* uscreen = UScreen::GetDefault(); @@ -814,19 +1012,6 @@ void Controller::PushToFront()  pimpl->launcher_->GetParent()->PushToFront();  } -void Controller::SetShowDesktopIcon(bool show_desktop_icon) -{ - if (pimpl->show_desktop_icon_ == show_desktop_icon) - return; - - pimpl->show_desktop_icon_ = show_desktop_icon; - - if (pimpl->show_desktop_icon_) - pimpl->InsertDesktopIcon(); - else - pimpl->RemoveDesktopIcon(); -} -  int Controller::Impl::MonitorWithMouse()  {  UScreen* uscreen = UScreen::GetDefault(); @@ -1110,7 +1295,7 @@ Controller::AddProperties(GVariantBuilder* builder)  timespec current;  clock_gettime(CLOCK_MONOTONIC, ¤t); - unity::variant::BuilderWrapper(builder) + variant::BuilderWrapper(builder)  .add("key_nav_is_active", KeyNavIsActive())  .add("key_nav_launcher_monitor", pimpl->keyboard_launcher_.IsValid() ? pimpl->keyboard_launcher_->monitor : -1)  .add("key_nav_selection", pimpl->model_->SelectionIndex()) diff --git a/launcher/LauncherController.h b/launcher/LauncherController.h index e18f2cae2..ec203ec8f 100644 --- a/launcher/LauncherController.h +++ b/launcher/LauncherController.h @@ -47,7 +47,7 @@ public:  nux::Property<Options::Ptr> options;  nux::Property<bool> multiple_launchers; - Controller(Display* display); + Controller();  ~Controller();  Launcher& launcher() const; @@ -61,8 +61,6 @@ public:  void PushToFront(); - void SetShowDesktopIcon(bool show_desktop_icon); -  bool AboutToShowDash(int was_tap, int when) const;  void HandleLauncherKeyPress(int when); diff --git a/launcher/LauncherControllerPrivate.h b/launcher/LauncherControllerPrivate.h index cd730a37e..9cb22c856 100644 --- a/launcher/LauncherControllerPrivate.h +++ b/launcher/LauncherControllerPrivate.h @@ -46,59 +46,53 @@ namespace launcher  class Controller::Impl  {  public: - Impl(Display* display, Controller* parent); + Impl(Controller* parent);  ~Impl();  void UpdateNumWorkspaces(int workspaces);  Launcher* CreateLauncher(int monitor); - void Save(); + void SaveIconsOrder();  void SortAndUpdate();  nux::ObjectPtr<Launcher> CurrentLauncher(); - void OnIconAdded(AbstractLauncherIcon::Ptr icon); + template<typename IconType> + int GetLastIconPriority(std::string const& favorite_uri = "", bool sticky = false); + void AddFavoriteKeepingOldPosition(FavoriteList& icons, std::string const& icon_uri) const; +  void OnIconRemoved(AbstractLauncherIcon::Ptr icon); + void OnDeviceIconAdded(AbstractLauncherIcon::Ptr icon); - void OnLauncherAddRequest(char* path, AbstractLauncherIcon::Ptr before); + void OnLauncherAddRequest(std::string const& icon_uri, AbstractLauncherIcon::Ptr before);  void OnLauncherAddRequestSpecial(std::string const& path, std::string const& aptdaemon_trans_id,  std::string const& icon_path, int icon_x, int icon_y, int icon_size);  void OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr icon); - void OnSCIconAnimationComplete(AbstractLauncherIcon::Ptr icon);  void OnLauncherEntryRemoteAdded(LauncherEntryRemote::Ptr const& entry);  void OnLauncherEntryRemoteRemoved(LauncherEntryRemote::Ptr const& entry);  void OnFavoriteStoreFavoriteAdded(std::string const& entry, std::string const& pos, bool before);  void OnFavoriteStoreFavoriteRemoved(std::string const& entry); - void OnFavoriteStoreReordered(); - - - void InsertExpoAction(); - void RemoveExpoAction(); - - void InsertDesktopIcon(); - void RemoveDesktopIcon(); + void ResetIconPriorities();  void SendHomeActivationRequest();  int MonitorWithMouse(); - void InsertTrash(); - - void RegisterIcon(AbstractLauncherIcon::Ptr icon); - - AbstractLauncherIcon::Ptr CreateFavorite(const char* file_path); + void RegisterIcon(AbstractLauncherIcon::Ptr icon, int priority = std::numeric_limits<int>::min()); + AbstractLauncherIcon::Ptr CreateFavoriteIcon(std::string const& icon_uri); + AbstractLauncherIcon::Ptr GetIconByUri(std::string const& icon_uri);  SoftwareCenterLauncherIcon::Ptr CreateSCLauncherIcon(std::string const& file_path, std::string const& aptdaemon_trans_id, std::string const& icon_path); - void SetupBamf(); + void SetupIcons(); + void AddRunningApps(); + void AddDevices();  void EnsureLaunchers(int primary, std::vector<nux::Geometry> const& monitors); - void OnExpoActivated(); -  void OnScreenChanged(int primary_monitor, std::vector<nux::Geometry>& monitors);  void OnWindowFocusChanged (guint32 xid); @@ -123,38 +117,31 @@ public:  Controller* parent_;  LauncherModel::Ptr model_; + glib::Object<BamfMatcher> matcher_;  nux::ObjectPtr<Launcher> launcher_;  nux::ObjectPtr<Launcher> keyboard_launcher_; - int sort_priority_; - AbstractVolumeMonitorWrapper::Ptr volume_monitor_; - DevicesSettingsImp::Ptr devices_settings_;  DeviceLauncherSection device_section_;  LauncherEntryRemoteModel remote_model_;  AbstractLauncherIcon::Ptr expo_icon_;  AbstractLauncherIcon::Ptr desktop_icon_; - int num_workspaces_; - bool show_desktop_icon_; - Display* display_; - - bool launcher_open; - bool launcher_keynav; - bool launcher_grabbed; - bool reactivate_keynav; - int reactivate_index; - bool keynav_restore_window_; - int launcher_key_press_time_; - unsigned int dbus_owner_; -  ui::EdgeBarrierController edge_barriers_; -  LauncherList launchers; - glib::Object<BamfMatcher> matcher_; + unsigned sort_priority_; + bool launcher_open; + bool launcher_keynav; + bool launcher_grabbed; + bool reactivate_keynav; + int reactivate_index; + bool keynav_restore_window_; + int launcher_key_press_time_; + unsigned dbus_owner_; + +  glib::Signal<void, BamfMatcher*, BamfView*> view_opened_signal_;  glib::SourceManager sources_;  UBusManager ubus; - sigc::connection on_expoicon_activate_connection_;  sigc::connection launcher_key_press_connection_;  sigc::connection launcher_event_outside_connection_;  }; diff --git a/launcher/LauncherIcon.cpp b/launcher/LauncherIcon.cpp index e216358b4..5ddd5494f 100644 --- a/launcher/LauncherIcon.cpp +++ b/launcher/LauncherIcon.cpp @@ -74,10 +74,11 @@ glib::Object<GtkIconTheme> LauncherIcon::_unity_theme;  LauncherIcon::LauncherIcon(IconType type)  : _icon_type(type) + , _sticky(false)  , _remote_urgent(false)  , _present_urgency(0)  , _progress(0) - , _sort_priority(0) + , _sort_priority(DefaultPriority(type))  , _last_monitor(0)  , _background_color(nux::color::White)  , _glow_color(nux::color::White) @@ -89,7 +90,7 @@ LauncherIcon::LauncherIcon(IconType type)  , _saved_center(max_num_monitors)  , _allow_quicklist_to_show(true)  { - for (unsigned i = 0; i < unsigned(Quirk::LAST); i++) + for (unsigned i = 0; i < unsigned(Quirk::LAST); ++i)  {  _quirks[i] = false;  _quirk_times[i].tv_sec = 0; @@ -107,10 +108,10 @@ LauncherIcon::LauncherIcon(IconType type)  tooltip_text.SetSetterFunction(sigc::mem_fun(this, &LauncherIcon::SetTooltipText));  tooltip_text = "blank"; + position = Position::FLOATING; +  // FIXME: the abstraction is already broken, should be fixed for O  // right now, hooking the dynamic quicklist the less ugly possible way - -  mouse_enter.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseEnter));  mouse_leave.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseLeave));  mouse_down.connect(sigc::mem_fun(this, &LauncherIcon::RecvMouseDown)); @@ -517,6 +518,7 @@ LauncherIcon::ShowTooltip()  if (!_tooltip)  LoadTooltip(); + _tooltip->SetText(tooltip_text());  _tooltip->ShowTooltipWithTipAt(tip_x, tip_y);  _tooltip->ShowWindow(!tooltip_text().empty());  tooltip_visible.emit(_tooltip); @@ -1179,6 +1181,31 @@ void LauncherIcon::EmitRemove()  remove.emit(AbstractLauncherIcon::Ptr(this));  } +void LauncherIcon::Stick(bool save) +{ + if (_sticky) + return; + + _sticky = true; + + if (save) + position_saved.emit(); + + SetQuirk(Quirk::VISIBLE, true); +} + +void LauncherIcon::UnStick() +{ + if (!_sticky) + return; + + _sticky = false; + + position_forgot.emit(); + + SetQuirk(Quirk::VISIBLE, false); +} +  } // namespace launcher  } // namespace unity diff --git a/launcher/LauncherIcon.h b/launcher/LauncherIcon.h index 34bdb077e..c1b79a517 100644 --- a/launcher/LauncherIcon.h +++ b/launcher/LauncherIcon.h @@ -108,11 +108,6 @@ public:  const bool WindowVisibleOnViewport(); - virtual bool IsSpacer() - { - return false; - }; -  float PresentUrgency();  float GetProgress(); @@ -188,9 +183,9 @@ public:  virtual std::string DesktopFile() { return std::string(""); } - virtual bool IsSticky() const { return false; } + virtual bool IsSticky() const { return _sticky; } - virtual bool IsVisible() const { return false; } + virtual bool IsVisible() const { return GetQuirk(Quirk::VISIBLE); }  virtual bool IsVisibleOnMonitor(int monitor) const; @@ -198,9 +193,9 @@ public:  virtual void AboutToRemove() {} - virtual void Stick(bool save = true) {} + virtual void Stick(bool save = true); - virtual void UnStick() {} + virtual void UnStick();  protected:  std::vector<nux::Point3> GetCenters(); @@ -313,6 +308,7 @@ private:  void OnTooltipEnabledChanged(bool value); + bool _sticky;  bool _remote_urgent;  float _present_urgency;  float _progress; diff --git a/launcher/LauncherModel.cpp b/launcher/LauncherModel.cpp index 8e01465dd..6efefa106 100644 --- a/launcher/LauncherModel.cpp +++ b/launcher/LauncherModel.cpp @@ -56,14 +56,14 @@ unity::debug::Introspectable::IntrospectableList LauncherModel::GetIntrospectabl  bool LauncherModel::IconShouldShelf(AbstractLauncherIcon::Ptr const& icon) const  { - return icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH; + return icon->position() == AbstractLauncherIcon::Position::END;  }  bool LauncherModel::CompareIcons(AbstractLauncherIcon::Ptr const& first, AbstractLauncherIcon::Ptr const& second)  { - if (first->GetIconType() < second->GetIconType()) + if (first->position() < second->position())  return true; - else if (first->GetIconType() > second->GetIconType()) + else if (first->position() > second->position())  return false;  return first->SortPriority() < second->SortPriority(); @@ -163,25 +163,11 @@ bool LauncherModel::IconHasSister(AbstractLauncherIcon::Ptr const& icon) const  if (!icon)  return false; - const_iterator it; - const_iterator end; + auto const& container = IconShouldShelf(icon) ? _inner_shelf : _inner_main; - if (IconShouldShelf(icon)) + for (auto const& icon_it : container)  { - it = _inner_shelf.begin(); - end = _inner_shelf.end(); - } - else - { - it = _inner_main.begin(); - end = _inner_main.end(); - } - - for (; it != end; ++it) - { - AbstractLauncherIcon::Ptr const& iter_icon = *it; - - if (iter_icon != icon && iter_icon->GetIconType() == icon->GetIconType()) + if (icon_it != icon && icon_it->GetIconType() == icon->GetIconType())  return true;  } @@ -193,16 +179,20 @@ void LauncherModel::ReorderAfter(AbstractLauncherIcon::Ptr const& icon, Abstract  if (icon == other || icon.IsNull() || other.IsNull())  return; - if (icon->GetIconType() != other->GetIconType()) + if (icon->position() != other->position())  return;  icon->SetSortPriority(other->SortPriority() + 1);  for (auto it = std::next(std::find(begin(), end(), other)); it != end(); ++it)  { - // Increasing the priority of the icons next to the other one  auto const& icon_it = *it; - int new_priority = icon_it->SortPriority() + 1; + + if (icon_it == icon) + continue; + + // Increasing the priority of the icons next to the other one + int new_priority = icon_it->SortPriority() + 2;  icon_it->SetSortPriority(new_priority);  } @@ -214,7 +204,7 @@ void LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr const& icon, Abstrac  if (icon == other || icon.IsNull() || other.IsNull())  return; - if (icon->GetIconType() != other->GetIconType()) + if (icon->position() != other->position())  return;  bool found_target = false; @@ -228,7 +218,13 @@ void LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr const& icon, Abstrac  continue;  } - int new_priority = icon_it->SortPriority() + (found_target ? 1 : -1); + int old_priority = icon_it->SortPriority(); + int new_priority = old_priority + (found_target ? 1 : -1); + + // We need to reduce the priority of all the icons previous to 'other' + if (icon_it != other && !found_target && other->SortPriority() == old_priority) + new_priority -= 1; +  icon_it->SetSortPriority(new_priority);  if (icon_it == other) @@ -237,7 +233,7 @@ void LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr const& icon, Abstrac  icon_it->SaveCenter();  center = !center; - new_priority = new_priority - 1; + new_priority += -1;  icon->SetSortPriority(new_priority);  if (animate && center) @@ -260,7 +256,7 @@ void LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr const& icon, Abstract  if (icon == other || icon.IsNull() || other.IsNull())  return; - if (icon->GetIconType() != other->GetIconType()) + if (icon->position() != other->position())  return;  bool found_icon = false; @@ -276,7 +272,13 @@ void LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr const& icon, Abstract  continue;  } - int new_priority = icon_it->SortPriority() + (found_target ? 1 : -1); + int old_priority = icon_it->SortPriority(); + int new_priority = old_priority + (found_target ? 1 : -1); + + // We need to reduce the priority of all the icons previous to 'other' + if (icon_it != other && !found_target && other->SortPriority() == old_priority) + new_priority -= 1; +  icon_it->SetSortPriority(new_priority);  if (icon_it == other) @@ -285,7 +287,7 @@ void LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr const& icon, Abstract  icon_it->SaveCenter();  center = !center; - new_priority = new_priority + (found_icon ? 1 : -1); + new_priority += found_icon ? 1 : -1;  icon->SetSortPriority(new_priority);  if (animate && center) @@ -377,7 +379,7 @@ AbstractLauncherIcon::Ptr LauncherModel::GetClosestIcon(AbstractLauncherIcon::Pt  for (auto const& current : _inner)  { - if (current->GetIconType() != icon->GetIconType()) + if (current->position() != icon->position())  continue;  if (!found_target) diff --git a/launcher/MockLauncherIcon.h b/launcher/MockLauncherIcon.h index b52cb272b..a5c95457a 100644 --- a/launcher/MockLauncherIcon.h +++ b/launcher/MockLauncherIcon.h @@ -45,20 +45,20 @@ class MockLauncherIcon : public AbstractLauncherIcon  NUX_DECLARE_OBJECT_TYPE(MockLauncherIcon, AbstractLauncherIcon);  public:  MockLauncherIcon(IconType type = IconType::APPLICATION) - : type_(type) - , sort_priority_(0) - , icon_(0) + : icon_(0) + , type_(type) + , sort_priority_(DefaultPriority(type)) + , remote_uri_("fake")  {  tooltip_text = "Mock Icon"; + position = Position::FLOATING;  for (unsigned i = 0; i < unsigned(Quirk::LAST); ++i) - {  quirks_[i] = false; - }  }  std::string GetName() const { return "MockLauncherIcon"; } - +   void AddProperties(GVariantBuilder* builder) {}  void HideTooltip() {} @@ -180,11 +180,6 @@ public:  return true;  } - bool IsSpacer() - { - return false; - } -  float PresentUrgency()  {  return 0.0f; @@ -244,7 +239,7 @@ public:  std::string RemoteUri()  { - return "fake"; + return remote_uri_;  }  nux::BaseTexture* TextureForSize(int size) @@ -293,9 +288,9 @@ public:  bool IsVisible() const { return false; }  void AboutToRemove() {} - +   void Stick(bool save = true) {} - +   void UnStick() {}  private: @@ -346,12 +341,13 @@ private:  return result;  } + nux::BaseTexture* icon_;  IconType type_;  int sort_priority_; - nux::BaseTexture* icon_;  bool quirks_[unsigned(Quirk::LAST)];  timespec quirk_times_[unsigned(Quirk::LAST)];  std::map<int, nux::Point3> center_; + std::string remote_uri_;  };  } diff --git a/launcher/SoftwareCenterLauncherIcon.cpp b/launcher/SoftwareCenterLauncherIcon.cpp index 93227a2a8..522034e81 100644 --- a/launcher/SoftwareCenterLauncherIcon.cpp +++ b/launcher/SoftwareCenterLauncherIcon.cpp @@ -45,23 +45,19 @@ SoftwareCenterLauncherIcon::SoftwareCenterLauncherIcon(BamfApplication* app,  , needs_urgent_(false)  , aptdaemon_trans_id_(aptdaemon_trans_id)  { - + SetQuirk(Quirk::VISIBLE, false);  aptdaemon_trans_.Connect("PropertyChanged", sigc::mem_fun(this, &SoftwareCenterLauncherIcon::OnPropertyChanged));  aptdaemon_trans_.Connect("Finished", sigc::mem_fun(this, &SoftwareCenterLauncherIcon::OnFinished)); - icon_name = icon_path; + if (!icon_path.empty()) + icon_name = icon_path; +  if (!aptdaemon_trans_id_.empty()) // Application is being installed, or hasn't been installed yet  tooltip_text = _("Waiting to install");  } -void SoftwareCenterLauncherIcon::Animate(nux::ObjectPtr<Launcher> launcher, - int icon_x, - int icon_y, - int icon_size) +void SoftwareCenterLauncherIcon::Animate(nux::ObjectPtr<Launcher> const& launcher, int start_x, int start_y)  { - int target_x = 0; - int target_y = 0; -  launcher_ = launcher;  icon_texture_ = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture( @@ -72,30 +68,15 @@ void SoftwareCenterLauncherIcon::Animate(nux::ObjectPtr<Launcher> launcher,  drag_window_ = new LauncherDragWindow(icon_texture_); + launcher->ForceReveal(true);  launcher->RenderIconToTexture(nux::GetWindowThread()->GetGraphicsEngine(),  AbstractLauncherIcon::Ptr(this),  icon_texture_); - drag_window_->SetBaseXY(icon_x, icon_y); + auto const& icon_center = GetCenter(launcher->monitor()); + drag_window_->SetBaseXY(start_x, start_y);  drag_window_->ShowWindow(true); - - // Find out the center of last BamfLauncherIcon with non-zero co-ordinates - auto bamf_icons = launcher->GetModel()->GetSublist<BamfLauncherIcon>(); - //TODO: don't iterate through them and pick the last one, just use back() to get the last one. - for (auto current_bamf_icon : bamf_icons) - { - auto icon_center = current_bamf_icon->GetCenter(launcher->monitor); - - if (icon_center.x != 0 && icon_center.y != 0) - { - target_x = icon_center.x; - target_y = icon_center.y; - } - } - - target_y = target_y + (launcher->GetIconSize() / 2); - drag_window_->SetAnimationTarget(target_x, target_y); - + drag_window_->SetAnimationTarget(icon_center.x, icon_center.y + (launcher->GetIconSize() / 2));  drag_window_->on_anim_completed = drag_window_->anim_completed.connect(sigc::mem_fun(this, &SoftwareCenterLauncherIcon::OnDragAnimationFinished));  drag_window_->StartAnimation();  } @@ -103,23 +84,29 @@ void SoftwareCenterLauncherIcon::Animate(nux::ObjectPtr<Launcher> launcher,  void SoftwareCenterLauncherIcon::OnDragAnimationFinished()  {  drag_window_->ShowWindow(false); - launcher_->icon_animation_complete.emit(AbstractLauncherIcon::Ptr(this));  drag_window_ = nullptr; + launcher_->ForceReveal(false); + launcher_ = nullptr; + icon_texture_ = nullptr; + SetQuirk(Quirk::VISIBLE, true);  }  void SoftwareCenterLauncherIcon::ActivateLauncherIcon(ActionArg arg)  {  if (finished_)  { - if (needs_urgent_) - { - SetQuirk(Quirk::URGENT, false); - needs_urgent_ = false; - } - BamfLauncherIcon::ActivateLauncherIcon(arg); + if (needs_urgent_) + { + SetQuirk(Quirk::URGENT, false); + needs_urgent_ = false; + } + + BamfLauncherIcon::ActivateLauncherIcon(arg);  }  else - SetQuirk(Quirk::STARTING, false); + { + SetQuirk(Quirk::STARTING, false); + }  }  void SoftwareCenterLauncherIcon::OnFinished(GVariant *params) @@ -135,7 +122,9 @@ void SoftwareCenterLauncherIcon::OnFinished(GVariant *params)  SetProgress(0.0f);  finished_ = true;  needs_urgent_ = true; - } else { + } + else + {  // failure condition, remove icon again  UnStick();  } @@ -167,7 +156,7 @@ void SoftwareCenterLauncherIcon::OnPropertyChanged(GVariant* params)  std::string SoftwareCenterLauncherIcon::GetName() const  { - return "SoftwareCenterLauncherIcon"; + return "SoftwareCenterLauncherIcon";  }  } diff --git a/launcher/SoftwareCenterLauncherIcon.h b/launcher/SoftwareCenterLauncherIcon.h index 70794437a..a25d5babb 100644 --- a/launcher/SoftwareCenterLauncherIcon.h +++ b/launcher/SoftwareCenterLauncherIcon.h @@ -42,7 +42,7 @@ public:  std::string const& aptdaemon_trans_id,  std::string const& icon_path); - void Animate(nux::ObjectPtr<Launcher> launcher, int icon_x, int icon_y, int icon_size); + void Animate(nux::ObjectPtr<Launcher> const& launcher, int start_x, int start_y);  std::string GetName() const; diff --git a/launcher/SpacerLauncherIcon.cpp b/launcher/SpacerLauncherIcon.cpp index db4803b7a..3839e4a77 100644 --- a/launcher/SpacerLauncherIcon.cpp +++ b/launcher/SpacerLauncherIcon.cpp @@ -28,7 +28,7 @@ namespace launcher  {  SpacerLauncherIcon::SpacerLauncherIcon(int monitor) - : SingleMonitorLauncherIcon(IconType::APPLICATION, monitor) + : SingleMonitorLauncherIcon(IconType::SPACER, monitor)  {  SetQuirk(Quirk::VISIBLE, true);  SetQuirk(Quirk::RUNNING, false); diff --git a/launcher/SpacerLauncherIcon.h b/launcher/SpacerLauncherIcon.h index 584d25253..f31303f0c 100644 --- a/launcher/SpacerLauncherIcon.h +++ b/launcher/SpacerLauncherIcon.h @@ -32,10 +32,6 @@ class SpacerLauncherIcon : public SingleMonitorLauncherIcon  public:  SpacerLauncherIcon(int monitor); - bool IsSpacer() - { - return true; - }  protected:  std::string GetName() const;  }; diff --git a/launcher/StandaloneLauncher.cpp b/launcher/StandaloneLauncher.cpp index 43eb597f0..cef9f0c9f 100644 --- a/launcher/StandaloneLauncher.cpp +++ b/launcher/StandaloneLauncher.cpp @@ -46,7 +46,7 @@ static launcher::Controller::Ptr controller;  void ThreadWidgetInit(nux::NThread* thread, void* InitData)  {  // launcherWindow->SetGeometry (nux::Geometry(0, 0, 300, 800)); - controller.reset(new launcher::Controller(0)); + controller.reset(new launcher::Controller());  }  int main(int argc, char** argv) diff --git a/launcher/TrashLauncherIcon.cpp b/launcher/TrashLauncherIcon.cpp index d9cd579a7..865d3edfe 100644 --- a/launcher/TrashLauncherIcon.cpp +++ b/launcher/TrashLauncherIcon.cpp @@ -46,6 +46,7 @@ TrashLauncherIcon::TrashLauncherIcon()  {  tooltip_text = _("Trash");  icon_name = "user-trash"; + position = Position::END;  SetQuirk(Quirk::VISIBLE, true);  SetQuirk(Quirk::RUNNING, false);  SetShortcut('t'); diff --git a/launcher/VolumeLauncherIcon.cpp b/launcher/VolumeLauncherIcon.cpp index bba622e9b..cbe9fcff2 100644 --- a/launcher/VolumeLauncherIcon.cpp +++ b/launcher/VolumeLauncherIcon.cpp @@ -26,6 +26,7 @@  #include "DevicesSettings.h"  #include "Volume.h"  #include "VolumeLauncherIcon.h" +#include "FavoriteStore.h"  namespace unity  { @@ -36,7 +37,7 @@ namespace  nux::logging::Logger logger("unity.launcher"); -const unsigned int volume_changed_timeout = 500; +const unsigned int volume_changed_timeout = 500;  } @@ -61,7 +62,7 @@ public:  }  ~Impl() - {  + {  volume_changed_conn_.disconnect();  volume_removed_conn_.disconnect();  settings_changed_conn_.disconnect(); @@ -83,7 +84,7 @@ public:  void UpdateKeepInLauncher()  { - auto identifier = volume_->GetIdentifier(); + auto const& identifier = volume_->GetIdentifier();  keep_in_launcher_ = !devices_settings_->IsABlacklistedDevice(identifier);  } @@ -104,6 +105,7 @@ public:  if (devices_settings_->IsABlacklistedDevice(volume_->GetIdentifier()))  devices_settings_->TryToUnblacklist(volume_->GetIdentifier()); + parent_->UnStick();  parent_->Remove();  } @@ -163,7 +165,8 @@ public:  dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true);  gsignals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, [this] (DbusmenuMenuitem*, int) { - auto identifier = volume_->GetIdentifier(); + auto const& identifier = volume_->GetIdentifier(); + parent_->UnStick();  devices_settings_->TryToBlacklist(identifier);  })); @@ -239,6 +242,16 @@ public:  menu.push_back(menu_item);  } + std::string GetRemoteUri() + { + auto const& identifier = volume_->GetIdentifier(); + + if (identifier.empty()) + return ""; + + return FavoriteStore::URI_PREFIX_DEVICE + identifier; + } +  VolumeLauncherIcon* parent_;  bool keep_in_launcher_;  Volume::Ptr volume_; @@ -293,6 +306,23 @@ AbstractLauncherIcon::MenuItemsVector VolumeLauncherIcon::GetMenus()  return pimpl_->GetMenus();  } +std::string VolumeLauncherIcon::GetRemoteUri() +{ + return pimpl_->GetRemoteUri(); +} + +void VolumeLauncherIcon::Stick(bool save) +{ + SimpleLauncherIcon::Stick(save); + pimpl_->devices_settings_->TryToUnblacklist(pimpl_->volume_->GetIdentifier()); +} + +void VolumeLauncherIcon::UnStick() +{ + SimpleLauncherIcon::UnStick(); + SetQuirk(Quirk::VISIBLE, true); +} +  //  // Introspection  // diff --git a/launcher/VolumeLauncherIcon.h b/launcher/VolumeLauncherIcon.h index 48e3e975f..a8bfb1ec1 100644 --- a/launcher/VolumeLauncherIcon.h +++ b/launcher/VolumeLauncherIcon.h @@ -43,7 +43,10 @@ public:  void EjectAndShowNotification(); // TODO: rename to private virtual void DoDropToTrash();  bool CanStop() const;  void StopDrive(); + void Stick(bool save = true); + void UnStick();  MenuItemsVector GetMenus(); + std::string GetRemoteUri();  protected:  virtual void ActivateLauncherIcon(ActionArg arg); diff --git a/manual-tests/Launcher.txt b/manual-tests/Launcher.txt index f31610116..fa5e5268b 100644 --- a/manual-tests/Launcher.txt +++ b/manual-tests/Launcher.txt @@ -191,6 +191,21 @@ Expected Result:  it, and the any pips for running apps show again. +Dragging fixed icons does not reorder an icon above or below +------------------------------------------------------------ +This test is about not reordering when dragging fixed icons. + +#. Move the mouse so it is over the BFB launcher icon +#. Press and hold the mouse button +#. Try to drag the icon to the right of the launcher. +#. Move the mouse down at least to the height of the second launcher icon + keeping the mouse pressed. +#. Release + +Outcome: + * No icon should be dragged. + +  Dragged launcher icons out of the launcher are properly drawn  -------------------------------------------------------------  This test ensures that the launcher icons out of the launcher are properly drawn diff --git a/panel/PanelMenuView.h b/panel/PanelMenuView.h index 6fc954a6c..be7e452c3 100644 --- a/panel/PanelMenuView.h +++ b/panel/PanelMenuView.h @@ -136,7 +136,6 @@ private:  glib::Object<BamfMatcher> _matcher;  nux::TextureLayer* _title_layer; - nux::HLayout* _menu_layout;  nux::ObjectPtr<WindowButtons> _window_buttons;  nux::ObjectPtr<PanelTitlebarGrabArea> _titlebar_grab_area;  nux::ObjectPtr<nux::BaseTexture> _title_texture; diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index 586040110..87e297d44 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -267,7 +267,6 @@ UnityScreen::UnityScreen(CompScreen* screen)  optionSetAutohideAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));  optionSetDashBlurExperimentalNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));  optionSetShortcutOverlayNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); - optionSetShowDesktopIconNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));  optionSetShowLauncherInitiate(boost::bind(&UnityScreen::showLauncherKeyInitiate, this, _1, _2, _3));  optionSetShowLauncherTerminate(boost::bind(&UnityScreen::showLauncherKeyTerminate, this, _1, _2, _3));  optionSetKeyboardFocusInitiate(boost::bind(&UnityScreen::setKeyboardFocusKeyInitiate, this, _1, _2, _3)); @@ -2845,9 +2844,6 @@ void UnityScreen::optionChanged(CompOption* opt, UnityshellOptions::Options num)  enable_shortcut_overlay_ = optionGetShortcutOverlay();  shortcut_controller_->SetEnabled(enable_shortcut_overlay_);  break; - case UnityshellOptions::ShowDesktopIcon: - launcher_controller_->SetShowDesktopIcon(optionGetShowDesktopIcon()); - break;  case UnityshellOptions::DecayRate:  launcher_options->edge_decay_rate = optionGetDecayRate() * 100;  break; @@ -2920,11 +2916,13 @@ bool UnityScreen::setOptionForPlugin(const char* plugin, const char* name,  CompOption::Value& v)  {  bool status = screen->setOptionForPlugin(plugin, name, v); +  if (status)  { - if (strcmp(plugin, "core") == 0 && strcmp(name, "hsize") == 0) + if (strcmp(plugin, "core") == 0)  { - launcher_controller_->UpdateNumWorkspaces(screen->vpSize().width() * screen->vpSize().height()); + if (strcmp(name, "hsize") == 0 || strcmp(name, "vsize") == 0) + launcher_controller_->UpdateNumWorkspaces(screen->vpSize().width() * screen->vpSize().height());  }  }  return status; @@ -2955,7 +2953,7 @@ void UnityScreen::OnDashRealized ()  void UnityScreen::initLauncher()  {  Timer timer; - launcher_controller_ = std::make_shared<launcher::Controller>(screen->dpy()); + launcher_controller_ = std::make_shared<launcher::Controller>();  AddChild(launcher_controller_.get());  switcher_controller_ = std::make_shared<switcher::Controller>(); diff --git a/plugins/unityshell/unityshell.xml.in b/plugins/unityshell/unityshell.xml.in index 98571394a..abaa07cba 100644 --- a/plugins/unityshell/unityshell.xml.in +++ b/plugins/unityshell/unityshell.xml.in @@ -400,12 +400,6 @@  <default>true</default>  </option> - <option name="show_desktop_icon" type="bool"> - <_short>Show "Desktop Icon" in the launcher</_short> - <_long>Enable/Disable "Show Desktop Icon" in the launcher.</_long> - <default>false</default> - </option> -  <option name="menus_fadein" type="int">  <_short>Menus Fade-in duration</_short>  <_long>Duration (in milliseconds) of the menus fade-in animation, used when the mouse goes over the top-panel.</_long> diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f74c52de8..3b868e16f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,16 +3,16 @@ set(UNITY_SRC ../plugins/unityshell/src)  #  # Data  # -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/ubuntuone-installer.desktop - ${CMAKE_BINARY_DIR}/tests/data/ubuntuone-installer.desktop) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/ubuntu-software-center.desktop - ${CMAKE_BINARY_DIR}/tests/data/ubuntu-software-center.desktop) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/update-manager.desktop - ${CMAKE_BINARY_DIR}/tests/data/update-manager.desktop) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/bzr-handle-patch.desktop - ${CMAKE_BINARY_DIR}/tests/data/bzr-handle-patch.desktop) -configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/no-icon.desktop - ${CMAKE_BINARY_DIR}/tests/data/no-icon.desktop) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/ubuntuone-installer.desktop + ${CMAKE_BINARY_DIR}/tests/data/applications/ubuntuone-installer.desktop) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/ubuntu-software-center.desktop + ${CMAKE_BINARY_DIR}/tests/data/applications/ubuntu-software-center.desktop) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/update-manager.desktop + ${CMAKE_BINARY_DIR}/tests/data/applications/update-manager.desktop) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/bzr-handle-patch.desktop + ${CMAKE_BINARY_DIR}/tests/data/applications/bzr-handle-patch.desktop) +configure_file (${CMAKE_CURRENT_SOURCE_DIR}/data/applications/no-icon.desktop + ${CMAKE_BINARY_DIR}/tests/data/applications/no-icon.desktop)  #  # Unit tests  # @@ -136,6 +136,7 @@ if (GTEST_SRC_DIR AND  test_indicator_entry.cpp  test_indicators.cpp  test_introspection.cpp + test_favorite_store.cpp  test_favorite_store_gsettings.cpp  test_favorite_store_private.cpp  test_home_lens.cpp @@ -200,20 +201,23 @@ if (GTEST_SRC_DIR AND  # Tests that require X  add_executable(test-gtest  test_main.cpp - test_bamflaunchericon.cpp + test_bamf_launcher_icon.cpp  test_bfb_launcher_icon.cpp  test_dashview_impl.cpp + test_desktop_launcher_icon.cpp  test_edge_barrier_controller.cpp  test_launcher.cpp  test_device_launcher_section.cpp  test_lensview_impl.cpp  test_hud_button.cpp  test_hud_controller.cpp + test_hud_launcher_icon.cpp  test_hud_view.cpp  test_icon_loader.cpp  test_im_text_entry.cpp  test_launcher_controller.cpp  test_launcher_drag_window.cpp + test_launcher_icon.cpp  test_keyboard_util.cpp  test_panel_style.cpp  test_previews_application.cpp @@ -226,10 +230,13 @@ if (GTEST_SRC_DIR AND  test_resultviewgrid.cpp  test_shortcut_controller.cpp  test_single_monitor_launcher_icon.cpp + test_software_center_launcher_icon.cpp + test_expo_launcher_icon.cpp  test_switcher_controller.cpp  test_switcher_model.cpp  test_texture_cache.cpp  test_thumbnail_generator.cpp + test_trash_launcher_icon.cpp  test_launcher_minimize_speed.cpp  test_unity_settings.cpp  test_volume_imp.cpp @@ -274,6 +281,7 @@ if (GTEST_SRC_DIR AND  ${CMAKE_SOURCE_DIR}/launcher/DevicesSettingsImp.cpp  ${CMAKE_SOURCE_DIR}/launcher/DndData.cpp  ${CMAKE_SOURCE_DIR}/launcher/EdgeBarrierController.cpp + ${CMAKE_SOURCE_DIR}/launcher/ExpoLauncherIcon.cpp  ${CMAKE_SOURCE_DIR}/launcher/FavoriteStore.cpp  ${CMAKE_SOURCE_DIR}/launcher/FavoriteStoreGSettings.cpp  ${CMAKE_SOURCE_DIR}/launcher/FavoriteStorePrivate.cpp diff --git a/tests/data/bzr-handle-patch.desktop b/tests/data/applications/bzr-handle-patch.desktop index 900e34d8d..900e34d8d 100644 --- a/tests/data/bzr-handle-patch.desktop +++ b/tests/data/applications/bzr-handle-patch.desktop diff --git a/tests/data/no-icon.desktop b/tests/data/applications/no-icon.desktop index 4f2902fe2..4f2902fe2 100644 --- a/tests/data/no-icon.desktop +++ b/tests/data/applications/no-icon.desktop diff --git a/tests/data/ubuntu-software-center.desktop b/tests/data/applications/ubuntu-software-center.desktop index e8f860e07..e8f860e07 100644 --- a/tests/data/ubuntu-software-center.desktop +++ b/tests/data/applications/ubuntu-software-center.desktop diff --git a/tests/data/ubuntuone-installer.desktop b/tests/data/applications/ubuntuone-installer.desktop index 99c6a47b7..99c6a47b7 100644 --- a/tests/data/ubuntuone-installer.desktop +++ b/tests/data/applications/ubuntuone-installer.desktop diff --git a/tests/data/update-manager.desktop b/tests/data/applications/update-manager.desktop index 5f9f3a64f..5f9f3a64f 100644 --- a/tests/data/update-manager.desktop +++ b/tests/data/applications/update-manager.desktop diff --git a/tests/gmockvolume.c b/tests/gmockvolume.c index be667f2b8..a46aee298 100644 --- a/tests/gmockvolume.c +++ b/tests/gmockvolume.c @@ -83,9 +83,10 @@ g_mock_volume_class_init (GMockVolumeClass *klass)  static void  g_mock_volume_init (GMockVolume *mock_volume)  { - mock_volume->name = g_strdup(""); + guint32 uuid = g_random_int(); + mock_volume->name = g_strdup_printf("MockVolume %u", uuid);  mock_volume->icon = g_icon_new_for_string("", NULL); - mock_volume->uuid = g_strdup(""); + mock_volume->uuid = g_strdup_printf("%u", uuid);  mock_volume->mount = NULL;  } diff --git a/tests/test_bamflaunchericon.cpp b/tests/test_bamf_launcher_icon.cpp index 874407b75..b33d20620 100644 --- a/tests/test_bamflaunchericon.cpp +++ b/tests/test_bamf_launcher_icon.cpp @@ -23,16 +23,19 @@  #include <gmock/gmock.h>  #include <UnityCore/GLibWrapper.h> +#include <UnityCore/DesktopUtilities.h>  #include "BamfLauncherIcon.h" +#include "FavoriteStore.h"  using namespace unity; +using namespace unity::launcher;  namespace  { -const std::string USC_DESKTOP = BUILDDIR"/tests/data/ubuntu-software-center.desktop"; -const std::string NO_ICON_DESKTOP = BUILDDIR"/tests/data/no-icon.desktop"; +const std::string USC_DESKTOP = BUILDDIR"/tests/data/applications/ubuntu-software-center.desktop"; +const std::string NO_ICON_DESKTOP = BUILDDIR"/tests/data/applications/no-icon.desktop";  class TestBamfLauncherIcon : public testing::Test  { @@ -61,6 +64,11 @@ public:  nux::ObjectPtr<launcher::BamfLauncherIcon> empty_app;  }; +TEST_F(TestBamfLauncherIcon, Position) +{ + EXPECT_EQ(usc_icon->position(), AbstractLauncherIcon::Position::FLOATING); +} +  TEST_F(TestBamfLauncherIcon, TestCustomBackgroundColor)  {  nux::Color const& color = usc_icon->BackgroundColor(); @@ -78,4 +86,64 @@ TEST_F(TestBamfLauncherIcon, TestDefaultIcon)  EXPECT_EQ(empty_app->icon_name.Get(), "application-default-icon");  } +TEST_F(TestBamfLauncherIcon, Stick) +{ + BamfView* bamf_app = BAMF_VIEW(bamf_matcher_get_application_for_desktop_file(bamf_matcher, USC_DESKTOP.c_str(), FALSE)); + ASSERT_FALSE(bamf_view_is_sticky(bamf_app)); + + bool saved = false; + usc_icon->position_saved.connect([&saved] {saved = true;}); + + usc_icon->Stick(false); + EXPECT_TRUE(bamf_view_is_sticky(bamf_app)); + EXPECT_TRUE(usc_icon->IsSticky()); + EXPECT_TRUE(usc_icon->IsVisible()); + EXPECT_FALSE(saved); + + usc_icon->Stick(true); + EXPECT_FALSE(saved); + bamf_view_set_sticky(bamf_app, FALSE); +} + +TEST_F(TestBamfLauncherIcon, StickAndSave) +{ + BamfView* bamf_app = BAMF_VIEW(bamf_matcher_get_application_for_desktop_file(bamf_matcher, USC_DESKTOP.c_str(), FALSE)); + ASSERT_FALSE(bamf_view_is_sticky(bamf_app)); + + bool saved = false; + usc_icon->position_saved.connect([&saved] {saved = true;}); + + usc_icon->Stick(true); + EXPECT_TRUE(bamf_view_is_sticky(bamf_app)); + EXPECT_TRUE(usc_icon->IsSticky()); + EXPECT_TRUE(usc_icon->IsVisible()); + EXPECT_TRUE(saved); + bamf_view_set_sticky(bamf_app, FALSE); +} + +TEST_F(TestBamfLauncherIcon, Unstick) +{ + BamfView* bamf_app = BAMF_VIEW(bamf_matcher_get_application_for_desktop_file(bamf_matcher, USC_DESKTOP.c_str(), FALSE)); + ASSERT_FALSE(bamf_view_is_sticky(bamf_app)); + + bool forgot = false; + usc_icon->position_forgot.connect([&forgot] {forgot = true;}); + + usc_icon->Stick(false); + ASSERT_TRUE(bamf_view_is_sticky(bamf_app)); + ASSERT_TRUE(usc_icon->IsSticky()); + + usc_icon->UnStick(); + EXPECT_FALSE(bamf_view_is_sticky(bamf_app)); + EXPECT_FALSE(usc_icon->IsSticky()); + EXPECT_FALSE(usc_icon->IsVisible()); + EXPECT_TRUE(forgot); +} + +TEST_F(TestBamfLauncherIcon, RemoteUri) +{ + EXPECT_EQ(usc_icon->RemoteUri(), FavoriteStore::URI_PREFIX_APP + DesktopUtilities::GetDesktopID(USC_DESKTOP)); + EXPECT_TRUE(empty_app->RemoteUri().empty()); +} +  } diff --git a/tests/test_bfb_launcher_icon.cpp b/tests/test_bfb_launcher_icon.cpp index bcc6c5f54..e4e0cb3f7 100644 --- a/tests/test_bfb_launcher_icon.cpp +++ b/tests/test_bfb_launcher_icon.cpp @@ -33,18 +33,21 @@ public:  MockBFBLauncherIcon()  : BFBLauncherIcon(LauncherHideMode::LAUNCHER_HIDE_NEVER)  {} - - AbstractLauncherIcon::MenuItemsVector GetMenus() - { - return BFBLauncherIcon::GetMenus(); - }  }; -TEST(TestBFBLauncherIcon, OverlayMenus) +struct TestBFBLauncherIcon : testing::Test  {  MockBFBLauncherIcon bfb; +}; - for (auto menu_item : bfb.GetMenus()) +TEST_F(TestBFBLauncherIcon, Position) +{ + EXPECT_EQ(bfb.position, AbstractLauncherIcon::Position::BEGIN); +} + +TEST_F(TestBFBLauncherIcon, OverlayMenus) +{ + for (auto menu_item : bfb.Menus())  {  bool overlay_item = dbusmenu_menuitem_property_get_bool(menu_item, QuicklistMenuItem::OVERLAY_MENU_ITEM_PROPERTY);  ASSERT_TRUE(overlay_item); diff --git a/tests/test_desktop_launcher_icon.cpp b/tests/test_desktop_launcher_icon.cpp new file mode 100644 index 000000000..2a4828e11 --- /dev/null +++ b/tests/test_desktop_launcher_icon.cpp @@ -0,0 +1,80 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> + +#include "PluginAdapter.h" +#include "DesktopLauncherIcon.h" + +using namespace unity; +using namespace unity::launcher; + +namespace +{ + +struct TestDesktopLauncherIcon : testing::Test +{ + DesktopLauncherIcon icon; +}; + +TEST_F(TestDesktopLauncherIcon, Type) +{ + EXPECT_EQ(icon.GetIconType(), AbstractLauncherIcon::IconType::DESKTOP); +} + +TEST_F(TestDesktopLauncherIcon, Shortcut) +{ + EXPECT_EQ(icon.GetShortcut(), 'd'); +} + +TEST_F(TestDesktopLauncherIcon, Position) +{ + EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); +} + +TEST_F(TestDesktopLauncherIcon, ActivateToggleShowDesktop) +{ + auto plugin_adapter = PluginAdapter::Default(); + + ASSERT_FALSE(plugin_adapter->InShowDesktop()); + + icon.Activate(ActionArg()); + ASSERT_TRUE(plugin_adapter->InShowDesktop()); + + icon.Activate(ActionArg()); + EXPECT_FALSE(plugin_adapter->InShowDesktop()); +} + +TEST_F(TestDesktopLauncherIcon, ShowInSwitcher) +{ + EXPECT_TRUE(icon.ShowInSwitcher(false)); + EXPECT_TRUE(icon.ShowInSwitcher(true)); + + icon.SetShowInSwitcher(false); + + EXPECT_FALSE(icon.ShowInSwitcher(false)); + EXPECT_FALSE(icon.ShowInSwitcher(true)); +} + +TEST_F(TestDesktopLauncherIcon, RemoteUri) +{ + EXPECT_EQ(icon.RemoteUri(), "unity://desktop-icon"); +} + +} diff --git a/tests/test_desktop_utilities.cpp b/tests/test_desktop_utilities.cpp index dd362a3ee..4cf258767 100644 --- a/tests/test_desktop_utilities.cpp +++ b/tests/test_desktop_utilities.cpp @@ -30,7 +30,9 @@ using namespace unity;  using testing::Eq;  namespace { -  + +const std::string LOCAL_DATA_DIR = BUILDDIR"/tests/data"; +  TEST(TestDesktopUtilitiesDesktopID, TestEmptyValues)  {  std::vector<std::string> empty_list; @@ -88,71 +90,99 @@ TEST(TestDesktopUtilitiesDesktopID, TestSubdirectory)  Eq("subdir-to.desktop"));  EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/applications/subdir1/subdir2/to.desktop"),  Eq("subdir1-subdir2-to.desktop")); + EXPECT_THAT(DesktopUtilities::GetDesktopID(dirs, "/this/path/applications/subdir1/subdir2-to.desktop"), + Eq("subdir1-subdir2-to.desktop"));  }  TEST(TestDesktopUtilitiesDataDirectories, TestGetUserDataDirectory)  { - const gchar* old_home = g_getenv("XDG_DATA_HOME"); + const gchar* env = g_getenv("XDG_DATA_HOME"); + std::string old_home = env ? env : ""; +  g_setenv("XDG_DATA_HOME", "UnityUserConfig", TRUE);  std::string const& user_data_dir = DesktopUtilities::GetUserDataDirectory(); - g_setenv("XDG_DATA_HOME", old_home, TRUE); + g_setenv("XDG_DATA_HOME", old_home.c_str(), TRUE);  EXPECT_THAT(user_data_dir, Eq("UnityUserConfig"));  }  TEST(TestDesktopUtilitiesDataDirectories, TestGetSystemDataDirectory)  { - const gchar* old_dirs = g_getenv("XDG_DATA_DIRS"); - g_setenv("XDG_DATA_DIRS", "dir1:dir2::dir3:dir4", TRUE); + const gchar* env = g_getenv("XDG_DATA_DIRS"); + std::string old_dirs = env ? env : ""; + g_setenv("XDG_DATA_DIRS", ("dir1:dir2::dir3:dir4:"+LOCAL_DATA_DIR).c_str(), TRUE);  std::vector<std::string> const& system_dirs = DesktopUtilities::GetSystemDataDirectories(); - g_setenv("XDG_DATA_DIRS", old_dirs, TRUE); + g_setenv("XDG_DATA_DIRS", old_dirs.c_str(), TRUE); - ASSERT_THAT(system_dirs.size(), Eq(4)); + ASSERT_THAT(system_dirs.size(), Eq(5));  EXPECT_THAT(system_dirs[0], Eq("dir1"));  EXPECT_THAT(system_dirs[1], Eq("dir2"));  EXPECT_THAT(system_dirs[2], Eq("dir3"));  EXPECT_THAT(system_dirs[3], Eq("dir4")); + EXPECT_THAT(system_dirs[4], Eq(LOCAL_DATA_DIR));  }  TEST(TestDesktopUtilitiesDataDirectories, TestGetDataDirectory)  { - const gchar* old_dirs = g_getenv("XDG_DATA_DIRS"); - g_setenv("XDG_DATA_DIRS", "dir1:dir2::dir3:dir4", TRUE); - const gchar* old_home = g_getenv("XDG_DATA_HOME"); + const gchar* env = g_getenv("XDG_DATA_DIRS"); + std::string old_dirs = env ? env : ""; + env = g_getenv("XDG_DATA_HOME"); + std::string old_home = env ? env : ""; + + g_setenv("XDG_DATA_DIRS", ("dir1:dir2::dir3:dir4:"+LOCAL_DATA_DIR).c_str(), TRUE);  g_setenv("XDG_DATA_HOME", "UnityUserConfig", TRUE);  std::vector<std::string> const& data_dirs = DesktopUtilities::GetDataDirectories(); - g_setenv("XDG_DATA_DIRS", old_dirs, TRUE); - g_setenv("XDG_DATA_HOME", old_home, TRUE); + g_setenv("XDG_DATA_DIRS", old_dirs.c_str(), TRUE); + g_setenv("XDG_DATA_HOME", old_home.c_str(), TRUE); - ASSERT_THAT(data_dirs.size(), Eq(5)); + ASSERT_THAT(data_dirs.size(), Eq(6));  EXPECT_THAT(data_dirs[0], Eq("dir1"));  EXPECT_THAT(data_dirs[1], Eq("dir2"));  EXPECT_THAT(data_dirs[2], Eq("dir3"));  EXPECT_THAT(data_dirs[3], Eq("dir4")); - EXPECT_THAT(data_dirs[4], Eq("UnityUserConfig")); + EXPECT_THAT(data_dirs[4], Eq(LOCAL_DATA_DIR)); + EXPECT_THAT(data_dirs[5], Eq("UnityUserConfig")); +} + +TEST(TestDesktopUtilities, TestGetDesktopPathById) +{ + const gchar* env = g_getenv("XDG_DATA_DIRS"); + std::string old_dirs = env ? env : ""; + env = g_getenv("XDG_DATA_HOME"); + std::string old_home = env ? env : ""; + + g_setenv("XDG_DATA_DIRS", LOCAL_DATA_DIR.c_str(), TRUE); + g_setenv("XDG_DATA_HOME", "UnityUserConfig", TRUE); + + std::string const& file = DesktopUtilities::GetDesktopPathById("ubuntu-software-center.desktop"); + + g_setenv("XDG_DATA_DIRS", old_dirs.c_str(), TRUE); + g_setenv("XDG_DATA_HOME", old_dirs.c_str(), TRUE); + + EXPECT_EQ(file, LOCAL_DATA_DIR + "/applications/ubuntu-software-center.desktop");  } -TEST(TestDesktopUtilitiesDataDirectories, TestGetBackgroundColor) +TEST(TestDesktopUtilities, TestGetBackgroundColor)  { - std::string const& color = DesktopUtilities::GetBackgroundColor(BUILDDIR"/tests/data/ubuntu-software-center.desktop"); + std::string const& color = DesktopUtilities::GetBackgroundColor(LOCAL_DATA_DIR+"/applications/ubuntu-software-center.desktop");  EXPECT_EQ(color, "#aabbcc");  } -TEST(TestDesktopUtilitiesDataDirectories, TestGetBackgroundColorNoKey) +TEST(TestDesktopUtilities, TestGetBackgroundColorNoKey)  { - std::string const& color = DesktopUtilities::GetBackgroundColor(BUILDDIR"/tests/data/update-manager.desktop"); + std::string const& color = DesktopUtilities::GetBackgroundColor(LOCAL_DATA_DIR+"/applications/update-manager.desktop");  EXPECT_TRUE(color.empty());  } -TEST(TestDesktopUtilitiesDataDirectories, TestGetBackgroundColorNoFile) +TEST(TestDesktopUtilities, TestGetBackgroundColorNoFile)  {  std::string const& color = DesktopUtilities::GetBackgroundColor("hello-world.desktop"); diff --git a/tests/test_device_launcher_section.cpp b/tests/test_device_launcher_section.cpp index 055a1156e..e436fa72d 100644 --- a/tests/test_device_launcher_section.cpp +++ b/tests/test_device_launcher_section.cpp @@ -21,17 +21,16 @@  */  #include <gmock/gmock.h> -using namespace testing; -#include "DeviceLauncherSection.h"  #include "DevicesSettings.h" -#include "AbstractVolumeMonitorWrapper.h" -using namespace unity; -using namespace unity::launcher; - -#include "gmockvolume.h" +#include "test_mock_devices.h"  #include "test_utils.h" +using namespace testing; +using namespace unity::launcher; + +namespace unity +{  namespace  { @@ -50,72 +49,38 @@ public:  bool icon_added;  }; -class MockVolumeMonitorWrapper : public AbstractVolumeMonitorWrapper -{ -public: - typedef std::shared_ptr<MockVolumeMonitorWrapper> Ptr; - - MockVolumeMonitorWrapper() - : volume1(G_VOLUME(g_mock_volume_new())) - , volume2(G_VOLUME(g_mock_volume_new())) - { - } - - VolumeList GetVolumes() - { - VolumeList ret; - - ret.push_back(volume1); - ret.push_back(volume2); - - return ret; - } - - glib::Object<GVolume> volume1; - glib::Object<GVolume> volume2; -}; - -class MockDevicesSettings : public DevicesSettings +struct TestDeviceLauncherSection : Test  { - MOCK_CONST_METHOD1(IsABlacklistedDevice, bool(std::string const& uuid)); - MOCK_METHOD1(TryToBlacklist, void(std::string const& uuid)); - MOCK_METHOD1(TryToUnblacklist, void(std::string const& uuid)); -}; - -class TestDeviceLauncherSection : public Test -{ -public:  TestDeviceLauncherSection()  : monitor_(new MockVolumeMonitorWrapper)  , devices_settings_(new MockDevicesSettings)  , section_(monitor_, devices_settings_)  {} - void SetUp() - { - // Make sure PopulateEntries is called. - Utils::WaitForTimeoutMSec(1500); - } -  MockVolumeMonitorWrapper::Ptr monitor_;  DevicesSettings::Ptr devices_settings_;  DeviceLauncherSection section_;  }; - -TEST_F(TestDeviceLauncherSection, TestNoDuplicates) +TEST_F(TestDeviceLauncherSection, NoDuplicates)  {  std::shared_ptr<EventListener> listener(new EventListener); - section_.IconAdded.connect(sigc::mem_fun(*listener, &EventListener::OnIconAdded)); + section_.icon_added.connect(sigc::mem_fun(*listener, &EventListener::OnIconAdded));  // Emit the signal volume_added for each volume. - monitor_->volume_added.emit(monitor_->volume1); - monitor_->volume_added.emit(monitor_->volume2); + monitor_->volume_added.emit(*(std::next(monitor_->volumes_.begin(), 0))); + monitor_->volume_added.emit(*(std::next(monitor_->volumes_.begin(), 1)));  Utils::WaitForTimeoutMSec(500);  EXPECT_EQ(listener->icon_added, false);  } +TEST_F(TestDeviceLauncherSection, GetIcons) +{ + EXPECT_EQ(section_.GetIcons().size(), 2); +} + +}  } diff --git a/tests/test_expo_launcher_icon.cpp b/tests/test_expo_launcher_icon.cpp new file mode 100644 index 000000000..e1550fc43 --- /dev/null +++ b/tests/test_expo_launcher_icon.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> + +#include "ExpoLauncherIcon.h" +#include "PluginAdapter.h" + +using namespace unity; +using namespace unity::launcher; + +namespace +{ +struct TestExpoLauncherIcon : testing::Test +{ + ExpoLauncherIcon icon; +}; + +TEST_F(TestExpoLauncherIcon, ActivateToggleExpo) +{ + auto plugin_adapter = PluginAdapter::Default(); + + ASSERT_FALSE(plugin_adapter->IsExpoActive()); + + icon.Activate(ActionArg()); + ASSERT_TRUE(plugin_adapter->IsExpoActive()); + + icon.Activate(ActionArg()); + EXPECT_FALSE(plugin_adapter->IsExpoActive()); +} + +TEST_F(TestExpoLauncherIcon, Position) +{ + EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); +} + +TEST_F(TestExpoLauncherIcon, RemoteUri) +{ + EXPECT_EQ(icon.RemoteUri(), "unity://expo-icon"); +} + +} diff --git a/tests/test_favorite_store.cpp b/tests/test_favorite_store.cpp new file mode 100644 index 000000000..3fd0d1bd3 --- /dev/null +++ b/tests/test_favorite_store.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> +#include <config.h> + +#include "FavoriteStore.h" + +using namespace unity; + +namespace +{ +class MockFavoriteStore : public FavoriteStore +{ +public: + FavoriteList const& GetFavorites() const { return fav_list_; } + void AddFavorite(std::string const& icon_uri, int position) {} + void RemoveFavorite(std::string const& icon_uri) {} + void MoveFavorite(std::string const& icon_uri, int position) {} + bool IsFavorite(std::string const& icon_uri) const { return false; } + int FavoritePosition(std::string const& icon_uri) const { return -1; } + void SetFavorites(FavoriteList const& icon_uris) {} + + std::string ParseFavoriteFromUri(std::string const& uri) const + { + return FavoriteStore::ParseFavoriteFromUri(uri); + } + +private: + FavoriteList fav_list_; +}; + +struct TestFavoriteStore : public testing::Test +{ + MockFavoriteStore favorite_store; +}; + +TEST_F(TestFavoriteStore, Construction) +{ + FavoriteStore& instance = FavoriteStore::Instance(); + EXPECT_EQ(&instance, &favorite_store); +} + +TEST_F(TestFavoriteStore, UriPrefixes) +{ + EXPECT_EQ(FavoriteStore::URI_PREFIX_APP, "application://"); + EXPECT_EQ(FavoriteStore::URI_PREFIX_FILE, "file://"); + EXPECT_EQ(FavoriteStore::URI_PREFIX_DEVICE, "device://"); + EXPECT_EQ(FavoriteStore::URI_PREFIX_UNITY, "unity://"); +} + +TEST_F(TestFavoriteStore, IsValidFavoriteUri) +{ + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("invalid-favorite")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("/path/to/desktop_file")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("desktop_file")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("file:///path/to/desktop_file")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("application://desktop_file")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("application://")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("device://")); + EXPECT_FALSE(FavoriteStore::IsValidFavoriteUri("unity://")); + + EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("device://uuid")); + EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("file:///path/to/desktop_file.desktop")); + EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("application://desktop_file.desktop")); + EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("device://a")); + EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("unity://b")); + EXPECT_TRUE(FavoriteStore::IsValidFavoriteUri("application://c.desktop")); +} + +TEST_F(TestFavoriteStore, ParseFavoriteFromUri) +{ + const std::string VALID_DESKTOP_PATH = BUILDDIR"/tests/data/applications/ubuntu-software-center.desktop"; + EXPECT_EQ(favorite_store.ParseFavoriteFromUri("file.desktop"), "application://file.desktop"); + EXPECT_EQ(favorite_store.ParseFavoriteFromUri(VALID_DESKTOP_PATH), "application://"+VALID_DESKTOP_PATH); + + EXPECT_EQ(favorite_store.ParseFavoriteFromUri("application://file.desktop"), "application://file.desktop"); + EXPECT_EQ(favorite_store.ParseFavoriteFromUri("application://"+VALID_DESKTOP_PATH), "application://"+VALID_DESKTOP_PATH); + EXPECT_EQ(favorite_store.ParseFavoriteFromUri("file://"+VALID_DESKTOP_PATH), "file://"+VALID_DESKTOP_PATH); + + EXPECT_EQ(favorite_store.ParseFavoriteFromUri("unity://uri"), "unity://uri"); + EXPECT_EQ(favorite_store.ParseFavoriteFromUri("device://uuid"), "device://uuid"); + + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("file").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("/path/to/file").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("/path/to/file.desktop").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("application:///path/to/file.desktop").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("file:///path/to/file.desktop").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("file://file.desktop").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("unity://").empty()); + EXPECT_TRUE(favorite_store.ParseFavoriteFromUri("device://").empty()); +} + +} diff --git a/tests/test_favorite_store_gsettings.cpp b/tests/test_favorite_store_gsettings.cpp index 245fcd001..3ed90f709 100644 --- a/tests/test_favorite_store_gsettings.cpp +++ b/tests/test_favorite_store_gsettings.cpp @@ -21,18 +21,12 @@  #include <config.h> -#include <algorithm> -#include <memory> -#include <string> - -#define G_SETTINGS_ENABLE_BACKEND -#include <gio/gsettingsbackend.h>  #include <gmock/gmock.h>  #include <glib.h>  #include "FavoriteStore.h"  #include "FavoriteStoreGSettings.h" -#include "FavoriteStorePrivate.h" +  #include <UnityCore/GLibWrapper.h>  using namespace unity; @@ -45,14 +39,16 @@ const gchar* SETTINGS_NAME = "com.canonical.Unity.Launcher";  const gchar* SETTINGS_KEY = "favorites";  const gchar* SCHEMA_DIRECTORY = BUILDDIR"/settings"; -const char* base_store_favs[] = { BUILDDIR"/tests/data/ubuntuone-installer.desktop", - BUILDDIR"/tests/data/ubuntu-software-center.desktop", - BUILDDIR"/tests/data/update-manager.desktop", +const char* base_store_favs[] = { BUILDDIR"/tests/data/applications/ubuntuone-installer.desktop", + "file://" BUILDDIR "/tests/data/applications/ubuntu-software-center.desktop", + "application://" BUILDDIR "/tests/data/applications/update-manager.desktop", + "unity://test-icon", + "device://uuid",  NULL  };  const int n_base_store_favs = G_N_ELEMENTS(base_store_favs) - 1; /* NULL */ -const std::string other_desktop = BUILDDIR"/tests/data/bzr-handle-patch.desktop"; +const std::string other_desktop = "application://" BUILDDIR "/tests/data/applications/bzr-handle-patch.desktop";  // Utilities  std::string const& at(FavoriteList const& favs, int index) @@ -110,9 +106,9 @@ TEST_F(TestFavoriteStoreGSettings, TestGetFavorites)  FavoriteList const& favs = settings.GetFavorites();  ASSERT_EQ(favs.size(), n_base_store_favs); - EXPECT_TRUE(ends_with(at(favs, 0), base_store_favs[0])); + EXPECT_TRUE(ends_with(at(favs, 0), FavoriteStore::URI_PREFIX_APP+base_store_favs[0]));  EXPECT_TRUE(ends_with(at(favs, 1), base_store_favs[1])); - EXPECT_TRUE(at(favs, 2) == base_store_favs[2]); + EXPECT_EQ(at(favs, 2), base_store_favs[2]);  }  TEST_F(TestFavoriteStoreGSettings, TestAddFavorite) @@ -122,7 +118,7 @@ TEST_F(TestFavoriteStoreGSettings, TestAddFavorite)  settings.AddFavorite(other_desktop, 0);  FavoriteList const& favs = settings.GetFavorites();  ASSERT_EQ(favs.size(), n_base_store_favs + 1); - EXPECT_TRUE(other_desktop == at(favs, 0)); + EXPECT_EQ(other_desktop, at(favs, 0));  }  TEST_F(TestFavoriteStoreGSettings, TestAddFavoritePosition) @@ -132,7 +128,7 @@ TEST_F(TestFavoriteStoreGSettings, TestAddFavoritePosition)  settings.AddFavorite(other_desktop, 2);  FavoriteList const& favs = settings.GetFavorites();  ASSERT_EQ(favs.size(), n_base_store_favs + 1); - EXPECT_TRUE(other_desktop == at(favs, 2)); + EXPECT_EQ(other_desktop, at(favs, 2));  }  TEST_F(TestFavoriteStoreGSettings,TestAddFavoriteLast) @@ -142,7 +138,7 @@ TEST_F(TestFavoriteStoreGSettings,TestAddFavoriteLast)  settings.AddFavorite(other_desktop, -1);  FavoriteList const& favs = settings.GetFavorites();  ASSERT_EQ(favs.size(), n_base_store_favs + 1); - EXPECT_TRUE(other_desktop == favs.back()); + EXPECT_EQ(other_desktop, favs.back());  }  TEST_F(TestFavoriteStoreGSettings,TestAddFavoriteOutOfRange) @@ -178,13 +174,13 @@ TEST_F(TestFavoriteStoreGSettings, TestRemoveFavoriteBad)  FavoriteStore &settings = FavoriteStore::Instance();  FavoriteList const& favs = settings.GetFavorites(); - settings.RemoveFavorite(""); + settings.RemoveFavorite(" ");  EXPECT_EQ(favs.size(), n_base_store_favs); - settings.RemoveFavorite("foo.desktop"); + settings.RemoveFavorite("application://foo.desktop");  EXPECT_EQ(favs.size(), n_base_store_favs); - settings.RemoveFavorite("/this/desktop/doesnt/exist/hopefully.desktop"); + settings.RemoveFavorite("application:///this/desktop/doesnt/exist/hopefully.desktop");  EXPECT_EQ(favs.size(), n_base_store_favs);  } @@ -198,7 +194,7 @@ TEST_F(TestFavoriteStoreGSettings, TestMoveFavorite)  ASSERT_EQ(favs.size(), n_base_store_favs);  EXPECT_EQ(at(favs, 0), base_store_favs[2]); - EXPECT_TRUE(ends_with(at(favs, 1), base_store_favs[0])); + EXPECT_TRUE(ends_with(at(favs, 1), FavoriteStore::URI_PREFIX_APP+base_store_favs[0]));  EXPECT_TRUE(ends_with(at(favs, 2), base_store_favs[1]));  } @@ -212,7 +208,7 @@ TEST_F(TestFavoriteStoreGSettings, TestMoveFavoriteBad)  settings.MoveFavorite(at(favs, 0), 100);  ASSERT_EQ(favs.size(), n_base_store_favs); - EXPECT_TRUE(ends_with(at(favs, 0), base_store_favs[0])); + EXPECT_TRUE(ends_with(at(favs, 0), FavoriteStore::URI_PREFIX_APP+base_store_favs[0]));  EXPECT_TRUE(ends_with(at(favs, 1), base_store_favs[1]));  EXPECT_EQ(at(favs, 2), base_store_favs[2]);  } @@ -239,7 +235,7 @@ TEST_F(TestFavoriteStoreGSettings, TestFavoriteAddedSignalFirst)  favorite_store->SaveFavorites(favs, false);  ASSERT_TRUE(signal_received); - EXPECT_EQ(position, base_store_favs[0]); + EXPECT_EQ(position, FavoriteStore::URI_PREFIX_APP+base_store_favs[0]);  EXPECT_TRUE(before);  } @@ -322,12 +318,12 @@ TEST_F(TestFavoriteStoreGSettings, TestFavoriteRemoved)  {  FavoriteStore &settings = FavoriteStore::Instance();  bool signal_received = false; - std::string path_removed; + std::vector<std::string> paths_removed;  settings.favorite_removed.connect([&](std::string const& path)  {  signal_received = true; - path_removed = path; + paths_removed.push_back(path);  });  FavoriteList favs; @@ -336,7 +332,10 @@ TEST_F(TestFavoriteStoreGSettings, TestFavoriteRemoved)  favorite_store->SaveFavorites(favs, false);  ASSERT_TRUE(signal_received); - EXPECT_EQ(path_removed, base_store_favs[1]); + ASSERT_EQ(paths_removed.size(), 3); + EXPECT_EQ(paths_removed[0], base_store_favs[4]); + EXPECT_EQ(paths_removed[1], base_store_favs[1]); + EXPECT_EQ(paths_removed[2], base_store_favs[3]);  }  TEST_F(TestFavoriteStoreGSettings, TestFavoriteReordered) @@ -464,4 +463,28 @@ TEST_F(TestFavoriteStoreGSettings, TestFavoriteSignalsMixed3)  EXPECT_TRUE(reordered_received);  } +TEST_F(TestFavoriteStoreGSettings, TestIsFavorite) +{ + EXPECT_TRUE(favorite_store->IsFavorite(FavoriteStore::URI_PREFIX_APP+base_store_favs[0])); + + for (int i = 1; i < n_base_store_favs; i++) + { + ASSERT_TRUE(favorite_store->IsFavorite(base_store_favs[i])); + } + + EXPECT_FALSE(favorite_store->IsFavorite("unity://invalid-favorite")); +} + +TEST_F(TestFavoriteStoreGSettings, TestFavoritePosition) +{ + EXPECT_EQ(favorite_store->FavoritePosition(FavoriteStore::URI_PREFIX_APP+base_store_favs[0]), 0); + + for (int i = 1; i < n_base_store_favs; i++) + { + ASSERT_EQ(favorite_store->FavoritePosition(base_store_favs[i]), i); + } + + EXPECT_EQ(favorite_store->FavoritePosition("unity://invalid-favorite"), -1); +} +  } // anonymous namespace diff --git a/tests/test_hud_launcher_icon.cpp b/tests/test_hud_launcher_icon.cpp new file mode 100644 index 000000000..20dbe30ee --- /dev/null +++ b/tests/test_hud_launcher_icon.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> + +#include "HudLauncherIcon.h" + +using namespace unity; +using namespace unity::launcher; + +namespace +{ + +class MockHudLauncherIcon : public HudLauncherIcon +{ +public: + MockHudLauncherIcon() + : HudLauncherIcon(LauncherHideMode::LAUNCHER_HIDE_NEVER) + {} +}; + +struct TestHudLauncherIcon : testing::Test +{ + MockHudLauncherIcon hud; +}; + +TEST_F(TestHudLauncherIcon, Type) +{ + EXPECT_EQ(hud.GetIconType(), AbstractLauncherIcon::IconType::HUD); +} + +TEST_F(TestHudLauncherIcon, Position) +{ + EXPECT_EQ(hud.position(), AbstractLauncherIcon::Position::BEGIN); +} + +} diff --git a/tests/test_launcher.cpp b/tests/test_launcher.cpp index ae8f7bb4e..2e83f4f17 100644 --- a/tests/test_launcher.cpp +++ b/tests/test_launcher.cpp @@ -48,8 +48,12 @@ class MockMockLauncherIcon : public launcher::MockLauncherIcon  {  public:  typedef nux::ObjectPtr<MockMockLauncherIcon> Ptr; + MockMockLauncherIcon(IconType type = IconType::APPLICATION) + : MockLauncherIcon(type) + {}  MOCK_METHOD1(ShouldHighlightOnDrag, bool(DndData const&)); + MOCK_METHOD1(Stick, void(bool));  };  } @@ -57,7 +61,7 @@ public:  class TestLauncher : public Test  {  public: - class MockLauncher : public launcher::Launcher + class MockLauncher : public Launcher  {  public:  MockLauncher(nux::BaseWindow* parent, nux::ObjectPtr<DNDCollectionWindow> const& collection_window) @@ -92,6 +96,11 @@ public:  Launcher::ShowDragWindow();  } + void EndIconDrag() + { + Launcher::EndIconDrag(); + } +  void UpdateDragWindowPosition(int x, int y)  {  Launcher::UpdateDragWindowPosition(x, y); @@ -106,6 +115,55 @@ public:  {  Launcher::ResetMouseDragState();  } + + bool DndIsSpecialRequest(std::string const& uri) const + { + return Launcher::DndIsSpecialRequest(uri); + } + + int GetDragIconPosition() const + { + return _drag_icon_position; + } + + void ProcessDndEnter() + { + Launcher::ProcessDndEnter(); + } + + void ProcessDndLeave() + { + Launcher::ProcessDndLeave(); + } + + void ProcessDndMove(int x, int y, std::list<char*> mimes) + { + Launcher::ProcessDndMove(x, y, mimes); + } + + void FakeProcessDndMove(int x, int y, std::list<std::string> uris) + { + _dnd_data.Reset(); + + std::string data_uri; + for (std::string const& uri : uris) + data_uri += uri+"\r\n"; + + _dnd_data.Fill(data_uri.c_str()); + + if (std::find_if(_dnd_data.Uris().begin(), _dnd_data.Uris().end(), [this] (std::string const& uri) + {return DndIsSpecialRequest(uri);}) != _dnd_data.Uris().end()) + { + _steal_drag = true; + } + + _dnd_hovered_icon = MouseIconIntersection(x, y); + } + + void ProcessDndDrop(int x, int y) + { + Launcher::ProcessDndDrop(x, y); + }  };  TestLauncher() @@ -119,6 +177,29 @@ public:  launcher_->SetModel(model_);  } + std::vector<MockMockLauncherIcon::Ptr> AddMockIcons(unsigned number) + { + std::vector<MockMockLauncherIcon::Ptr> icons; + int icon_size = launcher_->GetIconSize(); + int monitor = launcher_->monitor(); + auto const& launcher_geo = launcher_->GetGeometry(); + int model_pre_size = model_->Size(); + + for (unsigned i = 0; i < number; ++i) + { + MockMockLauncherIcon::Ptr icon(new MockMockLauncherIcon); + icon->SetCenter(nux::Point3(icon_size/2.0f, icon_size/2.0f * (i+1) + 1, 0), monitor, launcher_geo); + + icons.push_back(icon); + model_->AddIcon(icon); + } + + EXPECT_EQ(icons.size(), number); + EXPECT_EQ(model_pre_size + number, number); + + return icons; + } +  MockUScreen uscreen;  nux::BaseWindow* parent_window_;  nux::ObjectPtr<DNDCollectionWindow> dnd_collection_window_; @@ -206,19 +287,11 @@ TEST_F(TestLauncher, TestIconBackgroundIntensity)  TEST_F(TestLauncher, DragLauncherIconCancelRestoresIconOrder)  { - MockMockLauncherIcon::Ptr icon1(new MockMockLauncherIcon); - MockMockLauncherIcon::Ptr icon2(new MockMockLauncherIcon); - MockMockLauncherIcon::Ptr icon3(new MockMockLauncherIcon); + auto const& icons = AddMockIcons(3); - model_->AddIcon(icon1); - model_->AddIcon(icon2); - model_->AddIcon(icon3); - - // Set the icon centers - int icon_size = launcher_->GetIconSize(); - icon1->SetCenter(nux::Point3(icon_size/2.0f, icon_size/2.0f * 1 + 1, 0), launcher_->monitor(), launcher_->GetGeometry()); - icon2->SetCenter(nux::Point3(icon_size/2.0f, icon_size/2.0f * 2 + 1, 0), launcher_->monitor(), launcher_->GetGeometry()); - icon3->SetCenter(nux::Point3(icon_size/2.0f, icon_size/2.0f * 3 + 1, 0), launcher_->monitor(), launcher_->GetGeometry()); + auto const& icon1 = icons[0]; + auto const& icon2 = icons[1]; + auto const& icon3 = icons[2];  // Start dragging icon2  launcher_->StartIconDrag(icon2); @@ -257,8 +330,146 @@ TEST_F(TestLauncher, DragLauncherIconCancelRestoresIconOrder)  EXPECT_FALSE(model_saved);  launcher_->HideDragWindow(); + + // Let's wait the drag icon animation to be completed + Utils::WaitForTimeout(1); + EXPECT_EQ(launcher_->GetDraggedIcon(), nullptr); +} + +TEST_F(TestLauncher, DragLauncherIconSavesIconOrderIfPositionHasChanged) +{ + auto const& icons = AddMockIcons(3); + + auto const& icon1 = icons[0]; + auto const& icon2 = icons[1]; + auto const& icon3 = icons[2]; + + // Start dragging icon2 + launcher_->StartIconDrag(icon2); + launcher_->ShowDragWindow(); + ASSERT_EQ(launcher_->GetDragIconPosition(), model_->IconIndex(icon2)); + + // Moving icon2 at the end + auto const& center3 = icon3->GetCenter(launcher_->monitor()); + launcher_->UpdateDragWindowPosition(center3.x, center3.y); + + bool model_saved = false; + model_->saved.connect([&model_saved] { model_saved = true; }); + + ASSERT_NE(launcher_->GetDragIconPosition(), model_->IconIndex(icon2)); + launcher_->EndIconDrag(); + + // The icon order should be reset + auto it = model_->begin(); + ASSERT_EQ(*it, icon1); it++; + ASSERT_EQ(*it, icon3); it++; + ASSERT_EQ(*it, icon2); + + EXPECT_TRUE(model_saved); + + // Let's wait the drag icon animation to be completed + Utils::WaitForTimeout(1); + EXPECT_EQ(launcher_->GetDraggedIcon(), nullptr);  } +TEST_F(TestLauncher, DragLauncherIconSavesIconOrderIfPositionHasNotChanged) +{ + auto const& icons = AddMockIcons(3); + + auto const& icon1 = icons[0]; + auto const& icon2 = icons[1]; + auto const& icon3 = icons[2]; + + // Start dragging icon2 + launcher_->StartIconDrag(icon2); + launcher_->ShowDragWindow(); + ASSERT_EQ(launcher_->GetDragIconPosition(), model_->IconIndex(icon2)); + + // Moving icon2 at the end + auto center3 = icon3->GetCenter(launcher_->monitor()); + launcher_->UpdateDragWindowPosition(center3.x, center3.y); + + // Swapping the centers + icon3->SetCenter(icon2->GetCenter(launcher_->monitor()), launcher_->monitor(), launcher_->GetGeometry()); + icon2->SetCenter(center3, launcher_->monitor(), launcher_->GetGeometry()); + + // Moving icon2 back to the middle + center3 = icon3->GetCenter(launcher_->monitor()); + launcher_->UpdateDragWindowPosition(center3.x, center3.y); + + bool model_saved = false; + model_->saved.connect([&model_saved] { model_saved = true; }); + + ASSERT_EQ(launcher_->GetDragIconPosition(), model_->IconIndex(icon2)); + launcher_->EndIconDrag(); + + // The icon order should be reset + auto it = model_->begin(); + ASSERT_EQ(*it, icon1); it++; + ASSERT_EQ(*it, icon2); it++; + ASSERT_EQ(*it, icon3); + + EXPECT_FALSE(model_saved); + + // Let's wait the drag icon animation to be completed + Utils::WaitForTimeout(1); + EXPECT_EQ(launcher_->GetDraggedIcon(), nullptr); +} + +TEST_F(TestLauncher, DragLauncherIconSticksDeviceIcon) +{ + auto const& icons = AddMockIcons(1); + + MockMockLauncherIcon::Ptr device(new MockMockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); + model_->AddIcon(device); + + // Start dragging device icon + launcher_->StartIconDrag(device); + launcher_->ShowDragWindow(); + + // Moving device icon to the beginning + auto const& center = icons[0]->GetCenter(launcher_->monitor()); + launcher_->UpdateDragWindowPosition(center.x, center.y); + + EXPECT_CALL(*device, Stick(false)); + launcher_->EndIconDrag(); +} + +TEST_F(TestLauncher, DndIsSpecialRequest) +{ + EXPECT_TRUE(launcher_->DndIsSpecialRequest("MyFile.desktop")); + EXPECT_TRUE(launcher_->DndIsSpecialRequest("/full/path/to/MyFile.desktop")); + EXPECT_TRUE(launcher_->DndIsSpecialRequest("application://MyFile.desktop")); + EXPECT_TRUE(launcher_->DndIsSpecialRequest("file://MyFile.desktop")); + EXPECT_TRUE(launcher_->DndIsSpecialRequest("file://full/path/to/MyFile.desktop")); + EXPECT_TRUE(launcher_->DndIsSpecialRequest("device://uuuid")); + + EXPECT_FALSE(launcher_->DndIsSpecialRequest("MyFile.txt")); + EXPECT_FALSE(launcher_->DndIsSpecialRequest("/full/path/to/MyFile.txt")); + EXPECT_FALSE(launcher_->DndIsSpecialRequest("file://full/path/to/MyFile.txt")); +} + +TEST_F(TestLauncher, AddRequestSignal) +{ + auto const& icons = AddMockIcons(1); + auto const& center = icons[0]->GetCenter(launcher_->monitor()); + launcher_->ProcessDndEnter(); + launcher_->FakeProcessDndMove(center.x, center.y, {"application://MyFile.desktop"}); + + bool add_request = false; + launcher_->add_request.connect([&] (std::string const& uri, AbstractLauncherIcon::Ptr const& drop_icon) { + EXPECT_EQ(drop_icon, icons[0]); + EXPECT_EQ(uri, "application://MyFile.desktop"); + add_request = true; + }); + + launcher_->ProcessDndDrop(center.x, center.y); + launcher_->ProcessDndLeave(); + + EXPECT_TRUE(add_request); +} + +  }  } diff --git a/tests/test_launcher_controller.cpp b/tests/test_launcher_controller.cpp index a4d5fc660..79b036b81 100644 --- a/tests/test_launcher_controller.cpp +++ b/tests/test_launcher_controller.cpp @@ -23,8 +23,18 @@  #include "FavoriteStore.h"  #include "LauncherController.h"  #include "LauncherControllerPrivate.h" +#include "ExpoLauncherIcon.h" +#include "DesktopLauncherIcon.h" +#include "MockLauncherIcon.h" +#include "BFBLauncherIcon.h" +#include "HudLauncherIcon.h" +#include "TrashLauncherIcon.h" +#include "VolumeLauncherIcon.h" +#include "SoftwareCenterLauncherIcon.h"  #include "PanelStyle.h"  #include "UnitySettings.h" +#include "test_utils.h" +#include "test_mock_devices.h"  using namespace unity::launcher;  using namespace testing; @@ -32,63 +42,221 @@ using namespace testing;  namespace unity  { -class MockFavoriteStore : public FavoriteStore +namespace  { -public: - FavoriteList const& GetFavorites() +namespace places +{ +const std::string APPS_URI = "unity://running-apps"; +const std::string DEVICES_URI = "unity://devices"; +} + +namespace app +{ + const std::string UBUNTU_ONE = BUILDDIR "/tests/data/applications/ubuntuone-installer.desktop"; + const std::string SW_CENTER = BUILDDIR "/tests/data/applications/ubuntu-software-center.desktop"; + const std::string UPDATE_MANAGER = BUILDDIR "/tests/data/applications/update-manager.desktop"; + const std::string BZR_HANDLE_PATCH = BUILDDIR "/tests/data/applications/bzr-handle-patch.desktop"; + const std::string NO_ICON = BUILDDIR "/tests/data/applications/no-icon.desktop"; +} +} + +struct MockFavoriteStore : FavoriteStore +{ + MockFavoriteStore() + { + fav_list_ = { FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, + FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }; + } + + FavoriteList const& GetFavorites() const  {  return fav_list_; - }; + } + + void AddFavorite(std::string const& icon_uri, int position) + { + if (!IsValidFavoriteUri(icon_uri)) + return; + + if (position < 0) + fav_list_.push_back(icon_uri); + else + fav_list_.insert(std::next(fav_list_.begin(), position), icon_uri); + } + + void RemoveFavorite(std::string const& icon_uri) + { + fav_list_.remove(icon_uri); + } + + void MoveFavorite(std::string const& icon_uri, int position) + { + RemoveFavorite(icon_uri); + AddFavorite(icon_uri, position); + } + + bool IsFavorite(std::string const& icon_uri) const + { + return std::find(fav_list_.begin(), fav_list_.end(), icon_uri) != fav_list_.end(); + } + + int FavoritePosition(std::string const& icon_uri) const + { + auto it = std::find(fav_list_.begin(), fav_list_.end(), icon_uri); + + if (it != fav_list_.end()) + return std::distance(fav_list_.begin(), it); - void AddFavorite(std::string const& desktop_path, int position) {}; - void RemoveFavorite(std::string const& desktop_path) {}; - void MoveFavorite(std::string const& desktop_path, int position) {}; - void SetFavorites(FavoriteList const& desktop_paths) {}; + return -1; + } + + void SetFavorites(FavoriteList const& icon_uris) + { + fav_list_ = icon_uris; + } + + void ClearFavorites() + { + fav_list_.clear(); + }  private:  FavoriteList fav_list_;  }; -class MockBamfLauncherIcon : public BamfLauncherIcon +struct MockBamfLauncherIcon : public BamfLauncherIcon  { -public: - MockBamfLauncherIcon(BamfApplication* app) - : BamfLauncherIcon(app) {} + typedef nux::ObjectPtr<MockBamfLauncherIcon> Ptr; + typedef bool Fake; + MockBamfLauncherIcon(Fake = true, std::string const& remote_uri = "") + : BamfLauncherIcon(static_cast<BamfApplication*>(g_object_new(BAMF_TYPE_APPLICATION, nullptr))) + , remote_uri_(remote_uri) + { + InitMock(); + SetQuirk(Quirk::VISIBLE, true); + } + + explicit MockBamfLauncherIcon(BamfApplication* app) + : BamfLauncherIcon(app) + { + InitMock(); + } + + MockBamfLauncherIcon(std::string const& desktop_file) + : BamfLauncherIcon(bamf_matcher_get_application_for_desktop_file(bamf_matcher_get_default(), desktop_file.c_str(), TRUE)) + { + InitMock(); + } + + void InitMock() + { + ON_CALL(*this, Stick(_)).WillByDefault(Invoke(this, &MockBamfLauncherIcon::ReallyStick)); + } + + std::string GetRemoteUri() + { + if (remote_uri_.empty()) + return BamfLauncherIcon::GetRemoteUri(); + else + return FavoriteStore::URI_PREFIX_APP + remote_uri_; + } + + bool IsSticky() const + { + if (remote_uri_.empty()) + return BamfLauncherIcon::IsSticky(); + else + return SimpleLauncherIcon::IsSticky(); + } + + void ReallyStick(bool save) { BamfLauncherIcon::Stick(save); } + + MOCK_METHOD1(Stick, void(bool));  MOCK_METHOD0(UnStick, void());  MOCK_METHOD0(Quit, void()); + + std::string remote_uri_; +}; + +struct MockVolumeLauncherIcon : public VolumeLauncherIcon +{ + typedef nux::ObjectPtr<MockVolumeLauncherIcon> Ptr; + + MockVolumeLauncherIcon() + : VolumeLauncherIcon(Volume::Ptr(volume_ = new MockVolume()), + std::make_shared<MockDevicesSettings>()) + , uuid_(std::to_string(g_random_int())) + { + ON_CALL(*volume_, GetIdentifier()).WillByDefault(Return(uuid_)); + ON_CALL(*this, Stick(_)).WillByDefault(Invoke(this, &MockVolumeLauncherIcon::ReallyStick)); + } + + void ReallyStick(bool save) { VolumeLauncherIcon::Stick(save); } + + MOCK_METHOD1(Stick, void(bool)); + MOCK_METHOD0(UnStick, void()); + + MockVolume* volume_; + std::string uuid_;  };  namespace launcher  { -class TestLauncherController : public testing::Test +struct TestLauncherController : public testing::Test  { -public: - TestLauncherController() - : lc(nux::GetGraphicsDisplay()->GetX11Display()) - {} -  virtual void SetUp()  {  lc.multiple_launchers = true;  }  protected: - ui::EdgeBarrierController &GetBarrierController() + struct MockLauncherController : Controller  { - return lc.pimpl->edge_barriers_; - } + Controller::Impl* Impl() const { return pimpl.get(); } - LauncherModel::Ptr GetLauncherModel() - { - return lc.pimpl->model_; - } + AbstractLauncherIcon::Ptr GetIconByDesktop(std::string const& path) const + { + auto const& model = Impl()->model_; + auto icon = std::find_if(model->begin(), model->end(), + [&path](AbstractLauncherIcon::Ptr const& i) { return ( i->DesktopFile() == path); }); + + if (icon != model->end()) + return *icon; + + return AbstractLauncherIcon::Ptr(); + } + + void ClearModel() + { + auto const& model = Impl()->model_; + std::vector<AbstractLauncherIcon::Ptr> icons; + + for (auto const& icon : *model) + icons.push_back(icon); + + for (auto const& icon : icons) + model->RemoveIcon(icon); + + ASSERT_EQ(model->Size(), 0); + } + + void DisconnectSignals() + { + Impl()->view_opened_signal_.Disconnect(); + Impl()->device_section_.icon_added.clear(); + Impl()->model_->icon_removed.clear(); + Impl()->model_->saved.clear(); + Impl()->model_->order_changed.clear(); + } + };  MockUScreen uscreen;  Settings settings;  panel::Style panel_style;  MockFavoriteStore favorite_store; - Controller lc; + MockLauncherController lc;  };  } @@ -98,6 +266,29 @@ TEST_F(TestLauncherController, Construction)  EXPECT_TRUE(lc.multiple_launchers());  ASSERT_EQ(lc.launchers().size(), 1);  EXPECT_EQ(lc.launcher().monitor(), 0); + ASSERT_EQ(lc.Impl()->parent_, &lc); + ASSERT_TRUE(lc.Impl()->matcher_.IsType(BAMF_TYPE_MATCHER)); + ASSERT_NE(lc.Impl()->model_, nullptr); + EXPECT_EQ(lc.Impl()->expo_icon_->GetIconType(), AbstractLauncherIcon::IconType::EXPO); + EXPECT_EQ(lc.Impl()->desktop_icon_->GetIconType(), AbstractLauncherIcon::IconType::DESKTOP); + EXPECT_GE(lc.Impl()->sort_priority_, AbstractLauncherIcon::DefaultPriority(AbstractLauncherIcon::IconType::APPLICATION)); + EXPECT_EQ(lc.Impl()->model_->GetSublist<BFBLauncherIcon>().size(), 1); + EXPECT_EQ(lc.Impl()->model_->GetSublist<HudLauncherIcon>().size(), 1); + EXPECT_EQ(lc.Impl()->model_->GetSublist<TrashLauncherIcon>().size(), 1); + EXPECT_FALSE(lc.Impl()->launcher_open); + EXPECT_FALSE(lc.Impl()->launcher_keynav); + EXPECT_FALSE(lc.Impl()->launcher_grabbed); + EXPECT_FALSE(lc.Impl()->reactivate_keynav); + EXPECT_TRUE(lc.Impl()->keynav_restore_window_); + EXPECT_EQ(lc.Impl()->launcher_key_press_time_, 0); + + for (auto const& fav_uri : favorite_store.GetFavorites()) + { + auto const& model_icon_it = std::find_if(lc.Impl()->model_->begin(), lc.Impl()->model_->end(), + [&fav_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == fav_uri); }); + + ASSERT_TRUE((*model_icon_it).IsValid()); + }  }  TEST_F(TestLauncherController, MultimonitorMultipleLaunchers) @@ -188,7 +379,7 @@ TEST_F(TestLauncherController, MultiMonitorEdgeBarrierSubscriptions)  uscreen.SetupFakeMultiMonitor();  for (int i = 0; i < max_num_monitors; ++i) - ASSERT_EQ(GetBarrierController().GetSubscriber(i), lc.launchers()[i].GetPointer()); + ASSERT_EQ(lc.Impl()->edge_barriers_.GetSubscriber(i), lc.launchers()[i].GetPointer());  }  TEST_F(TestLauncherController, SingleMonitorEdgeBarrierSubscriptionsUpdates) @@ -204,11 +395,11 @@ TEST_F(TestLauncherController, SingleMonitorEdgeBarrierSubscriptionsUpdates)  {  if (j == i)  { - ASSERT_EQ(GetBarrierController().GetSubscriber(j), &lc.launcher()); + ASSERT_EQ(lc.Impl()->edge_barriers_.GetSubscriber(j), &lc.launcher());  }  else  { - ASSERT_EQ(GetBarrierController().GetSubscriber(j), nullptr); + ASSERT_EQ(lc.Impl()->edge_barriers_.GetSubscriber(j), nullptr);  }  }  } @@ -216,18 +407,14 @@ TEST_F(TestLauncherController, SingleMonitorEdgeBarrierSubscriptionsUpdates)  TEST_F(TestLauncherController, OnlyUnstickIconOnFavoriteRemoval)  { - const std::string USC_DESKTOP = BUILDDIR"/tests/data/ubuntu-software-center.desktop"; - - glib::Object<BamfMatcher> matcher(bamf_matcher_get_default()); - - auto bamf_app = bamf_matcher_get_application_for_desktop_file(matcher, USC_DESKTOP.c_str(), TRUE); - MockBamfLauncherIcon *bamf_icon = new MockBamfLauncherIcon(bamf_app); - GetLauncherModel()->AddIcon(AbstractLauncherIcon::Ptr(bamf_icon)); + const std::string desktop = app::BZR_HANDLE_PATCH; + MockBamfLauncherIcon::Ptr bamf_icon(new MockBamfLauncherIcon(desktop)); + lc.Impl()->model_->AddIcon(bamf_icon);  EXPECT_CALL(*bamf_icon, UnStick());  EXPECT_CALL(*bamf_icon, Quit()).Times(0); - favorite_store.favorite_removed.emit(USC_DESKTOP); + favorite_store.favorite_removed.emit(FavoriteStore::URI_PREFIX_APP + desktop);  }  TEST_F(TestLauncherController, EnabledStrutsByDefault) @@ -290,5 +477,1020 @@ TEST_F(TestLauncherController, DisabledStrutsAddingNewLaunchersOnNeverHide)  ASSERT_FALSE(lc.launchers()[i]->GetParent()->InputWindowStrutsEnabled());  } +TEST_F(TestLauncherController, CreateFavoriteInvalid) +{ + auto const& fav = lc.Impl()->CreateFavoriteIcon("InvalidUri"); + + EXPECT_FALSE(fav.IsValid()); +} + +TEST_F(TestLauncherController, CreateFavoriteDesktopFile) +{ + std::string desktop_file = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop_file; + auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); + + ASSERT_TRUE(fav.IsValid()); + EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::APPLICATION); + EXPECT_EQ(fav->DesktopFile(), desktop_file); + EXPECT_EQ(fav->RemoteUri(), icon_uri); + EXPECT_TRUE(fav->IsSticky()); + EXPECT_NE(dynamic_cast<BamfLauncherIcon*>(fav.GetPointer()), nullptr); +} + +TEST_F(TestLauncherController, CreateFavoriteInvalidDesktopFile) +{ + // This desktop file has already been added as favorite, so it is invalid + std::string desktop_file = *(favorite_store.GetFavorites().begin()); + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop_file; + auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); + + EXPECT_FALSE(fav.IsValid()); +} + +TEST_F(TestLauncherController, CreateFavoriteDevice) +{ + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& icons = lc.Impl()->device_section_.GetIcons(); + auto const& device_icon = *(icons.begin()); + + ASSERT_TRUE(device_icon.IsValid()); + ASSERT_FALSE(device_icon->IsSticky()); + + auto const& fav = lc.Impl()->CreateFavoriteIcon(device_icon->RemoteUri()); + + ASSERT_TRUE(fav.IsValid()); + EXPECT_EQ(fav, device_icon); + EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::DEVICE); + EXPECT_EQ(fav->RemoteUri(), device_icon->RemoteUri()); + EXPECT_TRUE(fav->IsSticky()); + EXPECT_NE(dynamic_cast<VolumeLauncherIcon*>(fav.GetPointer()), nullptr); +} + +TEST_F(TestLauncherController, CreateFavoriteInvalidDevice) +{ + auto const& fav = lc.Impl()->CreateFavoriteIcon(FavoriteStore::URI_PREFIX_APP + "invalid-uuid"); + + EXPECT_FALSE(fav.IsValid()); +} + +TEST_F(TestLauncherController, CreateFavoriteExpoIcon) +{ + lc.Impl()->expo_icon_->UnStick(); + std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "expo-icon"; + auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); + + ASSERT_TRUE(fav.IsValid()); + EXPECT_EQ(fav, lc.Impl()->expo_icon_); + EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::EXPO); + EXPECT_EQ(fav->RemoteUri(), icon_uri); + EXPECT_TRUE(fav->IsSticky()); + EXPECT_NE(dynamic_cast<ExpoLauncherIcon*>(fav.GetPointer()), nullptr); +} + +TEST_F(TestLauncherController, CreateFavoriteDesktopIcon) +{ + lc.Impl()->desktop_icon_->UnStick(); + std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "desktop-icon"; + auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); + + ASSERT_TRUE(fav.IsValid()); + EXPECT_EQ(fav, lc.Impl()->desktop_icon_); + EXPECT_EQ(fav->GetIconType(), AbstractLauncherIcon::IconType::DESKTOP); + EXPECT_EQ(fav->RemoteUri(), icon_uri); + EXPECT_TRUE(fav->IsSticky()); + EXPECT_NE(dynamic_cast<DesktopLauncherIcon*>(fav.GetPointer()), nullptr); +} + +TEST_F(TestLauncherController, CreateFavoriteInvalidUnity) +{ + std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "foooooo"; + auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); + + EXPECT_FALSE(fav.IsValid()); +} + +TEST_F(TestLauncherController, RegisterIconApplication) +{ + AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); + int pre_priority = icon->SortPriority(); + + ASSERT_TRUE(icon->position_saved.empty()); + ASSERT_TRUE(icon->position_forgot.empty()); + ASSERT_TRUE(icon->visibility_changed.empty()); + ASSERT_EQ(lc.Impl()->model_->IconIndex(icon), -1); + + lc.Impl()->RegisterIcon(icon); + + EXPECT_EQ(pre_priority, icon->SortPriority()); + EXPECT_FALSE(icon->position_saved.empty()); + EXPECT_FALSE(icon->position_forgot.empty()); + EXPECT_FALSE(icon->visibility_changed.empty()); + EXPECT_NE(lc.Impl()->model_->IconIndex(icon), -1); +} + +TEST_F(TestLauncherController, RegisterIconApplicationWithPriority) +{ + AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); + lc.Impl()->RegisterIcon(icon, std::numeric_limits<int>::max()); + EXPECT_EQ(icon->SortPriority(), std::numeric_limits<int>::max()); +} + +TEST_F(TestLauncherController, RegisterIconApplicationWithDefaultPriority) +{ + AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); + int pre_priority = icon->SortPriority(); + lc.Impl()->RegisterIcon(icon, std::numeric_limits<int>::min()); + EXPECT_EQ(icon->SortPriority(), pre_priority); +} + +TEST_F(TestLauncherController, RegisterIconTwoTimesDoesNotWork) +{ + AbstractLauncherIcon::Ptr icon(new MockLauncherIcon()); + lc.Impl()->RegisterIcon(icon, std::numeric_limits<int>::min()); + int pre_registrations = icon->visibility_changed.size(); + + lc.Impl()->RegisterIcon(icon, std::numeric_limits<int>::min()); + EXPECT_EQ(icon->visibility_changed.size(), pre_registrations); +} + +TEST_F(TestLauncherController, RegisterIconDevice) +{ + AbstractLauncherIcon::Ptr icon(new MockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); + int pre_priority = icon->SortPriority(); + + lc.Impl()->RegisterIcon(icon); + + EXPECT_EQ(pre_priority, icon->SortPriority()); + EXPECT_FALSE(icon->position_saved.empty()); + EXPECT_FALSE(icon->position_forgot.empty()); + EXPECT_TRUE(icon->visibility_changed.empty()); + EXPECT_NE(lc.Impl()->model_->IconIndex(icon), -1); +} + +TEST_F(TestLauncherController, RegisteredIconSavesPosition) +{ + MockBamfLauncherIcon::Ptr app_icon(new MockBamfLauncherIcon(true, "normal-icon.desktop")); + lc.Impl()->RegisterIcon(app_icon); + ASSERT_FALSE(favorite_store.IsFavorite(app_icon->RemoteUri())); + + app_icon->Stick(true); + ASSERT_TRUE(app_icon->IsSticky()); + EXPECT_TRUE(favorite_store.IsFavorite(app_icon->RemoteUri())); +} + +TEST_F(TestLauncherController, RegisteredIconForgetsPosition) +{ + auto const& fav = lc.Impl()->GetIconByUri(*(favorite_store.GetFavorites().begin())); + ASSERT_TRUE(favorite_store.IsFavorite(fav->RemoteUri())); + + fav->UnStick(); + EXPECT_FALSE(favorite_store.IsFavorite(fav->RemoteUri())); +} + +TEST_F(TestLauncherController, GetIconByUriDesktop) +{ + lc.Impl()->RegisterIcon(lc.Impl()->desktop_icon_); + std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "desktop-icon"; + auto const& fav = lc.Impl()->GetIconByUri(icon_uri); + + EXPECT_EQ(fav, lc.Impl()->desktop_icon_); +} + +TEST_F(TestLauncherController, GetIconByUriExpo) +{ + lc.Impl()->RegisterIcon(lc.Impl()->expo_icon_); + std::string icon_uri = FavoriteStore::URI_PREFIX_UNITY + "expo-icon"; + auto const& fav = lc.Impl()->GetIconByUri(icon_uri); + + EXPECT_EQ(fav, lc.Impl()->expo_icon_); +} + +TEST_F(TestLauncherController, GetIconByUriApplications) +{ + for (auto const& fav_uri : favorite_store.GetFavorites()) + { + auto const& model_icon_it = std::find_if(lc.Impl()->model_->begin(), lc.Impl()->model_->end(), + [&fav_uri](AbstractLauncherIcon::Ptr const& i) { return (i->RemoteUri() == fav_uri); }); + ASSERT_NE(model_icon_it, lc.Impl()->model_->end()); + + auto const& fav = lc.Impl()->GetIconByUri(fav_uri); + ASSERT_EQ(fav, *model_icon_it); + } + + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + auto const& fav = lc.Impl()->CreateFavoriteIcon(icon_uri); + lc.Impl()->RegisterIcon(fav); + EXPECT_EQ(fav, lc.Impl()->GetIconByUri(icon_uri)); +} + +TEST_F(TestLauncherController, AddRunningApps) +{ + lc.ClearModel(); + lc.DisconnectSignals(); + lc.Impl()->AddRunningApps(); + + std::shared_ptr<GList> apps(bamf_matcher_get_applications(lc.Impl()->matcher_), g_list_free); + + for (GList *l = apps.get(); l; l = l->next) + { + if (!BAMF_IS_APPLICATION(l->data)) + continue; + + BamfApplication* app = BAMF_APPLICATION(l->data); + + ASSERT_NE(g_object_get_qdata(G_OBJECT(app), g_quark_from_static_string("unity-seen")), nullptr); + + auto desktop = bamf_application_get_desktop_file(app); + std::string path(desktop ? desktop : ""); + + if (path.empty()) + continue; + + ASSERT_TRUE(lc.GetIconByDesktop(path).IsValid()); + } +} + +TEST_F(TestLauncherController, AddDevices) +{ + lc.ClearModel(); + lc.DisconnectSignals(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& icons = lc.Impl()->device_section_.GetIcons(); + auto const& device_icon1 = *(icons.begin()); + auto const& device_icon2 = *(std::next(icons.begin())); + + device_icon1->Stick(false); + + lc.Impl()->AddDevices(); + + EXPECT_FALSE(lc.Impl()->GetIconByUri(device_icon1->RemoteUri()).IsValid()); + EXPECT_TRUE(lc.Impl()->GetIconByUri(device_icon2->RemoteUri()).IsValid()); +} + +TEST_F(TestLauncherController, SetupIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& model = lc.Impl()->model_; + int icon_index = 0; + + favorite_store.SetFavorites({ FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, + places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }); + std::shared_ptr<GList> apps(bamf_matcher_get_applications(lc.Impl()->matcher_), g_list_free); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + auto fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE); + EXPECT_EQ(model->IconIndex(fav), icon_index); + + fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); + EXPECT_EQ(model->IconIndex(fav), ++icon_index); + + for (auto const& device : lc.Impl()->device_section_.GetIcons()) + ASSERT_EQ(model->IconIndex(device), ++icon_index); + + fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER); + EXPECT_EQ(model->IconIndex(fav), ++icon_index); + + + for (GList *l = apps.get(); l; l = l->next) + { + if (!BAMF_IS_APPLICATION(l->data)) + continue; + + auto desktop = bamf_application_get_desktop_file(BAMF_APPLICATION(l->data)); + std::string path(desktop ? desktop : ""); + ++icon_index; + + if (path.empty()) + continue; + + auto const& icon = lc.GetIconByDesktop(path); + + ASSERT_TRUE(icon.IsValid()); + ASSERT_EQ(model->IconIndex(icon), icon_index); + } +} + +TEST_F(TestLauncherController, ResetIconPriorities) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& model = lc.Impl()->model_; + + favorite_store.AddFavorite(places::APPS_URI, -1); + favorite_store.AddFavorite(places::DEVICES_URI, -1); + std::shared_ptr<GList> apps(bamf_matcher_get_applications(lc.Impl()->matcher_), g_list_free); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, + places::APPS_URI, + FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, + FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }); + lc.Impl()->ResetIconPriorities(); + + int icon_index = -1; + + for (auto const& device : lc.Impl()->device_section_.GetIcons()) + ASSERT_EQ(model->IconIndex(device), ++icon_index); + + auto fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); + EXPECT_EQ(model->IconIndex(fav), ++icon_index); + + for (GList *l = apps.get(); l; l = l->next) + { + if (!BAMF_IS_APPLICATION(l->data)) + continue; + + auto desktop = bamf_application_get_desktop_file(BAMF_APPLICATION(l->data)); + std::string path(desktop ? desktop : ""); + ++icon_index; + + if (path.empty()) + continue; + + auto const& icon = lc.GetIconByDesktop(path); + + ASSERT_TRUE(icon.IsValid()); + ASSERT_EQ(model->IconIndex(icon), icon_index); + } + + fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE); + EXPECT_EQ(model->IconIndex(fav), ++icon_index); + + fav = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER); + EXPECT_EQ(model->IconIndex(fav), ++icon_index); +} + +TEST_F(TestLauncherController, GetLastIconPriorityUnSticky) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(3); + auto const& device_icons = lc.Impl()->device_section_.GetIcons(); + auto const& last_device = *(device_icons.rbegin()); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); + + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(); + EXPECT_EQ(last_priority, last_device->SortPriority()); +} + +TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithAllStickyIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(3); + auto const& device_icons = lc.Impl()->device_section_.GetIcons(); + auto const& last_device = *(device_icons.rbegin()); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); + + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + for (auto const& device : device_icons) + device->Stick(false); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(); + EXPECT_EQ(last_priority, last_device->SortPriority()); +} + +TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithSomeStickyIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(3); + auto const& device_icons = lc.Impl()->device_section_.GetIcons(); + auto const& first_device = *(std::next(device_icons.rbegin())); + auto const& last_device = *(device_icons.rbegin()); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); + + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + last_device->Stick(false); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(); + EXPECT_EQ(last_priority, first_device->SortPriority()); +} + +TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithNoIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(0); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(); + EXPECT_EQ(last_priority, std::numeric_limits<int>::min()); +} + +TEST_F(TestLauncherController, GetLastIconPriorityUnStickyWithNoIconsAndUri) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(0); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); + lc.Impl()->SetupIcons(); + + auto first_icon = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(places::DEVICES_URI); + EXPECT_EQ(last_priority, first_icon->SortPriority() - 1); + + favorite_store.SetFavorites({ FavoriteStore::URI_PREFIX_APP + app::SW_CENTER, + places::DEVICES_URI }); + favorite_store.reordered.emit(); + + first_icon = lc.Impl()->GetIconByUri(FavoriteStore::URI_PREFIX_APP + app::SW_CENTER); + + last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(places::DEVICES_URI); + EXPECT_EQ(last_priority, first_icon->SortPriority()); +} + +TEST_F(TestLauncherController, GetLastIconPrioritySticky) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(3); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + auto const& device_icons = lc.Impl()->device_section_.GetIcons(); + auto const& first_device = *(device_icons.begin()); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>("", true); + EXPECT_EQ(last_priority, first_device->SortPriority() - 1); +} + +TEST_F(TestLauncherController, GetLastIconPriorityStickyWithAllStickyIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(3); + auto const& device_icons = lc.Impl()->device_section_.GetIcons(); + auto const& last_device = *(device_icons.rbegin()); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); + + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + for (auto const& device : device_icons) + device->Stick(false); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>("", true); + EXPECT_EQ(last_priority, last_device->SortPriority()); +} + +TEST_F(TestLauncherController, GetLastIconPriorityStickyWithSomeStickyIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(3); + auto const& device_icons = lc.Impl()->device_section_.GetIcons(); + auto const& first_device = *(std::next(device_icons.rbegin())); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::SW_CENTER }); + + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + first_device->Stick(false); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>("", true); + EXPECT_EQ(last_priority, first_device->SortPriority()); +} + +TEST_F(TestLauncherController, GetLastIconPriorityStickyWithNoIcons) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(0); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + int last_priority = lc.Impl()->GetLastIconPriority<VolumeLauncherIcon>(); + EXPECT_EQ(last_priority, std::numeric_limits<int>::min());  } +TEST_F(TestLauncherController, LauncherAddRequestApplicationAdd) +{ + auto const& model = lc.Impl()->model_; + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + ASSERT_FALSE(lc.Impl()->GetIconByUri(icon_uri).IsValid()); + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& second_app = *(std::next(app_icons.begin())); + + lc.launcher().add_request.emit(icon_uri, second_app); + + auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); + ASSERT_TRUE(new_icon.IsValid()); + EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(second_app) + 1); +} + +TEST_F(TestLauncherController, LauncherAddRequestApplicationStick) +{ + auto const& model = lc.Impl()->model_; + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_FILE + desktop; + + MockBamfLauncherIcon::Ptr bamf_icon(new MockBamfLauncherIcon(desktop)); + lc.Impl()->RegisterIcon(bamf_icon, std::numeric_limits<int>::max()); + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& first_app = *(app_icons.begin()); + ASSERT_LT(model->IconIndex(first_app), model->IconIndex(bamf_icon)); + + EXPECT_CALL(*bamf_icon, Stick(false)); + lc.launcher().add_request.emit(icon_uri, first_app); + + EXPECT_EQ(model->IconIndex(bamf_icon), model->IconIndex(first_app) + 1); +} + +TEST_F(TestLauncherController, LauncherAddRequestDeviceAdd) +{ + auto const& model = lc.Impl()->model_; + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& icons = lc.Impl()->device_section_.GetIcons(); + auto const& device_icon = *(icons.begin()); + auto const& icon_uri = device_icon->RemoteUri(); + + ASSERT_FALSE(lc.Impl()->GetIconByUri(icon_uri).IsValid()); + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& first_app = *(app_icons.begin()); + + lc.launcher().add_request.emit(icon_uri, first_app); + + auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); + ASSERT_TRUE(new_icon.IsValid()); + EXPECT_EQ(new_icon, device_icon); + EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(first_app) + 1); +} + +TEST_F(TestLauncherController, LauncherAddRequestDeviceStick) +{ + auto const& model = lc.Impl()->model_; + MockVolumeLauncherIcon::Ptr device_icon(new MockVolumeLauncherIcon()); + lc.Impl()->RegisterIcon(device_icon, std::numeric_limits<int>::max()); + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& second_app = *(std::next(app_icons.begin())); + ASSERT_LT(model->IconIndex(second_app), model->IconIndex(device_icon)); + + EXPECT_CALL(*device_icon, Stick(false)); + lc.launcher().add_request.emit(device_icon->RemoteUri(), second_app); + + EXPECT_EQ(model->IconIndex(device_icon), model->IconIndex(second_app) + 1); +} + +TEST_F(TestLauncherController, LauncherRemoveRequestApplicationUnStickAndQuit) +{ + MockBamfLauncherIcon::Ptr bamf_icon(new MockBamfLauncherIcon()); + + EXPECT_CALL(*bamf_icon, UnStick()); + EXPECT_CALL(*bamf_icon, Quit()); + lc.launcher().remove_request.emit(bamf_icon); +} + +TEST_F(TestLauncherController, LauncherRemoveRequestDeviceEjects) +{ + MockVolumeLauncherIcon::Ptr device_icon(new MockVolumeLauncherIcon()); + + EXPECT_CALL(*(device_icon->volume_), CanBeEjected()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*(device_icon->volume_), CanBeStopped()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*(device_icon->volume_), EjectAndShowNotification()); + EXPECT_CALL(*(device_icon->volume_), StopDrive()).Times(0); + + lc.launcher().remove_request.emit(device_icon); +} + +TEST_F(TestLauncherController, LauncherRemoveRequestDeviceStops) +{ + MockVolumeLauncherIcon::Ptr device_icon(new MockVolumeLauncherIcon()); + + EXPECT_CALL(*(device_icon->volume_), CanBeEjected()) + .WillRepeatedly(Return(false)); + EXPECT_CALL(*(device_icon->volume_), CanBeStopped()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*(device_icon->volume_), StopDrive()); + EXPECT_CALL(*(device_icon->volume_), EjectAndShowNotification()).Times(0); + + lc.launcher().remove_request.emit(device_icon); +} + +TEST_F(TestLauncherController, LauncherAddRequestSpecial) +{ + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + ASSERT_FALSE(lc.Impl()->GetIconByUri(icon_uri).IsValid()); + + lc.Impl()->OnLauncherAddRequestSpecial(desktop, "", "", 0, 0, 32); + + auto const& sw_center_icon = lc.Impl()->GetIconByUri(icon_uri); + ASSERT_TRUE(sw_center_icon.IsValid()); + EXPECT_NE(dynamic_cast<SoftwareCenterLauncherIcon*>(sw_center_icon.GetPointer()), nullptr); +} + +TEST_F(TestLauncherController, LauncherAddRequestSpecialIgnored) +{ + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + + MockBamfLauncherIcon::Ptr bamf_icon(new MockBamfLauncherIcon(desktop)); + lc.Impl()->RegisterIcon(bamf_icon, std::numeric_limits<int>::max()); + ASSERT_TRUE(lc.Impl()->GetIconByUri(icon_uri).IsValid()); + + EXPECT_CALL(*bamf_icon, Stick(false)).Times(0); + + int previous_model_size = lc.Impl()->model_->Size(); + lc.Impl()->OnLauncherAddRequestSpecial(desktop, "", "", 0, 0, 32); + + EXPECT_EQ(previous_model_size, lc.Impl()->model_->Size()); +} + +TEST_F(TestLauncherController, SaveIconsOrder) +{ + favorite_store.ClearFavorites(); + lc.ClearModel(); + lc.DisconnectSignals(); + int priority = 0; + + MockBamfLauncherIcon::Ptr sticky_app(new MockBamfLauncherIcon(true, "sticky-app")); + sticky_app->Stick(false); + lc.Impl()->RegisterIcon(sticky_app, ++priority); + + MockBamfLauncherIcon::Ptr invisible_app(new MockBamfLauncherIcon(true, "invisible-app")); + invisible_app->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); + lc.Impl()->RegisterIcon(invisible_app, ++priority); + + MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon()); + sticky_device->Stick(false); + lc.Impl()->RegisterIcon(sticky_device, ++priority); + + MockVolumeLauncherIcon::Ptr device(new MockVolumeLauncherIcon()); + lc.Impl()->RegisterIcon(device, ++priority); + + MockBamfLauncherIcon::Ptr running_app(new MockBamfLauncherIcon(true, "running-app")); + lc.Impl()->RegisterIcon(running_app, ++priority); + + lc.Impl()->SaveIconsOrder(); + + auto it = favorite_store.GetFavorites().begin(); + + ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; + ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; + ASSERT_EQ(*it, places::DEVICES_URI); ++it; + ASSERT_EQ(*it, places::APPS_URI); ++it; + ASSERT_EQ(it, favorite_store.GetFavorites().end()); +} + +TEST_F(TestLauncherController, SaveIconsOrderWithOnlyStickyIcons) +{ + favorite_store.ClearFavorites(); + lc.ClearModel(); + int priority = 0; + + MockBamfLauncherIcon::Ptr sticky_app(new MockBamfLauncherIcon(true, "sticky-app")); + sticky_app->Stick(false); + lc.Impl()->RegisterIcon(sticky_app, ++priority); + + MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon()); + sticky_device->Stick(false); + lc.Impl()->RegisterIcon(sticky_device, ++priority); + + lc.Impl()->SaveIconsOrder(); + + auto it = favorite_store.GetFavorites().begin(); + + ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; + ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; + ASSERT_EQ(*it, places::APPS_URI); ++it; + ASSERT_EQ(*it, places::DEVICES_URI); ++it; + ASSERT_EQ(it, favorite_store.GetFavorites().end()); +} + +TEST_F(TestLauncherController, SaveIconsOrderTriesToKeepIconProvidersOrder) +{ + favorite_store.ClearFavorites(); + lc.ClearModel(); + int priority = 0; + + favorite_store.SetFavorites({FavoriteStore::URI_PREFIX_APP + "foo.desktop", places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + "bar.desktop", places::APPS_URI, + FavoriteStore::URI_PREFIX_APP + "foobar.desktop"}); + + MockBamfLauncherIcon::Ptr sticky_app(new MockBamfLauncherIcon(true, "sticky-app")); + sticky_app->Stick(false); + lc.Impl()->RegisterIcon(sticky_app, ++priority); + + MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon()); + sticky_device->Stick(false); + lc.Impl()->RegisterIcon(sticky_device, ++priority); + + lc.Impl()->SaveIconsOrder(); + + auto it = favorite_store.GetFavorites().begin(); + + ASSERT_EQ(*it, places::DEVICES_URI); ++it; + ASSERT_EQ(*it, places::APPS_URI); ++it; + ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; + ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; + ASSERT_EQ(it, favorite_store.GetFavorites().end()); +} + +TEST_F(TestLauncherController, SaveIconsOrderTriesToKeepIconProvidersOrder2) +{ + favorite_store.ClearFavorites(); + lc.ClearModel(); + int priority = 0; + + MockBamfLauncherIcon::Ptr sticky_app(new MockBamfLauncherIcon(true, "sticky-app")); + sticky_app->Stick(false); + lc.Impl()->RegisterIcon(sticky_app, ++priority); + + MockVolumeLauncherIcon::Ptr sticky_device(new MockVolumeLauncherIcon()); + sticky_device->Stick(false); + lc.Impl()->RegisterIcon(sticky_device, ++priority); + + favorite_store.SetFavorites({places::DEVICES_URI, sticky_app->RemoteUri(), places::APPS_URI}); + lc.Impl()->SaveIconsOrder(); + + auto it = favorite_store.GetFavorites().begin(); + + ASSERT_EQ(*it, places::DEVICES_URI); ++it; + ASSERT_EQ(*it, sticky_app->RemoteUri()); ++it; + ASSERT_EQ(*it, places::APPS_URI); ++it; + ASSERT_EQ(*it, sticky_device->RemoteUri()); ++it; + ASSERT_EQ(it, favorite_store.GetFavorites().end()); +} + +TEST_F(TestLauncherController, SortAndUpdate) +{ + lc.ClearModel(); + + MockVolumeLauncherIcon::Ptr device(new MockVolumeLauncherIcon()); + lc.Impl()->RegisterIcon(device, 0); + + for (int i = 0; i < 15; ++i) + { + MockBamfLauncherIcon::Ptr app(new MockBamfLauncherIcon()); + app->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, (i % 5) != 0); + lc.Impl()->RegisterIcon(app, 0); + } + + int expected_shortcut = 1; + + for (auto const& icon : *(lc.Impl()->model_)) + { + if (icon->IsVisible() && icon->GetIconType() == AbstractLauncherIcon::IconType::APPLICATION && expected_shortcut <= 10) + { + ASSERT_EQ(icon->GetShortcut(), std::to_string(expected_shortcut % 10)[0]); + ++expected_shortcut; + } + else + { + ASSERT_EQ(icon->GetShortcut(), 0); + } + } +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNew) +{ + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + app::BZR_HANDLE_PATCH; + + favorite_store.favorite_added.emit(icon_uri, "", true); + + auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); + ASSERT_TRUE(new_icon.IsValid()); + EXPECT_TRUE(new_icon->IsSticky()); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNewBeforeIcon) +{ + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + app::BZR_HANDLE_PATCH; + auto const& model = lc.Impl()->model_; + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& first_app = *(app_icons.begin()); + favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), true); + + auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); + + ASSERT_TRUE(new_icon.IsValid()); + EXPECT_TRUE(new_icon->IsSticky()); + EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(first_app) - 1); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedNewAfterIcon) +{ + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + app::BZR_HANDLE_PATCH; + auto const& model = lc.Impl()->model_; + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& first_app = *(app_icons.begin()); + favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), false); + + auto const& new_icon = lc.Impl()->GetIconByUri(icon_uri); + + ASSERT_TRUE(new_icon.IsValid()); + EXPECT_TRUE(new_icon->IsSticky()); + EXPECT_EQ(model->IconIndex(new_icon), model->IconIndex(first_app) + 1); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStick) +{ + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + + MockBamfLauncherIcon::Ptr app_icon(new MockBamfLauncherIcon(desktop)); + lc.Impl()->RegisterIcon(app_icon, std::numeric_limits<int>::max()); + + EXPECT_CALL(*app_icon, Stick(false)); + favorite_store.favorite_added.emit(icon_uri, "", false); + EXPECT_TRUE(app_icon->IsSticky()); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStickBefore) +{ + auto const& model = lc.Impl()->model_; + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + + MockBamfLauncherIcon::Ptr app_icon(new MockBamfLauncherIcon(desktop)); + lc.Impl()->RegisterIcon(app_icon, std::numeric_limits<int>::max()); + + auto app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& first_app = *(app_icons.begin()); + ASSERT_LT(model->IconIndex(first_app), model->IconIndex(app_icon)); + + EXPECT_CALL(*app_icon, Stick(false)); + + favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), false); + EXPECT_TRUE(app_icon->IsSticky()); + EXPECT_EQ(model->IconIndex(app_icon), model->IconIndex(first_app) + 1); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedStickAfter) +{ + auto const& model = lc.Impl()->model_; + std::string desktop = app::BZR_HANDLE_PATCH; + std::string icon_uri = FavoriteStore::URI_PREFIX_APP + desktop; + + MockBamfLauncherIcon::Ptr app_icon(new MockBamfLauncherIcon(desktop)); + lc.Impl()->RegisterIcon(app_icon, std::numeric_limits<int>::max()); + + auto const& app_icons = model->GetSublist<BamfLauncherIcon>(); + auto const& first_app = *(app_icons.begin()); + ASSERT_LT(model->IconIndex(first_app), model->IconIndex(app_icon)); + + EXPECT_CALL(*app_icon, Stick(false)); + + favorite_store.favorite_added.emit(icon_uri, first_app->RemoteUri(), true); + EXPECT_TRUE(app_icon->IsSticky()); + EXPECT_EQ(model->IconIndex(app_icon), model->IconIndex(first_app) - 1); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteAddedDeviceSection) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& model = lc.Impl()->model_; + auto const& icons = lc.Impl()->device_section_.GetIcons(); + auto const& device_icon1(*(icons.begin())); + auto const& device_icon2(*(std::next(icons.begin()))); + + favorite_store.SetFavorites({ FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE }); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + auto const& app_icons = lc.Impl()->model_->GetSublist<BamfLauncherIcon>(); + auto const& last_app = *(app_icons.rbegin()); + + ASSERT_EQ(model->IconIndex(device_icon1), model->IconIndex(last_app) + 1); + ASSERT_EQ(model->IconIndex(device_icon2), model->IconIndex(last_app) + 2); + + favorite_store.AddFavorite(places::DEVICES_URI, 0); + favorite_store.favorite_added.emit(places::DEVICES_URI, "", false); + + EXPECT_EQ(model->IconIndex(device_icon1), 0); + EXPECT_EQ(model->IconIndex(device_icon2), 1); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedApplication) +{ + MockBamfLauncherIcon::Ptr app_icon(new MockBamfLauncherIcon(true, "sticky-icon")); + lc.Impl()->RegisterIcon(app_icon); + app_icon->Stick(false); + + EXPECT_CALL(*app_icon, UnStick()); + favorite_store.favorite_removed.emit(app_icon->RemoteUri()); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDevice) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& model = lc.Impl()->model_; + + auto const& icons = lc.Impl()->device_section_.GetIcons(); + auto const& device_icon(*(icons.begin())); + + favorite_store.SetFavorites({ FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE, + device_icon->RemoteUri(), + FavoriteStore::URI_PREFIX_APP + app::UPDATE_MANAGER }); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + ASSERT_EQ(model->IconIndex(device_icon), 1); + + favorite_store.RemoveFavorite(device_icon->RemoteUri()); + favorite_store.favorite_removed.emit(device_icon->RemoteUri()); + + auto const& app_icons = lc.Impl()->model_->GetSublist<BamfLauncherIcon>(); + auto const& last_app = *(app_icons.rbegin()); + EXPECT_EQ(model->IconIndex(device_icon), model->IconIndex(last_app) + 1); +} + +TEST_F(TestLauncherController, OnFavoriteStoreFavoriteRemovedDeviceSection) +{ + lc.ClearModel(); + lc.Impl()->device_section_ = MockDeviceLauncherSection(); + auto const& model = lc.Impl()->model_; + + auto const& icons = lc.Impl()->device_section_.GetIcons(); + auto const& device_icon1(*(icons.begin())); + auto const& device_icon2(*(std::next(icons.begin()))); + + favorite_store.SetFavorites({ places::DEVICES_URI, + FavoriteStore::URI_PREFIX_APP + app::UBUNTU_ONE }); + lc.Impl()->SetupIcons(); + lc.DisconnectSignals(); + + ASSERT_EQ(model->IconIndex(device_icon1), 0); + ASSERT_EQ(model->IconIndex(device_icon2), 1); + + favorite_store.RemoveFavorite(places::DEVICES_URI); + favorite_store.favorite_removed.emit(places::DEVICES_URI); + + auto const& app_icons = lc.Impl()->model_->GetSublist<BamfLauncherIcon>(); + auto const& last_app = *(app_icons.rbegin()); + EXPECT_EQ(model->IconIndex(device_icon1), model->IconIndex(last_app) + 1); + EXPECT_EQ(model->IconIndex(device_icon2), model->IconIndex(last_app) + 2); +} + +TEST_F(TestLauncherController, OnViewOpened) +{ + auto const& app_icons = lc.Impl()->model_->GetSublist<BamfLauncherIcon>(); + auto const& last_app = *(app_icons.rbegin()); + + auto app = bamf_matcher_get_application_for_desktop_file(lc.Impl()->matcher_, app::BZR_HANDLE_PATCH.c_str(), TRUE); + g_signal_emit_by_name(lc.Impl()->matcher_, "view-opened", app); + lc.DisconnectSignals(); + + auto const& icon = lc.GetIconByDesktop(app::BZR_HANDLE_PATCH); + ASSERT_TRUE(icon.IsValid()); + + ASSERT_EQ(lc.Impl()->model_->IconIndex(icon), lc.Impl()->model_->IconIndex(last_app) + 1); +} + +TEST_F(TestLauncherController, UpdateNumWorkspacesDisable) +{ + favorite_store.AddFavorite(lc.Impl()->expo_icon_->RemoteUri(), -1); + auto const& fav = lc.Impl()->CreateFavoriteIcon(lc.Impl()->expo_icon_->RemoteUri()); + lc.Impl()->RegisterIcon(fav); + ASSERT_TRUE(lc.Impl()->expo_icon_->IsVisible()); + + lc.UpdateNumWorkspaces(1); + EXPECT_FALSE(lc.Impl()->expo_icon_->IsVisible()); +} + +TEST_F(TestLauncherController, UpdateNumWorkspacesEnable) +{ + favorite_store.AddFavorite(lc.Impl()->expo_icon_->RemoteUri(), -1); + auto const& fav = lc.Impl()->CreateFavoriteIcon(lc.Impl()->expo_icon_->RemoteUri()); + lc.Impl()->RegisterIcon(fav); + lc.Impl()->expo_icon_->SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); + + lc.UpdateNumWorkspaces(2); + EXPECT_TRUE(lc.Impl()->expo_icon_->IsVisible()); +} + +} diff --git a/tests/test_launcher_icon.cpp b/tests/test_launcher_icon.cpp new file mode 100644 index 000000000..f94618adb --- /dev/null +++ b/tests/test_launcher_icon.cpp @@ -0,0 +1,113 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> + +#include "LauncherIcon.h" + +using namespace unity; +using namespace unity::launcher; + +namespace +{ +struct MockLauncherIcon : LauncherIcon +{ + MockLauncherIcon(IconType type) + : LauncherIcon(type) + {} + + virtual nux::BaseTexture* GetTextureForSize(int size) { return nullptr; } +}; + +struct TestLauncherIcon : testing::Test +{ + TestLauncherIcon() +	: icon(AbstractLauncherIcon::IconType::APPLICATION) + {} + + MockLauncherIcon icon; +}; + +TEST_F(TestLauncherIcon, Construction) +{ + EXPECT_EQ(icon.GetIconType(), AbstractLauncherIcon::IconType::APPLICATION); + EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); + EXPECT_EQ(icon.SortPriority(), AbstractLauncherIcon::DefaultPriority(AbstractLauncherIcon::IconType::APPLICATION)); + EXPECT_FALSE(icon.IsSticky()); + EXPECT_FALSE(icon.IsVisible()); + + for (unsigned i = 0; i < unsigned(AbstractLauncherIcon::Quirk::LAST); ++i) + ASSERT_FALSE(icon.GetQuirk(static_cast<AbstractLauncherIcon::Quirk>(i))); +} + +TEST_F(TestLauncherIcon, Visibility) +{ + ASSERT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); + ASSERT_FALSE(icon.IsVisible()); + + icon.SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, true); + ASSERT_TRUE(icon.GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); + EXPECT_TRUE(icon.IsVisible()); + + icon.SetQuirk(AbstractLauncherIcon::Quirk::VISIBLE, false); + ASSERT_FALSE(icon.GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); + EXPECT_FALSE(icon.IsVisible()); +} + +TEST_F(TestLauncherIcon, Stick) +{ + bool saved = false; + icon.position_saved.connect([&saved] {saved = true;}); + + icon.Stick(false); + EXPECT_TRUE(icon.IsSticky()); + EXPECT_TRUE(icon.IsVisible()); + EXPECT_FALSE(saved); + + icon.Stick(true); + EXPECT_FALSE(saved); +} + +TEST_F(TestLauncherIcon, StickAndSave) +{ + bool saved = false; + icon.position_saved.connect([&saved] {saved = true;}); + + icon.Stick(true); + EXPECT_TRUE(icon.IsSticky()); + EXPECT_TRUE(icon.IsVisible()); + EXPECT_TRUE(saved); +} + +TEST_F(TestLauncherIcon, Unstick) +{ + bool forgot = false; + icon.position_forgot.connect([&forgot] {forgot = true;}); + + icon.Stick(false); + ASSERT_TRUE(icon.IsSticky()); + ASSERT_TRUE(icon.IsVisible()); + + icon.UnStick(); + EXPECT_FALSE(icon.IsSticky()); + EXPECT_FALSE(icon.IsVisible()); + EXPECT_TRUE(forgot); +} + +} diff --git a/tests/test_launcher_model.cpp b/tests/test_launcher_model.cpp index a56c929cf..30a698362 100644 --- a/tests/test_launcher_model.cpp +++ b/tests/test_launcher_model.cpp @@ -158,6 +158,27 @@ TEST_F(TestLauncherModel, ModelKeepsPriorityDeltas)  TEST_F(TestLauncherModel, ReorderBefore)  { + model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); + + model.ReorderBefore(icon3, icon2, false); + + LauncherModel::iterator it; + it = model.begin(); + + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon4, *it); +} + +TEST_F(TestLauncherModel, ReorderBeforeWithPriority) +{  icon1->SetSortPriority(0);  icon2->SetSortPriority(1);  icon3->SetSortPriority(2); @@ -182,28 +203,70 @@ TEST_F(TestLauncherModel, ReorderBefore)  EXPECT_EQ(icon4, *it);  } -TEST_F(TestLauncherModel, ReorderAfter) +TEST_F(TestLauncherModel, ReorderAfterNext)  {  model.AddIcon(icon1); + model.AddIcon(icon2);  model.AddIcon(icon3); + model.AddIcon(icon4); + + model.ReorderAfter(icon2, icon3); + + LauncherModel::iterator it; + it = model.begin(); + + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon4, *it); +} + +TEST_F(TestLauncherModel, ReorderAfterPrevious) +{ + model.AddIcon(icon1);  model.AddIcon(icon2); + model.AddIcon(icon3);  model.AddIcon(icon4); - model.ReorderAfter(icon3, icon2); + model.ReorderAfter(icon4, icon1);  LauncherModel::iterator it;  it = model.begin();  EXPECT_EQ(icon1, *it);  it++; + EXPECT_EQ(icon4, *it); + it++;  EXPECT_EQ(icon2, *it);  it++;  EXPECT_EQ(icon3, *it); +} + +TEST_F(TestLauncherModel, ReorderSmart) +{ + model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); + + model.ReorderSmart(icon3, icon2, false); + + LauncherModel::iterator it; + it = model.begin(); + + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon2, *it);  it++;  EXPECT_EQ(icon4, *it);  } -TEST_F(TestLauncherModel, ReorderSmart) +TEST_F(TestLauncherModel, ReorderSmartWithDifferentPriority)  {  icon1->SetSortPriority(0);  icon2->SetSortPriority(1); @@ -229,50 +292,129 @@ TEST_F(TestLauncherModel, ReorderSmart)  EXPECT_EQ(icon4, *it);  } -TEST_F(TestLauncherModel, OrderByType) +TEST_F(TestLauncherModel, ReorderSmartWithSimilarPriority)  { - AbstractLauncherIcon::Ptr icon1(new MockLauncherIcon(AbstractLauncherIcon::IconType::HOME)); - AbstractLauncherIcon::Ptr icon2(new MockLauncherIcon(AbstractLauncherIcon::IconType::APPLICATION)); - AbstractLauncherIcon::Ptr icon3(new MockLauncherIcon(AbstractLauncherIcon::IconType::EXPO)); - AbstractLauncherIcon::Ptr icon4(new MockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); - AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon(AbstractLauncherIcon::IconType::TRASH)); + icon1->SetSortPriority(0); + icon2->SetSortPriority(0); + icon3->SetSortPriority(1); + icon4->SetSortPriority(1); + model.AddIcon(icon1); + model.AddIcon(icon2);  model.AddIcon(icon3);  model.AddIcon(icon4); - model.AddIcon(icon2); + + model.ReorderSmart(icon4, icon3, false); + + LauncherModel::iterator it; + it = model.begin(); + + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon4, *it); + it++; + EXPECT_EQ(icon3, *it); +} + +TEST_F(TestLauncherModel, ReorderSmartManyIconsWithSimilarPriority) +{ + AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon); + AbstractLauncherIcon::Ptr icon6(new MockLauncherIcon); + icon1->SetSortPriority(0); + icon2->SetSortPriority(0); + icon3->SetSortPriority(1); + icon4->SetSortPriority(1); + icon5->SetSortPriority(1); + icon6->SetSortPriority(2); +  model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4);  model.AddIcon(icon5); + model.AddIcon(icon6); + + model.ReorderSmart(icon6, icon4, false); + + LauncherModel::iterator it; + it = model.begin(); + + EXPECT_EQ(icon1, *it); it++; + EXPECT_EQ(icon2, *it); it++; + EXPECT_EQ(icon3, *it); it++; + EXPECT_EQ(icon6, *it); it++; + EXPECT_EQ(icon4, *it); it++; + EXPECT_EQ(icon5, *it); +} + +TEST_F(TestLauncherModel, OrderByPosition) +{ + icon1->position = AbstractLauncherIcon::Position::BEGIN; + icon2->position = AbstractLauncherIcon::Position::FLOATING; + icon3->position = AbstractLauncherIcon::Position::FLOATING; + icon4->position = AbstractLauncherIcon::Position::END; + + model.AddIcon(icon3); + model.AddIcon(icon4); + model.AddIcon(icon2); + model.AddIcon(icon1);  auto it = model.begin();  EXPECT_EQ(icon1, *it);  it++; - EXPECT_EQ(icon2, *it); - it++;  EXPECT_EQ(icon3, *it);  it++; - EXPECT_EQ(icon4, *it); + EXPECT_EQ(icon2, *it);  it++; - EXPECT_EQ(icon5, *it); + EXPECT_EQ(icon4, *it);  it++;  EXPECT_EQ(it, model.end());  auto it_main = model.main_begin();  EXPECT_EQ(icon1, *it_main);  it_main++; - EXPECT_EQ(icon2, *it_main); - it_main++;  EXPECT_EQ(icon3, *it_main);  it_main++; - EXPECT_EQ(icon4, *it_main); + EXPECT_EQ(icon2, *it_main);  it_main++;  EXPECT_EQ(it_main, model.main_end());  auto it_shelf = model.shelf_begin(); - EXPECT_EQ(icon5, *it_shelf); + EXPECT_EQ(icon4, *it_shelf);  it_shelf++;  EXPECT_EQ(it_shelf, model.shelf_end());  } +TEST_F(TestLauncherModel, OrderByType) +{ + AbstractLauncherIcon::Ptr icon1(new MockLauncherIcon(AbstractLauncherIcon::IconType::HOME)); + AbstractLauncherIcon::Ptr icon2(new MockLauncherIcon(AbstractLauncherIcon::IconType::APPLICATION)); + AbstractLauncherIcon::Ptr icon3(new MockLauncherIcon(AbstractLauncherIcon::IconType::EXPO)); + AbstractLauncherIcon::Ptr icon4(new MockLauncherIcon(AbstractLauncherIcon::IconType::DEVICE)); + AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon(AbstractLauncherIcon::IconType::TRASH)); + + model.AddIcon(icon3); + model.AddIcon(icon4); + model.AddIcon(icon2); + model.AddIcon(icon1); + model.AddIcon(icon5); + + auto it = model.begin(); + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon4, *it); + it++; + EXPECT_EQ(icon5, *it); + it++; + EXPECT_EQ(it, model.end()); +} +  TEST_F(TestLauncherModel, GetClosestIcon)  {  model.AddIcon(icon1); diff --git a/tests/test_main.cpp b/tests/test_main.cpp index a92978dab..ce210ddec 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -11,6 +11,7 @@ int main(int argc, char** argv)  {  ::testing::InitGoogleTest(&argc, argv);  gtk_init(&argc, &argv); + setlocale(LC_ALL, "C");  nux::NuxInitialize(0);  std::unique_ptr<nux::WindowThread> win_thread(nux::CreateNuxWindow("Tests", diff --git a/tests/test_main_xless.cpp b/tests/test_main_xless.cpp index da7862703..41577d15d 100644 --- a/tests/test_main_xless.cpp +++ b/tests/test_main_xless.cpp @@ -7,7 +7,7 @@ int main(int argc, char** argv)  {  ::testing::InitGoogleTest(&argc, argv);  g_type_init(); -  + setlocale(LC_ALL, "C");  // Slightly higher as we're more likely to test things we know will fail  nux::logging::configure_logging("<root>=error"); diff --git a/tests/test_mock_devices.h b/tests/test_mock_devices.h new file mode 100644 index 000000000..427608bbc --- /dev/null +++ b/tests/test_mock_devices.h @@ -0,0 +1,93 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the applicable version of the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of both the GNU Lesser General Public + * License version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com> + * + */ + +#ifndef TEST_MOCK_DEVICES_H +#define TEST_MOCK_DEVICES_H + +#include "DeviceLauncherSection.h" +#include "AbstractVolumeMonitorWrapper.h" +#include "Volume.h" +#include "gmockvolume.h" + +using namespace unity::launcher; + +namespace unity +{ + +struct MockVolumeMonitorWrapper : public AbstractVolumeMonitorWrapper +{ + typedef std::shared_ptr<MockVolumeMonitorWrapper> Ptr; + + MockVolumeMonitorWrapper(unsigned size = 2) + { + for (unsigned i = 0; i < size; ++i) + { + glib::Object<GVolume> volume(G_VOLUME(g_mock_volume_new())); + volumes_.push_back(volume); + } + } + + VolumeList GetVolumes() { return volumes_; } + + VolumeList volumes_; +}; + +struct MockDevicesSettings : DevicesSettings +{ + typedef std::shared_ptr<MockDevicesSettings> Ptr; + + MOCK_CONST_METHOD1(IsABlacklistedDevice, bool(std::string const& uuid)); + MOCK_METHOD1(TryToBlacklist, void(std::string const& uuid)); + MOCK_METHOD1(TryToUnblacklist, void(std::string const& uuid)); +}; + +struct MockDeviceLauncherSection : DeviceLauncherSection +{ + MockDeviceLauncherSection(unsigned size = 2) + : DeviceLauncherSection(MockVolumeMonitorWrapper::Ptr(new MockVolumeMonitorWrapper(size)), + DevicesSettings::Ptr(new MockDevicesSettings)) + {} +}; + +class MockVolume : public Volume +{ +public: + typedef std::shared_ptr<MockVolume> Ptr; + + MOCK_CONST_METHOD0(CanBeRemoved, bool(void)); + MOCK_CONST_METHOD0(CanBeStopped, bool(void)); + MOCK_CONST_METHOD0(GetName, std::string(void)); + MOCK_CONST_METHOD0(GetIconName, std::string(void)); + MOCK_CONST_METHOD0(GetIdentifier, std::string(void)); + MOCK_CONST_METHOD0(HasSiblings, bool(void)); + MOCK_CONST_METHOD0(CanBeEjected, bool(void)); + MOCK_CONST_METHOD0(IsMounted, bool(void)); + + MOCK_METHOD0(EjectAndShowNotification, void(void)); + MOCK_METHOD0(MountAndOpenInFileManager, void(void)); + MOCK_METHOD0(StopDrive, void(void)); + MOCK_METHOD0(Unmount, void(void)); +}; + +} + +#endif diff --git a/tests/test_software_center_launcher_icon.cpp b/tests/test_software_center_launcher_icon.cpp new file mode 100644 index 000000000..1ca700e67 --- /dev/null +++ b/tests/test_software_center_launcher_icon.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <config.h> +#include <gmock/gmock.h> + +#include "SoftwareCenterLauncherIcon.h" +#include "Launcher.h" +#include "PanelStyle.h" +#include "UnitySettings.h" +#include "test_utils.h" + +using namespace unity; +using namespace unity::launcher; + +namespace +{ +const std::string USC_DESKTOP = BUILDDIR"/tests/data/applications/ubuntu-software-center.desktop"; + +struct TestSoftwareCenterLauncherIcon : testing::Test +{ + TestSoftwareCenterLauncherIcon() + : bamf_matcher(bamf_matcher_get_default()) + , usc(bamf_matcher_get_application_for_desktop_file(bamf_matcher, USC_DESKTOP.c_str(), TRUE), glib::AddRef()) + , icon(usc, "", "") + {} + + glib::Object<BamfMatcher> bamf_matcher; + glib::Object<BamfApplication> usc; + SoftwareCenterLauncherIcon icon; +}; + +TEST_F(TestSoftwareCenterLauncherIcon, Construction) +{ + EXPECT_FALSE(icon.IsVisible()); + EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::FLOATING); + EXPECT_EQ(icon.tooltip_text(), bamf_view_get_name(glib::object_cast<BamfView>(usc))); +} + +TEST_F(TestSoftwareCenterLauncherIcon, Animate) +{ + ASSERT_FALSE(icon.IsVisible()); + + Settings settings; + panel::Style panel; + nux::ObjectPtr<nux::BaseWindow> win(new nux::BaseWindow("")); + nux::ObjectPtr<DNDCollectionWindow> cwin(new DNDCollectionWindow); + nux::ObjectPtr<Launcher> launcher(new Launcher(win.GetPointer(), cwin)); + launcher->options = Options::Ptr(new Options); + launcher->SetModel(LauncherModel::Ptr(new LauncherModel)); + + icon.Animate(launcher, 1, 2); + Utils::WaitForTimeoutMSec(500); + + EXPECT_TRUE(icon.IsVisible()); +} + +} diff --git a/tests/test_trash_launcher_icon.cpp b/tests/test_trash_launcher_icon.cpp new file mode 100644 index 000000000..dc91ee489 --- /dev/null +++ b/tests/test_trash_launcher_icon.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2012 Canonical Ltd. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 3 along with this program. If not, see + * <http://www.gnu.org/licenses/> + * + * Authored by: Marco Trevisan (Treviño) <marco.trevisan@canonical.com> + */ + +#include <gmock/gmock.h> + +#include "TrashLauncherIcon.h" + +using namespace unity; +using namespace unity::launcher; + +namespace +{ +struct TestTrashLauncherIcon : testing::Test +{ + TrashLauncherIcon icon; +}; + +TEST_F(TestTrashLauncherIcon, Position) +{ + EXPECT_EQ(icon.position(), AbstractLauncherIcon::Position::END); +} + +} diff --git a/tests/test_volume_launcher_icon.cpp b/tests/test_volume_launcher_icon.cpp index 287a9558c..46905144b 100644 --- a/tests/test_volume_launcher_icon.cpp +++ b/tests/test_volume_launcher_icon.cpp @@ -20,50 +20,19 @@  #include <gmock/gmock.h>  using namespace testing; -#include "launcher/DevicesSettings.h" -#include "launcher/Volume.h" -#include "launcher/VolumeLauncherIcon.h" +#include "DevicesSettings.h" +#include "VolumeLauncherIcon.h" +#include "FavoriteStore.h"  #include "test_utils.h" +#include "test_mock_devices.h"  using namespace unity;  using namespace unity::launcher;  namespace  { -class MockVolume : public Volume +struct TestVolumeLauncherIcon : public Test  { -public: - typedef std::shared_ptr<MockVolume> Ptr; - - MOCK_CONST_METHOD0(CanBeRemoved, bool(void)); - MOCK_CONST_METHOD0(CanBeStopped, bool(void)); - MOCK_CONST_METHOD0(GetName, std::string(void)); - MOCK_CONST_METHOD0(GetIconName, std::string(void)); - MOCK_CONST_METHOD0(GetIdentifier, std::string(void)); - MOCK_CONST_METHOD0(HasSiblings, bool(void)); - MOCK_CONST_METHOD0(CanBeEjected, bool(void)); - MOCK_CONST_METHOD0(IsMounted, bool(void)); - - MOCK_METHOD0(EjectAndShowNotification, void(void)); - MOCK_METHOD0(MountAndOpenInFileManager, void(void)); - MOCK_METHOD0(StopDrive, void(void)); - MOCK_METHOD0(Unmount, void(void)); -}; - -class MockDevicesSettings : public DevicesSettings -{ -public: - typedef std::shared_ptr<MockDevicesSettings> Ptr; - - MOCK_CONST_METHOD1(IsABlacklistedDevice, bool(std::string const& uuid)); - MOCK_METHOD1(TryToBlacklist, void(std::string const& uuid)); - MOCK_METHOD1(TryToUnblacklist, void(std::string const& uuid)); -}; - - -class TestVolumeLauncherIcon : public Test -{ -public:  virtual void SetUp()  {  volume_.reset(new MockVolume); @@ -118,11 +87,12 @@ public:  std::advance(menuitem, index);  return *menuitem; - }  + }  MockVolume::Ptr volume_;  MockDevicesSettings::Ptr settings_;  VolumeLauncherIcon::Ptr icon_; + std::string old_lang_;  };  TEST_F(TestVolumeLauncherIcon, TestIconType) @@ -138,6 +108,13 @@ TEST_F(TestVolumeLauncherIcon, TestQuirks)  EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING));  } +TEST_F(TestVolumeLauncherIcon, TestPosition) +{ + CreateIcon(); + + EXPECT_EQ(icon_->position(), AbstractLauncherIcon::Position::FLOATING); +} +  TEST_F(TestVolumeLauncherIcon, TestTooltipText)  {  CreateIcon(); @@ -159,6 +136,12 @@ TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedVolume)  EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));  } +TEST_F(TestVolumeLauncherIcon, RemoteUri) +{ + CreateIcon(); + EXPECT_EQ(icon_->GetRemoteUri(), FavoriteStore::URI_PREFIX_DEVICE + volume_->GetIdentifier()); +} +  TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedBlacklistedVolume)  {  EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) @@ -216,7 +199,6 @@ TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount)  .Times(0);  volume_->changed.emit(); - Utils::WaitForTimeout(1);  EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));  } @@ -235,7 +217,6 @@ TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount_BlacklistedVolume)  .Times(0);  volume_->changed.emit(); - Utils::WaitForTimeout(1);  EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));  } @@ -269,7 +250,6 @@ TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Success)  dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0);  settings_->changed.emit(); // TryToBlacklist() works if DevicesSettings emits a changed signal. - Utils::WaitForTimeout(1);  ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));  } @@ -288,7 +268,6 @@ TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Failure)  .Times(1);  dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); - Utils::WaitForTimeout(1);  ASSERT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE));  } @@ -307,7 +286,6 @@ TEST_F(TestVolumeLauncherIcon, TestOpenMenuItem)  .Times(1);  dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); - Utils::WaitForTimeout(1);  }  TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotEjectableVolume) @@ -335,7 +313,6 @@ TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem)  EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED));  dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); - Utils::WaitForTimeout(1);  }  TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotStoppableVolume) @@ -363,7 +340,6 @@ TEST_F(TestVolumeLauncherIcon, TestSafelyRemoveMenuItem)  EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED));  dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); - Utils::WaitForTimeout(1);  }  TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem_UnmountedVolume) @@ -423,7 +399,6 @@ TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem)  EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED));  dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); - Utils::WaitForTimeout(1);  }  TEST_F(TestVolumeLauncherIcon, TestCanBeEject) @@ -495,4 +470,50 @@ TEST_F(TestVolumeLauncherIcon, OnRemoved_RemovableAndBlacklistedVolume)  volume_->removed.emit();  } +TEST_F(TestVolumeLauncherIcon, Stick) +{ + CreateIcon(); + + bool saved = false; + icon_->position_saved.connect([&saved] {saved = true;}); + + EXPECT_CALL(*settings_, TryToUnblacklist(volume_->GetIdentifier())); + icon_->Stick(false); + EXPECT_TRUE(icon_->IsSticky()); + EXPECT_TRUE(icon_->IsVisible()); + EXPECT_FALSE(saved); +} + +TEST_F(TestVolumeLauncherIcon, StickAndSave) +{ + CreateIcon(); + + bool saved = false; + icon_->position_saved.connect([&saved] {saved = true;}); + + EXPECT_CALL(*settings_, TryToUnblacklist(volume_->GetIdentifier())); + icon_->Stick(true); + EXPECT_TRUE(icon_->IsSticky()); + EXPECT_TRUE(icon_->IsVisible()); + EXPECT_TRUE(saved); +} + +TEST_F(TestVolumeLauncherIcon, Unstick) +{ + CreateIcon(); + + bool forgot = false; + icon_->position_forgot.connect([&forgot] {forgot = true;}); + + EXPECT_CALL(*settings_, TryToUnblacklist(_)); + icon_->Stick(false); + ASSERT_TRUE(icon_->IsSticky()); + ASSERT_TRUE(icon_->IsVisible()); + + icon_->UnStick(); + EXPECT_FALSE(icon_->IsSticky()); + EXPECT_TRUE(icon_->IsVisible()); + EXPECT_TRUE(forgot); +} +  } diff --git a/unity-shared/PluginAdapter.h b/unity-shared/PluginAdapter.h index a6fda2bcf..36e454873 100644 --- a/unity-shared/PluginAdapter.h +++ b/unity-shared/PluginAdapter.h @@ -135,7 +135,9 @@ public:  void Activate(guint32 xid);  void Raise(guint32 xid);  void Lower(guint32 xid); +  void ShowDesktop(); + bool InShowDesktop() const;  void SetWindowIconGeometry(Window window, nux::Geometry const& geo); diff --git a/unity-shared/PluginAdapterCompiz.cpp b/unity-shared/PluginAdapterCompiz.cpp index fe1c978cc..5ddcd68ad 100644 --- a/unity-shared/PluginAdapterCompiz.cpp +++ b/unity-shared/PluginAdapterCompiz.cpp @@ -862,6 +862,11 @@ PluginAdapter::ShowDesktop()  }  } +bool PluginAdapter::InShowDesktop() const +{ + return _in_show_desktop; +} +  void  PluginAdapter::OnShowDesktop()  { diff --git a/unity-shared/PluginAdapterStandalone.cpp b/unity-shared/PluginAdapterStandalone.cpp index 9e31f2a1b..83982a12a 100644 --- a/unity-shared/PluginAdapterStandalone.cpp +++ b/unity-shared/PluginAdapterStandalone.cpp @@ -50,12 +50,13 @@ PluginAdapter::Initialize(CompScreen* screen)  _default = new PluginAdapter(screen);  } -PluginAdapter::PluginAdapter(CompScreen* screen) : - m_Screen(screen), - m_ExpoActionList(0), - m_ScaleActionList(0), - _in_show_desktop (false), - _last_focused_window(nullptr) +PluginAdapter::PluginAdapter(CompScreen* screen) + : m_Screen(screen) + , m_ExpoActionList(0) + , m_ScaleActionList(0) + , _expo_state(false) + , _in_show_desktop(false) + , _last_focused_window(nullptr)  {  } @@ -171,12 +172,13 @@ PluginAdapter::IsScaleActiveForGroup() const  bool  PluginAdapter::IsExpoActive() const  { - return false; + return _expo_state;  }  void  PluginAdapter::InitiateExpo()  { + _expo_state = !_expo_state;  }  bool @@ -306,6 +308,12 @@ PluginAdapter::SetWindowIconGeometry(Window window, nux::Geometry const& geo)  void  PluginAdapter::ShowDesktop()  { + _in_show_desktop = !_in_show_desktop; +} + +bool PluginAdapter::InShowDesktop() const +{ + return _in_show_desktop;  }  void diff --git a/unity-shared/WindowManager.cpp b/unity-shared/WindowManager.cpp index df983217c..5359d1a08 100644 --- a/unity-shared/WindowManager.cpp +++ b/unity-shared/WindowManager.cpp @@ -48,6 +48,11 @@ class WindowManagerDummy : public WindowManager  g_debug("%s", G_STRFUNC);  } + bool InShowDesktop() const + { + return false; + } +  bool IsWindowMaximized(guint32 xid) const  {  return false; diff --git a/unity-shared/WindowManager.h b/unity-shared/WindowManager.h index 204c9439c..0600f5552 100644 --- a/unity-shared/WindowManager.h +++ b/unity-shared/WindowManager.h @@ -63,6 +63,7 @@ public:  virtual bool IsWindowMaximizable(guint32 xid) const = 0;  virtual void ShowDesktop() = 0; + virtual bool InShowDesktop() const = 0;  virtual void Restore(guint32 xid) = 0;  virtual void RestoreAt(guint32 xid, int x, int y) = 0; diff --git a/unity-standalone/StandaloneUnity.cpp b/unity-standalone/StandaloneUnity.cpp index fc5421fce..50069c989 100644 --- a/unity-standalone/StandaloneUnity.cpp +++ b/unity-standalone/StandaloneUnity.cpp @@ -86,7 +86,7 @@ UnityStandalone::~UnityStandalone ()  void UnityStandalone::Init ()  { - launcher_controller.reset(new launcher::Controller(0)); + launcher_controller.reset(new launcher::Controller());  panel_controller.reset(new panel::Controller());  dash_controller.reset(new dash::Controller()); @@ -119,7 +119,7 @@ UnityStandaloneTV::~UnityStandaloneTV() {};  void UnityStandaloneTV::Init()  { - launcher_controller.reset(new launcher::Controller(0)); + launcher_controller.reset(new launcher::Controller());  dash_controller.reset(new dash::Controller());  dash_controller->launcher_width = launcher_controller->launcher().GetAbsoluteWidth() - 1; | 
