diff options
114 files changed, 5746 insertions, 2806 deletions
| diff --git a/CMakeLists.txt b/CMakeLists.txt index 6dc34c6b3..5332d8f48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,16 +17,13 @@ set (CMAKE_CXX_FLAGS "-DGNOME_DESKTOP_USE_UNSTABLE_API -std=c++0x -fno-permissiv  set (CMAKE_CXX_FLAGS_DEBUG "-g3")  set (CMAKE_CXX_FLAGS_RELEASE "") -if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l") - set (UNITY_STANDALONE_LADD "-lunity-core-${UNITY_API_VERSION} -lm") +if (BUILD_GLES) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNUX_OPENGLES_20 -DUSE_GLES") + set (UNITY_STANDALONE_LADD "-lunity-core-${UNITY_API_VERSION} -lm -lpthread -ldl")  else () - set (UNITY_STANDALONE_LADD "-lunity-core-${UNITY_API_VERSION} -lm -lGL -lGLU") + set (UNITY_STANDALONE_LADD "-lunity-core-${UNITY_API_VERSION} -lm -lpthread -ldl -lGL -lGLU")  endif () -if (BUILD_GLES) -	SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DNUX_OPENGLES_20 -DUSE_GLES") -endif (BUILD_GLES) -  #  # Niceties  # @@ -130,9 +127,7 @@ endif (DISABLE_MAINTAINER_CFLAGS)  #  # Compiz Plugins  # - -set (UNITY_PLUGIN_DEPS "compiz;nux-3.0>=3.0.0;libbamf3;dee-1.0;gio-2.0;gio-unix-2.0;dbusmenu-glib-0.4;x11;libstartup-notification-1.0;gthread-2.0;indicator3-0.4>=0.4.90;atk;unity-misc>=0.4.0;dbus-glib-1;gtk+-3.0>=3.1;sigc++-2.0;json-glib-1.0;libnotify;xfixes;unity-protocol-private>=5.95.1;libgeis") -# FIXME: unity-protocol-private shouldn't be there, but building of unityshell is just broken +set (UNITY_PLUGIN_DEPS "compiz>=0.9.8.0;nux-3.0>=3.0.0;libbamf3;dee-1.0;gio-2.0;gio-unix-2.0;gmodule-2.0;dbusmenu-glib-0.4;x11;libstartup-notification-1.0;gthread-2.0;indicator3-0.4>=0.4.90;atk;atk-bridge-2.0;unity-misc>=0.4.0;dbus-glib-1;gtk+-3.0>=3.1;sigc++-2.0;json-glib-1.0;libnotify;xfixes;unity-protocol-private>=5.95.1;libgeis;xrender>=0.9")  set (UNITY_PROTOCOL_PRIVATE_DEPS "unity-protocol-private>=5.95.1")  find_package (PkgConfig) @@ -7,6 +7,7 @@ If you want to hack on unity you need the following packages  - gthread-2.0  - indicator  - atk + - libatk-adaptor  Or if you are on ubuntu run the command, apt-get build-dep unity diff --git a/UnityCore/CMakeLists.txt b/UnityCore/CMakeLists.txt index bfc526410..f3091a940 100644 --- a/UnityCore/CMakeLists.txt +++ b/UnityCore/CMakeLists.txt @@ -103,7 +103,6 @@ set (CFLAGS  add_definitions (${CFLAGS})  set (LIBS ${CORE_DEPS_LIBRARIES}) -link_libraries (${LIBS})  set (LIB_PATHS ${CORE_DEPS_LIBRARY_DIRS})  link_directories(${LIB_PATHS}) @@ -124,7 +123,9 @@ set (CORE_LIB_LT_AGE 0)  set (CORE_LIB_LT_VERSION "${CORE_LIB_LT_CURRENT}:${CORE_LIB_LT_REV}:${CORE_LIB_LT_AGE}")  set (CORE_LIB_NAME "unity-core-${UNITY_API_VERSION}") +  add_library (${CORE_LIB_NAME} SHARED ${CORE_SOURCES}) +target_link_libraries (${CORE_LIB_NAME} ${LIBS})  set_target_properties(${CORE_LIB_NAME} PROPERTIES  VERSION ${CORE_LIB_LT_CURRENT}.${CORE_LIB_LT_REV}.${CORE_LIB_LT_AGE}  SOVERSION ${CORE_LIB_LT_CURRENT} diff --git a/UnityCore/GLibSource.cpp b/UnityCore/GLibSource.cpp index dbce7cf5b..29de4ed63 100644 --- a/UnityCore/GLibSource.cpp +++ b/UnityCore/GLibSource.cpp @@ -69,13 +69,12 @@ Source::Priority Source::GetPriority() const  return static_cast<Priority>(prio);  } -bool Source::Run(Callback callback) +bool Source::Run(Callback const& callback)  {  if (!source_ || source_id_ || IsRunning())  return false; - callback_ = callback; - callback_data_ = new CallBackData(this); + callback_data_ = new CallBackData(this, callback);  g_source_set_callback(source_, SourceCallback, callback_data_, DestroyCallback);  source_id_ = g_source_attach(source_, nullptr); @@ -101,16 +100,14 @@ gboolean Source::SourceCallback(gpointer data)  if (!data)  return G_SOURCE_REMOVE; - auto self = static_cast<CallBackData*>(data)->self; + auto cb_data = static_cast<CallBackData*>(data); - if (self && self->callback_ && self->callback_()) + if (cb_data && cb_data->callback_fn_ && cb_data->callback_fn_())  {  return G_SOURCE_CONTINUE;  } - else - { - return G_SOURCE_REMOVE; - } + + return G_SOURCE_REMOVE;  }  void Source::DestroyCallback(gpointer data) @@ -133,7 +130,7 @@ void Source::DestroyCallback(gpointer data)  } -Timeout::Timeout(unsigned int milliseconds, Callback cb, Priority prio) +Timeout::Timeout(unsigned int milliseconds, Callback const& cb, Priority prio)  {  Init(milliseconds, prio);  Run(cb); @@ -151,7 +148,7 @@ void Timeout::Init(unsigned int milliseconds, Priority prio)  } -TimeoutSeconds::TimeoutSeconds(unsigned int seconds, Callback cb, Priority prio) +TimeoutSeconds::TimeoutSeconds(unsigned int seconds, Callback const& cb, Priority prio)  {  Init(seconds, prio);  Run(cb); @@ -169,7 +166,7 @@ void TimeoutSeconds::Init(unsigned int seconds, Priority prio)  } -Idle::Idle(Callback cb, Priority prio) +Idle::Idle(Callback const& cb, Priority prio)  {  Init(prio);  Run(cb); @@ -255,7 +252,7 @@ Source::Ptr SourceManager::AddTimeout(unsigned int milliseconds, std::string con  return nullptr;  } -Source::Ptr SourceManager::AddTimeout(unsigned int milliseconds, Source::Callback cb, std::string const& nick) +Source::Ptr SourceManager::AddTimeout(unsigned int milliseconds, Source::Callback const& cb, std::string const& nick)  {  auto timeout = std::make_shared<Timeout>(milliseconds); @@ -280,7 +277,7 @@ Source::Ptr SourceManager::AddTimeoutSeconds(unsigned int seconds, std::string c  return nullptr;  } -Source::Ptr SourceManager::AddTimeoutSeconds(unsigned int seconds, Source::Callback cb, std::string const& nick) +Source::Ptr SourceManager::AddTimeoutSeconds(unsigned int seconds, Source::Callback const& cb, std::string const& nick)  {  auto timeout = std::make_shared<TimeoutSeconds>(seconds); @@ -305,7 +302,7 @@ Source::Ptr SourceManager::AddIdle(std::string const& nick)  return nullptr;  } -Source::Ptr SourceManager::AddIdle(Source::Callback cb, std::string const& nick) +Source::Ptr SourceManager::AddIdle(Source::Callback const& cb, std::string const& nick)  {  auto idle = std::make_shared<Idle>(); diff --git a/UnityCore/GLibSource.h b/UnityCore/GLibSource.h index 468e6b8af..3a09c088c 100644 --- a/UnityCore/GLibSource.h +++ b/UnityCore/GLibSource.h @@ -77,7 +77,7 @@ public:  * This Run a source using the @callback function as Source's callback.  * The method will return false if the source is already running, true otherwise.  */ - bool Run(Callback callback); + bool Run(Callback const& callback);  bool IsRunning() const;  /** @@ -104,11 +104,13 @@ protected:  private:  struct CallBackData  { - CallBackData(Source* src) + CallBackData(Source* src, Callback const& callback)  : self(src) + , callback_fn_(callback)  {}  Source* self; + Callback callback_fn_;  };  static gboolean SourceCallback(gpointer data); @@ -116,7 +118,6 @@ private:  unsigned int source_id_;  CallBackData* callback_data_; - Callback callback_;  }; @@ -133,7 +134,7 @@ class Timeout : public Source  {  public:  Timeout(unsigned int milliseconds, Priority prio = Priority::DEFAULT); - Timeout(unsigned int milliseconds, Callback cb, Priority prio = Priority::DEFAULT); + Timeout(unsigned int milliseconds, Callback const& cb, Priority prio = Priority::DEFAULT);  private:  void Init(unsigned int milliseconds, Priority prio); @@ -153,7 +154,7 @@ class TimeoutSeconds : public Source  {  public:  TimeoutSeconds(unsigned int seconds, Priority prio = Priority::DEFAULT); - TimeoutSeconds(unsigned int seconds, Callback cb, Priority prio = Priority::DEFAULT); + TimeoutSeconds(unsigned int seconds, Callback const& cb, Priority prio = Priority::DEFAULT);  private:  void Init(unsigned int seconds, Priority prio); @@ -173,7 +174,7 @@ class Idle : public Source  {  public:  Idle(Priority prio = Priority::DEFAULT_IDLE); - Idle(Callback cb, Priority prio = Priority::DEFAULT_IDLE); + Idle(Callback const& cb, Priority prio = Priority::DEFAULT_IDLE);  private:  void Init(Priority prio); @@ -206,13 +207,13 @@ public:  bool Add(Source::Ptr const& source, std::string const& nick = "");  Source::Ptr AddTimeout(unsigned int milliseconds, std::string const& nick = ""); - Source::Ptr AddTimeout(unsigned int milliseconds, Source::Callback cb, std::string const& nick = ""); + Source::Ptr AddTimeout(unsigned int milliseconds, Source::Callback const& cb, std::string const& nick = "");  Source::Ptr AddTimeoutSeconds(unsigned int seconds, std::string const& nick = ""); - Source::Ptr AddTimeoutSeconds(unsigned int seconds, Source::Callback cb, std::string const& nick = ""); + Source::Ptr AddTimeoutSeconds(unsigned int seconds, Source::Callback const& cb, std::string const& nick = "");  Source::Ptr AddIdle(std::string const& nick = ""); - Source::Ptr AddIdle(Source::Callback cb, std::string const& nick = ""); + Source::Ptr AddIdle(Source::Callback const& cb, std::string const& nick = "");  bool Remove(std::string const& nick);  bool Remove(unsigned int id); diff --git a/UnityCore/HomeLens.cpp b/UnityCore/HomeLens.cpp index be1f82b0f..da995637f 100644 --- a/UnityCore/HomeLens.cpp +++ b/UnityCore/HomeLens.cpp @@ -15,14 +15,17 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com> + * Michal Hruby <michal.hruby@canonical.com>  */  #include <glib.h> +#include <dee-icu.h>  #include <string>  #include <stdexcept>  #include <map>  #include <set>  #include <utility> +#include <algorithm>  #include "GLibSignal.h"  #include "HomeLens.h" @@ -44,6 +47,8 @@ nux::logging::Logger logger("unity.dash.homelens");  const gchar* const HOMELENS_PRIORITY = "unity-homelens-priority";  const gchar* const HOMELENS_RESULTS_MODEL = "unity-homelens-results-model"; +const unsigned RESULTS_NAME_COLUMN = 4; +const unsigned RESULTS_COMMENT_COLUMN = 5;  }  /* @@ -136,6 +141,21 @@ public:  return target_cat_index;  } + void UnregisterAllForModel(DeeModel* model) + { + auto it = reg_category_map_.begin(); + + // References and iterators to the erased elements are invalidated.  + // Other references and iterators are not affected. + while (it != reg_category_map_.end()) + { + if (it->first.first == model) + reg_category_map_.erase(it++); + else + ++it; + } + } +  void NotifyOrderChanged ()  {  owner_->categories_reordered(); @@ -157,17 +177,18 @@ public:  ModelMerger(glib::Object<DeeModel> target);  virtual ~ModelMerger(); - void AddSource(Lens::Ptr& owner_lens, glib::Object<DeeModel> source); + virtual void AddSource(Lens::Ptr& owner_lens, glib::Object<DeeModel> source); + virtual void RemoveSource(glib::Object<DeeModel> const& old_source);  protected: - virtual void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter); + virtual void OnSourceRowAdded(DeeModel* model, DeeModelIter* iter);  virtual void OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter);  virtual void OnSourceRowChanged(DeeModel* model, DeeModelIter* iter); - void EnsureRowBuf(DeeModel *source); + void EnsureRowBuf(DeeModel* source);  /* The merge tag lives on the source models, pointing to the mapped  * row in the target model */ - DeeModelTag* FindSourceToTargetTag(DeeModel *model); + DeeModelTag* FindSourceToTargetTag(DeeModel* model);  protected:  std::map<Lens::Ptr, glib::Object<DeeModel> > sources_by_owner_; @@ -197,9 +218,9 @@ public:  HomeLens::CategoryRegistry* cat_registry);  protected: - void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter); - void OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter); - void OnSourceRowChanged(DeeModel *model, DeeModelIter *iter); + void OnSourceRowAdded(DeeModel* model, DeeModelIter* iter); + void OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter); + void OnSourceRowChanged(DeeModel* model, DeeModelIter* iter);  private:  HomeLens::CategoryRegistry* cat_registry_; @@ -221,15 +242,22 @@ public:  HomeLens::CategoryRegistry* cat_registry,  MergeMode merge_mode); - void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter); - void OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter); + void OnSourceRowAdded(DeeModel* model, DeeModelIter* iter); + void OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter); - std::vector<unsigned> GetOrder(); + std::vector<unsigned> GetDefaultOrder(); + std::string GetLensIdForCategory(unsigned) const; + std::map<unsigned, Lens::Ptr> const& GetCategoryToLensMap() const; + MergeMode GetMergeMode() const { return merge_mode_; } + +protected: + void RemoveSource(glib::Object<DeeModel> const& old_source);  private:  HomeLens::CategoryRegistry* cat_registry_;  MergeMode merge_mode_;  std::multimap<unsigned, unsigned, std::greater<unsigned> > category_ordering_; + std::map<unsigned, Lens::Ptr> category_to_owner_;  };  /* @@ -241,9 +269,9 @@ class HomeLens::FiltersMerger : public ModelMerger  public:  FiltersMerger(glib::Object<DeeModel> target); - void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter); - void OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter); - void OnSourceRowChanged(DeeModel *model, DeeModelIter *iter); + void OnSourceRowAdded(DeeModel* model, DeeModelIter* iter); + void OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter); + void OnSourceRowChanged(DeeModel* model, DeeModelIter* iter);  };  /* @@ -255,11 +283,53 @@ public:  Impl(HomeLens* owner, MergeMode merge_mode);  ~Impl(); + struct CategorySorter + { + CategorySorter(std::map<unsigned, unsigned>& results_per_category, + std::map<unsigned, Lens::Ptr> const& category_owner_map) + : results_per_category_(results_per_category) + , category_to_owner_(category_owner_map) + {} + + bool operator() (unsigned cat_a, unsigned cat_b) + { + bool a_has_personal_content = false; + bool b_has_personal_content = false; + + auto it = category_to_owner_.find(cat_a); + if (it != category_to_owner_.end() && it->second) + { + a_has_personal_content = it->second->provides_personal_content(); + } + it = category_to_owner_.find(cat_b); + if (it != category_to_owner_.end() && it->second) + { + b_has_personal_content = it->second->provides_personal_content(); + } + + // prioritize categories that have private content + if (a_has_personal_content != b_has_personal_content) + { + return a_has_personal_content ? true : false; + } + + return results_per_category_[cat_a] > results_per_category_[cat_b]; + } + + private: + std::map<unsigned, unsigned>& results_per_category_; + std::map<unsigned, Lens::Ptr> const& category_to_owner_; + }; +  void OnLensAdded(Lens::Ptr& lens);  gsize FindLensPriority (Lens::Ptr& lens);  void EnsureCategoryAnnotation(Lens::Ptr& lens, DeeModel* results, DeeModel* categories);  Lens::Ptr FindLensForUri(std::string const& uri);  std::vector<unsigned> GetCategoriesOrder(); + void LensSearchFinished(Lens::Ptr const& lens); + bool ResultsContainVisibleMatch(unsigned category); + + std::string const& last_search_string() const { return last_search_string_; }  HomeLens* owner_;  Lenses::LensList lenses_; @@ -268,7 +338,11 @@ public:  HomeLens::CategoryMerger categories_merger_;  HomeLens::FiltersMerger filters_merger_;  int running_searches_; + bool apps_lens_contains_visible_match; + std::string last_search_string_;  glib::Object<GSettings> settings_; + std::vector<unsigned> cached_categories_order_; + std::map<unsigned, glib::Object<DeeModel> > category_filter_models_;  };  /* @@ -324,7 +398,7 @@ void HomeLens::ModelMerger::AddSource(Lens::Ptr& owner_lens,  {  if (it->second == source)  return; // this model was already added - sig_manager_.Disconnect(it->second); + RemoveSource(it->second);  }  sources_by_owner_[owner_lens] = source; @@ -344,12 +418,23 @@ void HomeLens::ModelMerger::AddSource(Lens::Ptr& owner_lens,  sigc::mem_fun(this, &HomeLens::ModelMerger::OnSourceRowChanged)));  } -void HomeLens::ModelMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter) +void HomeLens::ModelMerger::RemoveSource(glib::Object<DeeModel> const& source) +{ + if (!source) + { + LOG_ERROR(logger) << "Trying to remove NULL source from ModelMerger"; + return; + } + + sig_manager_.Disconnect(source); +} + +void HomeLens::ModelMerger::OnSourceRowAdded(DeeModel* model, DeeModelIter* iter)  {  // Default impl. does nothing.  } -void HomeLens::ResultsMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter) +void HomeLens::ResultsMerger::OnSourceRowAdded(DeeModel* model, DeeModelIter* iter)  {  DeeModelIter* target_iter;  int target_cat_offset, source_cat_offset; @@ -390,7 +475,7 @@ void HomeLens::ResultsMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *it  for (unsigned int i = 0; i < n_cols_; i++) g_variant_unref(row_buf_[i]);  } -void HomeLens::CategoryMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter) +void HomeLens::CategoryMerger::OnSourceRowAdded(DeeModel* model, DeeModelIter* iter)  {  DeeModel* results_model;  DeeModelIter* target_iter; @@ -413,15 +498,19 @@ void HomeLens::CategoryMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *i  target_tag = FindSourceToTargetTag(model);  unsigned source_cat_offset = dee_model_get_position(model, iter); - if (merge_mode_ == MergeMode::OWNER_LENS) + Lens::Ptr owner_lens; + for (auto it = sources_by_owner_.begin(); it != sources_by_owner_.end(); ++it)  { - for (auto it = sources_by_owner_.begin(); it != sources_by_owner_.end(); ++it) + if (it->second == model)  { - if (it->second == model) - { - lens_name = it->first->name(); - } + owner_lens = it->first; + break;  } + } + + if (merge_mode_ == MergeMode::OWNER_LENS) + { + if (owner_lens) lens_name = owner_lens->name();  display_name = lens_name.c_str();  }  else @@ -458,6 +547,9 @@ void HomeLens::CategoryMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *i  cat_registry_->RegisterCategoryOffset(results_model, source_cat_offset,  display_name); + if (owner_lens) category_to_owner_[target_cat_index] = owner_lens; + + // ensure priorities are taken into account, so default order works  gsize lens_priority = GPOINTER_TO_SIZE(g_object_get_data(  G_OBJECT(model), HOMELENS_PRIORITY));  unsigned lens_prio = static_cast<unsigned>(lens_priority); @@ -470,14 +562,14 @@ void HomeLens::CategoryMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *i  for (unsigned int i = 0; i < n_cols_; i++) g_variant_unref(row_buf_[i]);  } -void HomeLens::FiltersMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter) +void HomeLens::FiltersMerger::OnSourceRowAdded(DeeModel* model, DeeModelIter* iter)  {  /* Supporting filters on the home screen is possible, but *quite* tricky.  * So... Discard ALL the rows!  */  } -void HomeLens::CategoryMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter) +void HomeLens::CategoryMerger::OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter)  {  /* We don't support removals of categories.  * You can check out any time you like, but you can never leave @@ -488,7 +580,7 @@ void HomeLens::CategoryMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter  LOG_DEBUG(logger) << "Removal of categories not supported.";  } -void HomeLens::ModelMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter) +void HomeLens::ModelMerger::OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter)  {  DeeModelIter* target_iter;  DeeModelTag* target_tag; @@ -506,17 +598,17 @@ void HomeLens::ModelMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *it  dee_model_remove(target_, target_iter);  } -void HomeLens::ResultsMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter) +void HomeLens::ResultsMerger::OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter)  {  ModelMerger::OnSourceRowRemoved(model, iter);  } -void HomeLens::FiltersMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter) +void HomeLens::FiltersMerger::OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter)  {  /* We aren't adding any rows to the merged model, so nothing to do here */  } -void HomeLens::ModelMerger::OnSourceRowChanged(DeeModel *model, DeeModelIter *iter) +void HomeLens::ModelMerger::OnSourceRowChanged(DeeModel* model, DeeModelIter* iter)  {  DeeModelIter* target_iter;  DeeModelTag* target_tag; @@ -534,18 +626,18 @@ void HomeLens::ModelMerger::OnSourceRowChanged(DeeModel *model, DeeModelIter *it  for (unsigned int i = 0; i < n_cols_; i++) g_variant_unref(row_buf_[i]);  } -void HomeLens::ResultsMerger::OnSourceRowChanged(DeeModel *model, DeeModelIter *iter) +void HomeLens::ResultsMerger::OnSourceRowChanged(DeeModel* model, DeeModelIter* iter)  {  // FIXME: We can support this, but we need to re-calculate the category offset  LOG_WARN(logger) << "In-line changing of results not supported in the home lens. Sorry.";  } -void HomeLens::FiltersMerger::OnSourceRowChanged(DeeModel *model, DeeModelIter *iter) +void HomeLens::FiltersMerger::OnSourceRowChanged(DeeModel* model, DeeModelIter* iter)  {  /* We aren't adding any rows to the merged model, so nothing to do here */  } -void HomeLens::ModelMerger::EnsureRowBuf(DeeModel *model) +void HomeLens::ModelMerger::EnsureRowBuf(DeeModel* model)  {  if (G_UNLIKELY (n_cols_ == 0))  { @@ -606,12 +698,20 @@ void HomeLens::ModelMerger::EnsureRowBuf(DeeModel *model)  }  } -DeeModelTag* HomeLens::ModelMerger::FindSourceToTargetTag(DeeModel *model) +DeeModelTag* HomeLens::ModelMerger::FindSourceToTargetTag(DeeModel* model)  {  return source_to_target_tags_[model];  } -std::vector<unsigned> HomeLens::CategoryMerger::GetOrder() +void HomeLens::CategoryMerger::RemoveSource(glib::Object<DeeModel> const& source) +{ + // call base() + HomeLens::ModelMerger::RemoveSource(source); + + cat_registry_->UnregisterAllForModel(source); +} + +std::vector<unsigned> HomeLens::CategoryMerger::GetDefaultOrder()  {  std::vector<unsigned> result;  for (auto it = category_ordering_.begin(); it != category_ordering_.end(); ++it) @@ -622,6 +722,23 @@ std::vector<unsigned> HomeLens::CategoryMerger::GetOrder()  return result;  } +std::string HomeLens::CategoryMerger::GetLensIdForCategory(unsigned cat) const +{ + auto lens_it = category_to_owner_.find(cat); + if (lens_it != category_to_owner_.end()) + { + if (lens_it->second) return lens_it->second->id(); + } + + return ""; +} + +std::map<unsigned, Lens::Ptr> const& +HomeLens::CategoryMerger::GetCategoryToLensMap() const +{ + return category_to_owner_; +} +  HomeLens::Impl::Impl(HomeLens *owner, MergeMode merge_mode)  : owner_(owner)  , cat_registry_(owner) @@ -629,6 +746,7 @@ HomeLens::Impl::Impl(HomeLens *owner, MergeMode merge_mode)  , categories_merger_(owner->categories()->model(), &cat_registry_, merge_mode)  , filters_merger_(owner->filters()->model())  , running_searches_(0) + , apps_lens_contains_visible_match(false)  , settings_(g_settings_new("com.canonical.Unity.Dash"))  {  DeeModel* results = owner->results()->model(); @@ -743,9 +861,10 @@ void HomeLens::Impl::OnLensAdded (Lens::Ptr& lens)  /* When we dispatch a search we inc the search count and when we finish  * one we decrease it. When we reach 0 we'll emit search_finished. */ - lens->global_search_finished.connect([&] (Hints const& hints) { + lens->global_search_finished.connect([this, lens] (Hints const& hints) {  running_searches_--; + LensSearchFinished(lens);  if (running_searches_ <= 0)  {  owner_->search_finished.emit(Hints()); @@ -784,41 +903,15 @@ void HomeLens::Impl::OnLensAdded (Lens::Ptr& lens)  filters_merger_.AddSource(lens, filters_prop());  } - /* - * We'll assume that the models' swarm names do not change during life cycle - * of a lens. - * Otherwise we might run into a race where we would associate category - * model to a results model that is about to be replaced by a new one. - */ - lens->connected.changed.connect([&] (bool is_connected) - { - if (is_connected) - { - EnsureCategoryAnnotation(lens, lens->categories()->model(), - lens->global_results()->model()); - categories_merger_.AddSource(lens, lens->categories()->model()); - results_merger_.AddSource(lens, lens->global_results()->model()); - filters_merger_.AddSource(lens, lens->filters()->model()); - } - }); - /* - results_prop.changed.connect([&] (glib::Object<DeeModel> model) - { - EnsureCategoryAnnotation(lens, lens->categories()->model(), model); - results_merger_.AddSource(model); - }); - - categories_prop.changed.connect([&] (glib::Object<DeeModel> model) - { - EnsureCategoryAnnotation(lens, model, lens->global_results()->model()); - categories_merger_.AddSource(model); - }); - - filters_prop.changed.connect([&] (glib::Object<DeeModel> model) + /* Make sure the models are properly annotated when they change */ + lens->models_changed.connect([&] ()  { - filters_merger_.AddSource(model); + EnsureCategoryAnnotation(lens, lens->categories()->model(), + lens->global_results()->model()); + categories_merger_.AddSource(lens, lens->categories()->model()); + results_merger_.AddSource(lens, lens->global_results()->model()); + filters_merger_.AddSource(lens, lens->filters()->model());  }); - */  /*  * Register pre-existing categories up front @@ -838,9 +931,175 @@ void HomeLens::Impl::OnLensAdded (Lens::Ptr& lens)  }  } +void HomeLens::Impl::LensSearchFinished(Lens::Ptr const& lens) +{ + auto order_vector = categories_merger_.GetDefaultOrder(); + + // get number of results per category + std::map<unsigned, unsigned> results_per_cat; + for (unsigned i = 0; i < order_vector.size(); i++) + { + unsigned category = order_vector.at(i); + auto model = owner_->GetFilterModelForCategory(category); + results_per_cat[category] = model ? dee_model_get_n_rows(model) : 0; + } + + CategorySorter sorter(results_per_cat, + categories_merger_.GetCategoryToLensMap()); + // stable sort based on number of results in each cat + std::stable_sort(order_vector.begin(), order_vector.end(), sorter); + + // ensure shopping is second, need map[cat] = lens + int shopping_index = -1; + int apps_index = -1; + for (unsigned i = 0; i < order_vector.size(); i++) + { + // get lens that owns this category + std::string const& lens_id(categories_merger_.GetLensIdForCategory(order_vector.at(i))); + if (lens_id == "shopping.lens") + shopping_index = i; + else if (lens_id == "applications.lens") + apps_index = i; + } + + if (lens->id() == "applications.lens") + { + // checking the results isn't extermely fast, so cache the result + apps_lens_contains_visible_match = ResultsContainVisibleMatch(order_vector[apps_index]); + } + + // if there are no results in the apps category, we can't reorder, + // otherwise shopping won't end up being 2nd + if (apps_lens_contains_visible_match && apps_index > 0 &&  + results_per_cat[order_vector[apps_index]] > 0) + { + // we want apps first + unsigned apps_cat_num = order_vector.at(apps_index); + order_vector.erase(order_vector.begin() + apps_index); + order_vector.insert(order_vector.begin(), apps_cat_num); + + // we might shift the shopping index + if (shopping_index >= 0 && shopping_index < apps_index) shopping_index++; + } + + if (shopping_index >= 0 && shopping_index != 2) + { + unsigned shopping_cat_num = order_vector.at(shopping_index); + order_vector.erase(order_vector.begin() + shopping_index); + order_vector.insert(order_vector.begin() + 2, shopping_cat_num); + } + + if (cached_categories_order_ != order_vector) + { + cached_categories_order_ = order_vector; + owner_->categories_reordered(); + } +} + +bool HomeLens::Impl::ResultsContainVisibleMatch(unsigned category) +{ + // this method searches for match of the search string in the display name + // or comment fields + auto filter_model = owner_->GetFilterModelForCategory(category); + if (!filter_model) return false; + if (last_search_string_.empty()) return true; + + int checked_results = 5; + + glib::Object<DeeModel> model(dee_sequence_model_new()); + dee_model_set_schema(model, "s", "s", NULL); + + DeeModelIter* iter = dee_model_get_first_iter(filter_model); + DeeModelIter* end_iter = dee_model_get_last_iter(filter_model); + + // add first few results to the temporary model + while (iter != end_iter) + { + glib::Variant name(dee_model_get_value(filter_model, iter, RESULTS_NAME_COLUMN), + glib::StealRef()); + glib::Variant comment(dee_model_get_value(filter_model, iter, RESULTS_COMMENT_COLUMN), + glib::StealRef()); + GVariant* members[2] = { name, comment }; + dee_model_append_row(model, members); + + iter = dee_model_next(filter_model, iter); + if (--checked_results <= 0) break; + } + + if (dee_model_get_n_rows(model) == 0) return false; + + // setup model reader, analyzer and instantiate an index + DeeModelReader reader; + dee_model_reader_new([] (DeeModel* m, DeeModelIter* iter, gpointer data) -> gchar* + { + return g_strdup_printf("%s\n%s", + dee_model_get_string(m, iter, 0), + dee_model_get_string(m, iter, 1)); + }, NULL, NULL, &reader); + glib::Object<DeeAnalyzer> analyzer(DEE_ANALYZER(dee_text_analyzer_new())); + dee_analyzer_add_term_filter(analyzer, + [] (DeeTermList* terms_in, DeeTermList* terms_out, gpointer data) -> void + { + auto filter = static_cast<DeeICUTermFilter*>(data); + for (unsigned i = 0; i < dee_term_list_num_terms(terms_in); i++) + { + dee_term_list_add_term(terms_out, dee_icu_term_filter_apply(filter, dee_term_list_get_term(terms_in, i))); + } + }, + dee_icu_term_filter_new_ascii_folder(), + (GDestroyNotify)dee_icu_term_filter_destroy); + // ready to instantiate the index + glib::Object<DeeIndex> index(DEE_INDEX(dee_tree_index_new(model, analyzer, &reader))); + + // tokenize the search string, so this will work with multiple words + glib::Object<DeeTermList> search_terms(DEE_TERM_LIST(g_object_new(DEE_TYPE_TERM_LIST, NULL))); + dee_analyzer_tokenize(analyzer, last_search_string_.c_str(), search_terms); + + std::set<DeeModelIter*> iters; + for (unsigned i = 0; i < dee_term_list_num_terms(search_terms); i++) + { + glib::Object<DeeResultSet> results(dee_index_lookup(index, dee_term_list_get_term(search_terms, i), DEE_TERM_MATCH_PREFIX)); + if (i == 0) + { + while (dee_result_set_has_next(results)) + { + iters.insert(dee_result_set_next(results)); + } + } + else + { + std::set<DeeModelIter*> iters2; + while (dee_result_set_has_next(results)) + { + iters2.insert(dee_result_set_next(results)); + } + // intersect the sets, set iterators are stable, so we can do this + auto it = iters.begin(); + while (it != iters.end()) + { + if (iters2.find(*it) == iters2.end()) + iters.erase(it++); + else + ++it; + } + // no need to check more terms if the base set is already empty + if (iters.empty()) break; + } + } + + // there is a match if the iterator is isn't empty + return !iters.empty(); +} +  std::vector<unsigned> HomeLens::Impl::GetCategoriesOrder()  { - return categories_merger_.GetOrder(); + auto default_order = categories_merger_.GetDefaultOrder(); + if (!last_search_string_.empty() && + cached_categories_order_.size() == default_order.size()) + { + return cached_categories_order_; + } + return default_order;  }  HomeLens::HomeLens(std::string const& name, @@ -853,6 +1112,9 @@ HomeLens::HomeLens(std::string const& name,  , pimpl(new Impl(this, merge_mode))  {  count.SetGetterFunction(sigc::mem_fun(&pimpl->lenses_, &Lenses::LensList::size)); + last_search_string.SetGetterFunction(sigc::mem_fun(pimpl, &HomeLens::Impl::last_search_string)); + last_global_search_string.SetGetterFunction(sigc::mem_fun(pimpl, &HomeLens::Impl::last_search_string)); +  search_in_global = false;  } @@ -915,6 +1177,8 @@ void HomeLens::Search(std::string const& search_string)  /* Reset running search counter */  pimpl->running_searches_ = 0; + pimpl->last_search_string_ = search_string; + pimpl->apps_lens_contains_visible_match = false;  for (auto lens: pimpl->lenses_)  { @@ -967,5 +1231,15 @@ std::vector<unsigned> HomeLens::GetCategoriesOrder()  return pimpl->GetCategoriesOrder();  } +glib::Object<DeeModel> HomeLens::GetFilterModelForCategory(unsigned category) +{ + auto it = pimpl->category_filter_models_.find(category); + if (it != pimpl->category_filter_models_.end()) return it->second; + + auto model = Lens::GetFilterModelForCategory(category); + pimpl->category_filter_models_[category] = model; + return model; +} +  }  } diff --git a/UnityCore/HomeLens.h b/UnityCore/HomeLens.h index f92ee70a6..0facd0fa1 100644 --- a/UnityCore/HomeLens.h +++ b/UnityCore/HomeLens.h @@ -75,6 +75,7 @@ public:  void Preview(std::string const& uri);  std::vector<unsigned> GetCategoriesOrder(); + glib::Object<DeeModel> GetFilterModelForCategory(unsigned category);  private:  class Impl; diff --git a/UnityCore/Lens.cpp b/UnityCore/Lens.cpp index d0c3a0e53..a4e0c5b0d 100644 --- a/UnityCore/Lens.cpp +++ b/UnityCore/Lens.cpp @@ -36,6 +36,8 @@ namespace dash  namespace  {  nux::logging::Logger logger("unity.dash.lens"); + +const unsigned CATEGORY_COLUMN = 2;  }  using std::string; @@ -77,7 +79,8 @@ public:  string const& results_model_name,  string const& global_results_model_name,  string const& categories_model_name, - string const& filters_model_name); + string const& filters_model_name, + GVariantIter* hints_iter);  void OnViewTypeChanged(ViewType view_type);  void GlobalSearch(std::string const& search_string); @@ -86,11 +89,14 @@ public:  void ActivationReply(GVariant* parameters);  void Preview(std::string const& uri);  void ActivatePreviewAction(std::string const& action_id, - std::string const& uri); + std::string const& uri, + Hints const& hints);  void SignalPreview(std::string const& preview_uri,  glib::Variant const& preview_update,  glib::DBusProxy::ReplyCallback reply_cb);  std::vector<unsigned> GetCategoriesOrder(); + glib::Object<DeeModel> GetFilterModelForCategory(unsigned category); + void GetFilterForCategoryIndex(unsigned category, DeeFilter* filter);  string const& id() const;  string const& dbus_name() const; @@ -108,6 +114,10 @@ public:  Categories::Ptr const& categories() const;  Filters::Ptr const& filters() const;  bool connected() const; + bool provides_personal_content() const; + + string const& last_search_string() const { return last_search_string_; } + string const& last_global_search_string() const { return last_global_search_string_; }  Lens* owner_; @@ -126,16 +136,19 @@ public:  Categories::Ptr categories_;  Filters::Ptr filters_;  bool connected_; + bool provides_personal_content_;  string private_connection_name_; + string last_search_string_; + string last_global_search_string_;  glib::DBusProxy* proxy_;  glib::Object<GCancellable> search_cancellable_;  glib::Object<GCancellable> global_search_cancellable_;  glib::Object<GCancellable> preview_cancellable_; - GVariant *results_variant_; - GVariant *global_results_variant_; + glib::Variant results_variant_; + glib::Variant global_results_variant_;  };  Lens::Impl::Impl(Lens* owner, @@ -165,9 +178,8 @@ Lens::Impl::Impl(Lens* owner,  , categories_(new Categories(model_type))  , filters_(new Filters(model_type))  , connected_(false) + , provides_personal_content_(false)  , proxy_(NULL) - , results_variant_(NULL) - , global_results_variant_(NULL)  {  if (model_type == ModelType::REMOTE)  { @@ -199,6 +211,9 @@ Lens::Impl::Impl(Lens* owner,  owner_->categories.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::categories));  owner_->filters.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::filters));  owner_->connected.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::connected)); + owner_->provides_personal_content.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::provides_personal_content)); + owner_->last_search_string.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::last_search_string)); + owner_->last_global_search_string.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::last_global_search_string));  owner_->view_type.changed.connect(sigc::mem_fun(this, &Lens::Impl::OnViewTypeChanged));  } @@ -236,13 +251,11 @@ void Lens::Impl::OnProxyDisconnected()  void Lens::Impl::ResultsModelUpdated(unsigned long long begin_seqnum,  unsigned long long end_seqnum)  { - if (results_variant_ != NULL && - end_seqnum >= ExtractModelSeqnum (results_variant_)) + if (results_variant_ && end_seqnum >= ExtractModelSeqnum (results_variant_))  { - glib::Variant dict(results_variant_, glib::StealRef());  Hints hints; - dict.ASVToHints(hints); + results_variant_.ASVToHints(hints);  owner_->search_finished.emit(hints); @@ -253,13 +266,12 @@ void Lens::Impl::ResultsModelUpdated(unsigned long long begin_seqnum,  void Lens::Impl::GlobalResultsModelUpdated(unsigned long long begin_seqnum,  unsigned long long end_seqnum)  { - if (global_results_variant_ != NULL && + if (global_results_variant_ &&  end_seqnum >= ExtractModelSeqnum (global_results_variant_))  { - glib::Variant dict(global_results_variant_, glib::StealRef());  Hints hints; - dict.ASVToHints(hints); + global_results_variant_.ASVToHints(hints);  owner_->global_search_finished.emit(hints); @@ -296,8 +308,7 @@ void Lens::Impl::OnSearchFinished(GVariant* parameters)  if (results_->seqnum < reply_seqnum)  {  // wait for the end-transaction signal - if (results_variant_) g_variant_unref (results_variant_); - results_variant_ = g_variant_ref (parameters); + results_variant_ = parameters;  // ResultsModelUpdated will emit OnSearchFinished  return; @@ -318,8 +329,7 @@ void Lens::Impl::OnGlobalSearchFinished(GVariant* parameters)  if (global_results_->seqnum < reply_seqnum)  {  // wait for the end-transaction signal - if (global_results_variant_) g_variant_unref (global_results_variant_); - global_results_variant_ = g_variant_ref (parameters); + global_results_variant_ = parameters;  // GlobalResultsModelUpdated will emit OnGlobalSearchFinished  return; @@ -367,15 +377,28 @@ void Lens::Impl::OnChanged(GVariant* parameters)  << " Filters: " << filters_model_name << "\n";  if (dbus_path.Str() == dbus_path_)  { + std::string categories_model_swarm_name(categories_model_name.Str()); + std::string filters_model_swarm_name(filters_model_name.Str()); + std::string results_model_swarm_name(results_model_name.Str()); + std::string global_results_model_swarm_name(global_results_model_name.Str()); + bool models_changed =  + categories_model_swarm_name != categories_->swarm_name || + filters_model_swarm_name != filters_->swarm_name || + results_model_swarm_name != results_->swarm_name || + global_results_model_swarm_name != global_results_->swarm_name; +  /* FIXME: We ignore hints for now */  UpdateProperties(search_in_global,  visible,  search_hint.Str(),  private_connection_name.Str(), - results_model_name.Str(), - global_results_model_name.Str(), - categories_model_name.Str(), - filters_model_name.Str()); + results_model_swarm_name, + global_results_model_swarm_name, + categories_model_swarm_name, + filters_model_swarm_name, + hints_iter); + + if (models_changed) owner_->models_changed.emit();  }  else  { @@ -398,7 +421,8 @@ void Lens::Impl::UpdateProperties(bool search_in_global,  string const& results_model_name,  string const& global_results_model_name,  string const& categories_model_name, - string const& filters_model_name) + string const& filters_model_name, + GVariantIter* hints_iter)  {  // Diff the properties received from those we have  if (search_hint_ != search_hint) @@ -419,6 +443,26 @@ void Lens::Impl::UpdateProperties(bool search_in_global,  owner_->visible.EmitChanged(visible_);  } + bool provides_personal_content = false; + gchar* key; + GVariant* value; + + // g_variant_iter_loop manages the memory automatically, as long + // as the iteration is not stopped in the middle + while (g_variant_iter_loop(hints_iter, "{sv}", &key, &value)) + { + if (g_strcmp0(key, "provides-personal-content") == 0) + { + provides_personal_content = g_variant_get_boolean(value) != FALSE; + } + } + + if (provides_personal_content_ != provides_personal_content) + { + provides_personal_content_ = provides_personal_content; + owner_->provides_personal_content.EmitChanged(provides_personal_content_); + } +  if (private_connection_name_ != private_connection_name)  {  // FIXME: Update all the models as they are no longer valid when we use this @@ -447,11 +491,8 @@ void Lens::Impl::GlobalSearch(std::string const& search_string)  g_cancellable_cancel (global_search_cancellable_);  global_search_cancellable_ = g_cancellable_new (); - if (global_results_variant_) - { - g_variant_unref (global_results_variant_); - global_results_variant_ = NULL; - } + global_results_variant_ = NULL; + last_global_search_string_ = search_string;  proxy_->Call("GlobalSearch",  g_variant_new("(sa{sv})", @@ -459,6 +500,7 @@ void Lens::Impl::GlobalSearch(std::string const& search_string)  &b),  sigc::mem_fun(this, &Lens::Impl::OnGlobalSearchFinished),  global_search_cancellable_); +  g_variant_builder_clear(&b);  } @@ -478,11 +520,8 @@ void Lens::Impl::Search(std::string const& search_string)  if (search_cancellable_) g_cancellable_cancel (search_cancellable_);  search_cancellable_ = g_cancellable_new (); - if (results_variant_) - { - g_variant_unref (results_variant_); - results_variant_ = NULL; - } + results_variant_ = NULL; + last_search_string_ = search_string;  proxy_->Call("Search",  g_variant_new("(sa{sv})", @@ -571,7 +610,8 @@ void Lens::Impl::Preview(std::string const& uri)  }  void Lens::Impl::ActivatePreviewAction(std::string const& action_id, - std::string const& uri) + std::string const& uri, + Hints const& hints)  {  LOG_DEBUG(logger) << "Activating action '" << action_id << "' on '" << id_ << "'"; @@ -585,10 +625,35 @@ void Lens::Impl::ActivatePreviewAction(std::string const& action_id,  activation_uri += ":";  activation_uri += uri; - proxy_->Call("Activate", - g_variant_new("(su)", activation_uri.c_str(), - UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_ACTION), - sigc::mem_fun(this, &Lens::Impl::ActivationReply)); + if (hints.empty()) + { + // FIXME: we should really be using the (sua{sv}) method all the time, + // but we're past freezes and having to logout and back in is + // too big of a deal after beta freeze + proxy_->Call("Activate", + g_variant_new("(su)", activation_uri.c_str(), + UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_ACTION), + sigc::mem_fun(this, &Lens::Impl::ActivationReply)); + } + else + { + GVariantBuilder b; + g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}")); + + for (auto it = hints.begin(); it != hints.end(); ++it) + { + GVariant* variant = it->second; + g_variant_builder_add(&b, "{sv}", it->first.c_str(), variant); + } + + proxy_->Call("ActivateWithHints", + g_variant_new("(sua{sv})", activation_uri.c_str(), + UNITY_PROTOCOL_ACTION_TYPE_PREVIEW_ACTION, + &b), + sigc::mem_fun(this, &Lens::Impl::ActivationReply)); + + g_variant_builder_clear(&b); + }  }  void Lens::Impl::SignalPreview(std::string const& preview_uri, @@ -621,6 +686,61 @@ std::vector<unsigned> Lens::Impl::GetCategoriesOrder()  return result;  } +glib::Object<DeeModel> Lens::Impl::GetFilterModelForCategory(unsigned category) +{ + DeeFilter filter; + GetFilterForCategoryIndex(category, &filter); + glib::Object<DeeModel> filter_model(dee_filter_model_new(results_->model(), &filter)); + + return filter_model; +} + +static void category_filter_map_func (DeeModel* orig_model, + DeeFilterModel* filter_model, + gpointer user_data) +{ + DeeModelIter* iter; + DeeModelIter* end; + unsigned index = GPOINTER_TO_UINT(user_data); + + iter = dee_model_get_first_iter(orig_model); + end = dee_model_get_last_iter(orig_model); + while (iter != end) + { + unsigned category_index = dee_model_get_uint32(orig_model, iter, + CATEGORY_COLUMN); + if (index == category_index) + { + dee_filter_model_append_iter(filter_model, iter); + } + iter = dee_model_next(orig_model, iter); + } +} + +static gboolean category_filter_notify_func (DeeModel* orig_model, + DeeModelIter* orig_iter, + DeeFilterModel* filter_model, + gpointer user_data) +{ + unsigned index = GPOINTER_TO_UINT(user_data); + unsigned category_index = dee_model_get_uint32(orig_model, orig_iter, + CATEGORY_COLUMN); + + if (index != category_index) + return FALSE; + + dee_filter_model_insert_iter_with_original_order(filter_model, orig_iter); + return TRUE; +} + +void Lens::Impl::GetFilterForCategoryIndex(unsigned category, DeeFilter* filter) +{ + filter->map_func = category_filter_map_func; + filter->map_notify = category_filter_notify_func; + filter->destroy = nullptr; + filter->userdata = GUINT_TO_POINTER(category); +} +  string const& Lens::Impl::id() const  {  return id_; @@ -707,6 +827,11 @@ bool Lens::Impl::connected() const  return connected_;  } +bool Lens::Impl::provides_personal_content() const +{ + return provides_personal_content_; +} +  Lens::Lens(string const& id_,  string const& dbus_name_,  string const& dbus_path_, @@ -780,9 +905,10 @@ void Lens::Preview(std::string const& uri)  }  void Lens::ActivatePreviewAction(std::string const& action_id, - std::string const& uri) + std::string const& uri, + Hints const& hints)  { - pimpl->ActivatePreviewAction(action_id, uri); + pimpl->ActivatePreviewAction(action_id, uri, hints);  }  void Lens::SignalPreview(std::string const& uri, @@ -797,6 +923,11 @@ std::vector<unsigned> Lens::GetCategoriesOrder()  return pimpl->GetCategoriesOrder();  } +glib::Object<DeeModel> Lens::GetFilterModelForCategory(unsigned category) +{ + return pimpl->GetFilterModelForCategory(category); +} +  }  } diff --git a/UnityCore/Lens.h b/UnityCore/Lens.h index 71a5bd0e7..08a448107 100644 --- a/UnityCore/Lens.h +++ b/UnityCore/Lens.h @@ -86,10 +86,18 @@ public:  virtual void Activate(std::string const& uri);  virtual void Preview(std::string const& uri);  virtual void ActivatePreviewAction(std::string const& action_id, - std::string const& uri); + std::string const& uri, + Hints const& hints);  virtual void SignalPreview(std::string const& uri,  glib::Variant const& preview_update,  glib::DBusProxy::ReplyCallback reply_cb = nullptr); + + /** + * Note that this model is only valid for as long as the results model + * doesn't change. + * (you should call this again after models_changed is emitted) + */ + virtual glib::Object<DeeModel> GetFilterModelForCategory(unsigned category);  virtual std::vector<unsigned> GetCategoriesOrder();  nux::RWProperty<std::string> id; @@ -107,10 +115,16 @@ public:  nux::RWProperty<Categories::Ptr> categories;  nux::RWProperty<Filters::Ptr> filters;  nux::RWProperty<bool> connected; + nux::RWProperty<bool> provides_personal_content; + nux::ROProperty<std::string> last_search_string; + nux::ROProperty<std::string> last_global_search_string;  nux::Property<ViewType> view_type;  sigc::signal<void> categories_reordered; + /* Emitted when any of the models' swarm name changes, but collates the name + * changes into a single signal emission (when all are changed) */ + sigc::signal<void> models_changed;  sigc::signal<void, Hints const&> search_finished;  sigc::signal<void, Hints const&> global_search_finished;  sigc::signal<void, std::string const&, HandledType, Hints const&> activated; diff --git a/UnityCore/Preview.cpp b/UnityCore/Preview.cpp index 5d9b37d99..6f8b6bfff 100644 --- a/UnityCore/Preview.cpp +++ b/UnityCore/Preview.cpp @@ -262,11 +262,11 @@ void Preview::Update(glib::Variant const& properties,  }  } -void Preview::PerformAction(std::string const& id) const +void Preview::PerformAction(std::string const& id, Lens::Hints const& hints) const  {  if (pimpl->parent_lens_)  { - pimpl->parent_lens_->ActivatePreviewAction(id, preview_uri); + pimpl->parent_lens_->ActivatePreviewAction(id, preview_uri, hints);  }  else  { diff --git a/UnityCore/Preview.h b/UnityCore/Preview.h index 1bb1bbc32..1b43b1033 100644 --- a/UnityCore/Preview.h +++ b/UnityCore/Preview.h @@ -112,7 +112,9 @@ public:  ActionPtrList GetActions() const;  InfoHintPtrList GetInfoHints() const; - void PerformAction(std::string const& id) const; + void PerformAction(std::string const& id, + std::map<std::string, glib::Variant> const& hints = + std::map<std::string, glib::Variant>()) const;  void EmitClosed() const;  protected: diff --git a/UnityCore/Variant.cpp b/UnityCore/Variant.cpp index f1aff32b2..21507d001 100644 --- a/UnityCore/Variant.cpp +++ b/UnityCore/Variant.cpp @@ -97,8 +97,8 @@ bool Variant::ASVToHints(HintsMap& hints) const  Variant& Variant::operator=(GVariant* val)  { - if (variant_) g_variant_unref (variant_); - variant_ = g_variant_ref_sink (val); + if (variant_) g_variant_unref(variant_); + variant_ = val ? g_variant_ref_sink(val) : val;  return *this;  } @@ -108,6 +108,11 @@ Variant::operator GVariant* () const  return variant_;  } +Variant::operator bool() const +{ + return bool(variant_); +} +  } // namespace glib  namespace variant diff --git a/UnityCore/Variant.h b/UnityCore/Variant.h index b9ee024dc..a851a060d 100644 --- a/UnityCore/Variant.h +++ b/UnityCore/Variant.h @@ -55,6 +55,8 @@ public:  Variant& operator=(GVariant*);  operator GVariant*() const; + operator bool() const; +  private:  GVariant* variant_;  }; diff --git a/com.canonical.Unity.gschema.xml b/com.canonical.Unity.gschema.xml index 501cbebd5..7656110a5 100644 --- a/com.canonical.Unity.gschema.xml +++ b/com.canonical.Unity.gschema.xml @@ -21,6 +21,26 @@  <summary>Whether the home screen should be expanded.</summary>  <description>Whether the home screen should be expanded.</description>  </key> + <key type="i" name="minimize-count"> + <default>0</default> + <summary>Number of times a normal window has been minimized.</summary> + <description>This is used to adjust the animation speed by making it progressively faster the more it is used.</description> + </key> + <key type="i" name="minimize-fast-duration"> + <default>300</default> + <summary>The duration for the minimize animation when it is at its fastest setting.</summary> + <description>This is the duration that the minimize animation will have when it has been used more than an number of times equal minimize-speed-threshold.</description> + </key> + <key type="i" name="minimize-slow-duration"> + <default>800</default> + <summary>The duration for the minimize animation when it is at its slowest setting.</summary> + <description>This is the duration that the minimize animation will have when it has never been used.</description> + </key> + <key type="i" name="minimize-speed-threshold"> + <default>100</default> + <summary>The number of minimizations required to reach maximum speed.</summary> + <description>The speed of the minimize animation will progressively get faster as minimize-count approaches this value.</description> + </key>  </schema>  <schema path="/com/canonical/unity/launcher/" id="com.canonical.Unity.Launcher" gettext-domain="unity">  <key type="as" name="favorites"> @@ -42,10 +62,10 @@  </key>  </schema>  <schema path="/com/canonical/unity/devices/" id="com.canonical.Unity.Devices" gettext-domain="unity"> - <key type="as" name="favorites"> + <key type="as" name="blacklist">  <default>[]</default> - <summary>List of device uuid for favorites on the launcher.</summary> - <description>These devices are shown in the Launcher by default.</description> + <summary>List of device uuid blacklist from the launcher.</summary> + <description>These devices are not shown in the launcher by default.</description>  </key>  </schema>  <schema path="/com/canonical/unity/dash/" id="com.canonical.Unity.Dash" gettext-domain="unity"> diff --git a/dash/DashView.cpp b/dash/DashView.cpp index fb6bde6dd..65bd73e50 100644 --- a/dash/DashView.cpp +++ b/dash/DashView.cpp @@ -247,6 +247,12 @@ void DashView::AboutToHide()  home_lens_->view_type = ViewType::HIDDEN;  LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HIDDEN  << " on '" << home_lens_->id() << "'"; + + // if a preview is open, close it + if (preview_displaying_)  + { + ClosePreview(); + }  }  void DashView::SetupViews() diff --git a/dash/LensView.cpp b/dash/LensView.cpp index 16128abc1..2e8ab0903 100755 --- a/dash/LensView.cpp +++ b/dash/LensView.cpp @@ -45,8 +45,6 @@ nux::logging::Logger logger("unity.dash.lensview");  const int CARD_VIEW_GAP_VERT = 24; // pixels  const int CARD_VIEW_GAP_HORIZ = 25; // pixels - -const unsigned CATEGORY_COLUMN = 2;  }  // This is so we can access some protected members in scrollview. @@ -248,11 +246,8 @@ void LensView::SetupResults()  {  for (unsigned int i = 0; i < categories_.size(); ++i)  { - PlacesGroup* group = categories_[i]; - ResultViewGrid* grid = static_cast<ResultViewGrid*>(group->GetChildView()); - DeeFilter filter; - GetFilterForCategoryIndex(i, &filter); - glib::Object<DeeModel> filter_model(dee_filter_model_new(model, &filter)); + ResultViewGrid* grid = GetGridForCategory(i); + glib::Object<DeeModel> filter_model(lens_->GetFilterModelForCategory(i));  Results::Ptr results_model = lens_->results;  grid->SetModel(filter_model, results_model->GetTag());  } @@ -310,7 +305,7 @@ void LensView::OnCategoryAdded(Category const& category)  counts_[group] = 0;  ResultView* grid; -  +  if (renderer_name == "tile-horizontal")  {  grid = new ResultViewGrid(NUX_TRACKER_LOCATION); @@ -318,12 +313,12 @@ void LensView::OnCategoryAdded(Category const& category)  static_cast<ResultViewGrid*> (grid)->horizontal_spacing = CARD_VIEW_GAP_HORIZ;  static_cast<ResultViewGrid*> (grid)->vertical_spacing = CARD_VIEW_GAP_VERT;  } - else if (renderer_name == "flow") + else if (renderer_name == "flow" && nux::GetWindowThread()->GetGraphicsEngine().UsingGLSLCodePath())  {  grid = new CoverflowResultView(NUX_TRACKER_LOCATION);  grid->SetModelRenderer(new ResultRendererTile(NUX_TRACKER_LOCATION));  group->SetHeaderCountVisible(false); - }  + }  else  {  grid = new ResultViewGrid(NUX_TRACKER_LOCATION); @@ -360,9 +355,7 @@ void LensView::OnCategoryAdded(Category const& category)  Results::Ptr results_model = lens_->results;  if (results_model->model())  { - DeeFilter filter; - GetFilterForCategoryIndex(index, &filter); - glib::Object<DeeModel> filter_model(dee_filter_model_new(results_model->model(), &filter)); + glib::Object<DeeModel> filter_model(lens_->GetFilterModelForCategory(index));  grid->SetModel(filter_model, results_model->GetTag());  } @@ -422,62 +415,13 @@ void LensView::OnCategoryOrderChanged()  }  } -static void category_filter_map_func (DeeModel* orig_model, - DeeFilterModel* filter_model, - gpointer user_data) -{ - DeeModelIter* iter; - DeeModelIter* end; - unsigned index = GPOINTER_TO_UINT(user_data); - - iter = dee_model_get_first_iter(orig_model); - end = dee_model_get_last_iter(orig_model); - while (iter != end) - { - unsigned category_index = dee_model_get_uint32(orig_model, iter, - CATEGORY_COLUMN); - if (index == category_index) - { - dee_filter_model_append_iter(filter_model, iter); - } - iter = dee_model_next(orig_model, iter); - } -} - -static gboolean category_filter_notify_func (DeeModel* orig_model, - DeeModelIter* orig_iter, - DeeFilterModel* filter_model, - gpointer user_data) -{ - unsigned index = GPOINTER_TO_UINT(user_data); - unsigned category_index = dee_model_get_uint32(orig_model, orig_iter, - CATEGORY_COLUMN); - - if (index != category_index) - return FALSE; - - dee_filter_model_insert_iter_with_original_order(filter_model, orig_iter); - return TRUE; -} - -void LensView::GetFilterForCategoryIndex(unsigned index, DeeFilter* filter) -{ - filter->map_func = category_filter_map_func; - filter->map_notify = category_filter_notify_func; - filter->destroy = nullptr; - filter->userdata = GUINT_TO_POINTER(index); -} -  bool LensView::ReinitializeFilterModels()  {  Results::Ptr results_model = lens_->results;  for (unsigned i = last_good_filter_model_ + 1; i < categories_.size(); ++i)  { - PlacesGroup* group = categories_[i]; - ResultViewGrid* grid = static_cast<ResultViewGrid*>(group->GetChildView()); - DeeFilter filter; - GetFilterForCategoryIndex(i, &filter); - glib::Object<DeeModel> filter_model(dee_filter_model_new(results_model->model(), &filter)); + ResultViewGrid* grid = GetGridForCategory(i); + glib::Object<DeeModel> filter_model(lens_->GetFilterModelForCategory(i));  grid->SetModel(filter_model, results_model->GetTag());  } @@ -486,6 +430,13 @@ bool LensView::ReinitializeFilterModels()  return false;  } +ResultViewGrid* LensView::GetGridForCategory(unsigned category_index) +{ + if (category_index >= categories_.size()) return nullptr; + PlacesGroup* group = categories_.at(category_index); + return static_cast<ResultViewGrid*>(group->GetChildView()); +} +  void LensView::OnResultAdded(Result const& result)  {  try { @@ -755,18 +706,20 @@ void LensView::ActivateFirst()  Results::Ptr results = lens_->results;  if (results->count())  { - // FIXME: Linear search in the result model, use category grid's model! - for (unsigned int c = 0; c < scroll_layout_->GetChildren().size(); ++c) + // the first displayed category might not be categories_[0] + auto category_order = lens_->GetCategoriesOrder(); + for (unsigned int i = 0; i < category_order.size(); i++)  { - for (unsigned int i = 0; i < results->count(); ++i) + unsigned cat_index = category_order.at(i); + ResultViewGrid* grid = GetGridForCategory(cat_index); + if (grid == nullptr) continue; + auto it = grid->GetIteratorAtRow(0); + if (!it.IsLast())  { - Result result = results->RowAtIndex(i); - if (result.category_index == c && result.uri != "") - { - uri_activated(result.uri); - lens_->Activate(result.uri); - return; - } + Result result(*it); + uri_activated(result.uri); + lens_->Activate(result.uri); + return;  }  }  // Fallback diff --git a/dash/LensView.h b/dash/LensView.h index b9867c21a..7dbe940d4 100644 --- a/dash/LensView.h +++ b/dash/LensView.h @@ -95,8 +95,7 @@ private:  void QueueFixRenderering();  bool FixRenderering();  bool ReinitializeFilterModels(); - - static void GetFilterForCategoryIndex(unsigned index, DeeFilter* filter); + ResultViewGrid* GetGridForCategory(unsigned category_index);  void BuildPreview(std::string const& uri, Preview::Ptr model); diff --git a/dash/previews/ApplicationPreview.cpp b/dash/previews/ApplicationPreview.cpp index b32c5421d..79841fe15 100644 --- a/dash/previews/ApplicationPreview.cpp +++ b/dash/previews/ApplicationPreview.cpp @@ -309,7 +309,7 @@ void ApplicationPreview::PreLayoutManagement()  image_->SetMinMaxSize(geo_art.width, geo_art.height);  int details_width = MAX(0, geo.width - geo_art.width - style.GetPanelSplitWidth() - style.GetDetailsLeftMargin() - style.GetDetailsRightMargin()); - int top_app_info_max_width = details_width - style.GetAppIconAreaWidth() - style.GetSpaceBetweenIconAndDetails(); + int top_app_info_max_width = MAX(0, details_width - style.GetAppIconAreaWidth() - style.GetSpaceBetweenIconAndDetails());  if (title_) { title_->SetMaximumWidth(top_app_info_max_width); }  if (subtitle_) { subtitle_->SetMaximumWidth(top_app_info_max_width); } diff --git a/dash/previews/CMakeLists.txt b/dash/previews/CMakeLists.txt index 2ccb845dd..15d47bfe8 100644 --- a/dash/previews/CMakeLists.txt +++ b/dash/previews/CMakeLists.txt @@ -10,13 +10,13 @@ set (CFLAGS  "-I${CMAKE_CURRENT_BINARY_DIR}"  ) -if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") +if (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l")  set (CFLAGS ${CFLAGS} "-fPIC") -endif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64") +endif (${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86_64" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l")  add_definitions (${CFLAGS}) -set (LIBS ${CACHED_UNITY_DEPS_LIBRARIES} "-lunity-core-${UNITY_API_VERSION} -lm -lGL -lGLU") +set (LIBS ${CACHED_UNITY_DEPS_LIBRARIES} ${UNITY_STANDALONE_LADD})  link_libraries (${LIBS})  set (LIB_PATHS ${CACHED_UNITY_DEPS_LIBRARY_DIRS}) diff --git a/dash/previews/PreviewContainer.cpp b/dash/previews/PreviewContainer.cpp index f07c80d2b..54cc3bf99 100644 --- a/dash/previews/PreviewContainer.cpp +++ b/dash/previews/PreviewContainer.cpp @@ -149,9 +149,9 @@ public:  nux::Geometry swipeOut = geometry;  if (swipe_.direction == Navigation::RIGHT) - swipeOut.OffsetPosition(-(curve_progress * (geometry.GetWidth() + parent_->nav_left_->GetGeometry().GetWidth())), 0); + swipeOut.OffsetPosition(-(curve_progress * (parent_->GetGeometry().width - geometry.x)), 0);  else if (swipe_.direction == Navigation::LEFT) - swipeOut.OffsetPosition(curve_progress* (geometry.GetWidth() + parent_->nav_right_->GetGeometry().GetWidth()), 0); + swipeOut.OffsetPosition(curve_progress* (parent_->GetGeometry().width - geometry.x), 0);  current_preview_->SetGeometry(swipeOut);  } @@ -162,9 +162,9 @@ public:  nux::Geometry swipeIn = geometry;  if (swipe_.direction == Navigation::RIGHT) - swipeIn.OffsetPosition(float(geometry.GetWidth() + parent_->nav_right_->GetGeometry().GetWidth()) - (curve_progress * (geometry.GetWidth() + parent_->nav_right_->GetGeometry().GetWidth())), 0); + swipeIn.OffsetPosition(float(parent_->GetGeometry().width - geometry.x) - (curve_progress * (parent_->GetGeometry().width - geometry.x)), 0);  else if (swipe_.direction == Navigation::LEFT) - swipeIn.OffsetPosition(-((1.0-curve_progress)*(geometry.GetWidth() + parent_->nav_left_->GetGeometry().GetWidth())), 0); + swipeIn.OffsetPosition(-((1.0-curve_progress)*(parent_->GetGeometry().width - geometry.x)), 0);  swipe_.preview->SetGeometry(swipeIn);  }  } @@ -355,7 +355,6 @@ PreviewContainer::PreviewContainer(NUX_FILE_LINE_DECL)  : View(NUX_FILE_LINE_PARAM)  , content_layout_(nullptr)  , nav_disabled_(Navigation::NONE) - , last_calc_height_(0)  , navigation_progress_speed_(0.0)  , navigation_count_(0)  { @@ -411,7 +410,7 @@ void PreviewContainer::SetupViews()  layout_ = new nux::HLayout();  SetLayout(layout_); - layout_->AddSpace(0, 0); + layout_->AddSpace(0, 1);  nav_left_ = new PreviewNavigator(Orientation::LEFT, NUX_TRACKER_LOCATION);  AddChild(nav_left_); @@ -421,8 +420,9 @@ void PreviewContainer::SetupViews()  layout_->AddView(nav_left_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);  content_layout_ = new PreviewContent(this); + content_layout_->SetMinMaxSize(style.GetPreviewWidth(), style.GetPreviewHeight());  AddChild(content_layout_); - layout_->AddLayout(content_layout_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); + layout_->AddLayout(content_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);  nav_right_ = new PreviewNavigator(Orientation::RIGHT, NUX_TRACKER_LOCATION);  AddChild(nav_right_); @@ -431,7 +431,7 @@ void PreviewContainer::SetupViews()  nav_right_->activated.connect([&]() { navigate_right.emit(); });  layout_->AddView(nav_right_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); - layout_->AddSpace(0, 0); + layout_->AddSpace(0, 1);  content_layout_->start_navigation.connect([&]()  { @@ -497,32 +497,6 @@ void PreviewContainer::DrawContent(nux::GraphicsEngine& gfx_engine, bool force_d  gfx_engine.PopClippingRectangle();  } -void PreviewContainer::PreLayoutManagement() -{ - previews::Style& style = previews::Style::Instance(); - nux::Geometry const& geo = GetGeometry(); - - int available_preview_width = MAX(1, geo.width - nav_left_->GetGeometry().width - nav_right_->GetGeometry().width); - int aspect_altered_height = available_preview_width / style.GetPreviewAspectRatio(); - - aspect_altered_height = CLAMP(aspect_altered_height, 1, geo.height); - if (last_calc_height_ != aspect_altered_height) - { - last_calc_height_ = aspect_altered_height; - - content_layout_->SetMinimumHeight(aspect_altered_height); - content_layout_->SetMaximumHeight(aspect_altered_height); - - nav_left_->SetMinimumHeight(aspect_altered_height); - nav_left_->SetMaximumHeight(aspect_altered_height); - - nav_right_->SetMinimumHeight(aspect_altered_height); - nav_right_->SetMaximumHeight(aspect_altered_height); - } - - View::PreLayoutManagement(); -} -  bool PreviewContainer::AnimationInProgress()  {  // short circuit to avoid unneeded calculations @@ -569,7 +543,7 @@ bool PreviewContainer::QueueAnimation()  last_progress_time_ = current;  QueueDraw(); - return true; + return false;  }  bool PreviewContainer::AcceptKeyNavFocus() diff --git a/dash/previews/PreviewContainer.h b/dash/previews/PreviewContainer.h index 6e790295f..78b19750e 100644 --- a/dash/previews/PreviewContainer.h +++ b/dash/previews/PreviewContainer.h @@ -81,7 +81,6 @@ public:  protected:  void Draw(nux::GraphicsEngine& gfx_engine, bool force_draw);  void DrawContent(nux::GraphicsEngine& gfx_engine, bool force_draw); - void PreLayoutManagement();  bool InspectKeyEvent(unsigned int eventType, unsigned int keysym, const char* character);  void OnKeyDown(unsigned long event_type, unsigned long event_keysym, unsigned long event_state, const TCHAR* character, unsigned short key_repeat_count); @@ -101,7 +100,6 @@ private:  PreviewNavigator* nav_right_;  PreviewContent* content_layout_;  Navigation nav_disabled_; - int last_calc_height_;  // Animation  struct timespec last_progress_time_; diff --git a/dash/previews/PreviewInfoHintWidget.cpp b/dash/previews/PreviewInfoHintWidget.cpp index 1e90b4964..793b55e26 100644 --- a/dash/previews/PreviewInfoHintWidget.cpp +++ b/dash/previews/PreviewInfoHintWidget.cpp @@ -210,6 +210,7 @@ void PreviewInfoHintWidget::PreLayoutManagement()  int info_value_width = geo.width;  info_value_width -= layout_spacing;  info_value_width -= info_hint_width; + info_value_width = MAX(0, info_value_width);  for (InfoHint const& info_hint : info_hints_)  { diff --git a/dash/previews/Track.cpp b/dash/previews/Track.cpp index cfd7bf1f7..ca02a59d9 100644 --- a/dash/previews/Track.cpp +++ b/dash/previews/Track.cpp @@ -265,7 +265,6 @@ void Track::SetupViews()  duration_layout_->AddSpace(0, 1);  duration_layout_->AddView(duration_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); - layout->SetSpaceBetweenChildren(6);  layout->AddLayout(track_status_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);  layout->AddLayout(title_layout_, 1, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);  layout->AddLayout(duration_layout_, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL); diff --git a/hud/HudButton.cpp b/hud/HudButton.cpp index 13159eba7..3ab6ff043 100644 --- a/hud/HudButton.cpp +++ b/hud/HudButton.cpp @@ -75,6 +75,22 @@ HudButton::HudButton(NUX_FILE_LINE_DECL)  {  QueueDraw();  }); + + mouse_move.connect([&](int x, int y, int dx, int dy, unsigned int button, unsigned int key) + { + if (!fake_focused) + fake_focused = true; + }); + + mouse_enter.connect([&](int x, int y, unsigned int button, unsigned int key) + { + fake_focused = true; + }); + + mouse_leave.connect([&](int x, int y, unsigned int button, unsigned int key) + { + fake_focused = false; + });  }  void HudButton::InitTheme() @@ -113,7 +129,6 @@ bool HudButton::AcceptKeyNavFocus()  return false;  } -  long HudButton::ComputeContentSize()  {  long ret = nux::Button::ComputeContentSize(); @@ -207,6 +222,7 @@ void HudButton::SetQuery(Query::Ptr query)  nux::StaticCairoText* text = new nux::StaticCairoText(item.first);  text->SetTextColor(nux::Color(1.0f, 1.0f, 1.0f, item.second ? 1.0f : 0.5f));  text->SetFont(button_font); + text->SetInputEventSensitivity(false);  hlayout_->AddView(text, 0, nux::MINOR_POSITION_CENTER, nux::MINOR_SIZE_FULL);  }  } diff --git a/hud/HudView.cpp b/hud/HudView.cpp index 567392983..7c779b632 100644 --- a/hud/HudView.cpp +++ b/hud/HudView.cpp @@ -66,6 +66,7 @@ View::View()  , timeline_need_more_draw_(false)  , selected_button_(0)  , show_embedded_icon_(true) + , keyboard_stole_focus_(false)  {  renderer_.SetOwner(this);  renderer_.need_redraw.connect([this] () { @@ -251,6 +252,22 @@ void View::SetQueries(Hud::Queries queries)  query_activated.emit(dynamic_cast<HudButton*>(view)->GetQuery());  }); + button->mouse_move.connect([&](int x, int y, int dx, int dy, unsigned long mouse_button, unsigned long special_key) { + if (keyboard_stole_focus_) + { + MouseStealsHudButtonFocus(); + keyboard_stole_focus_ = false; + } + }); + + button->mouse_enter.connect([&](int x, int y, unsigned long mouse_button, unsigned long special_key) { + MouseStealsHudButtonFocus(); + }); + + button->mouse_leave.connect([&](int x, int y, unsigned long mouse_button, unsigned long special_key) { + SelectLastFocusedButton(); + }); +  button->key_nav_focus_activate.connect([&](nux::Area* area) {  query_activated.emit(dynamic_cast<HudButton*>(area)->GetQuery());  }); @@ -484,6 +501,49 @@ void View::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw)  }  } +void View::MouseStealsHudButtonFocus() +{ + LoseSelectedButtonFocus(); + FindNewSelectedButton(); +} + +void View::LoseSelectedButtonFocus() +{ + int button_index = 1; + for (auto it = buttons_.rbegin(); it != buttons_.rend(); ++it) + { + if (selected_button_ == button_index) + (*it)->fake_focused = false; + ++button_index; + } +} + +void View::FindNewSelectedButton() +{ + int button_index = 1; + for (auto it = buttons_.rbegin(); it != buttons_.rend(); ++it) + { + if ((*it)->fake_focused) + { + query_selected.emit((*it)->GetQuery()); + selected_button_ = button_index; + return; + } + ++button_index; + } +} + +void View::SelectLastFocusedButton() +{ + int button_index = 1; + for (auto it = buttons_.rbegin(); it != buttons_.rend(); ++it) + { + if (button_index == selected_button_) + (*it)->fake_focused = true; + ++button_index; + } +} +  // Keyboard navigation  bool View::AcceptKeyNavFocus()  { @@ -656,12 +716,12 @@ nux::Area* View::FindKeyFocusArea(unsigned int event_type,  (*next)->fake_focused = true;  query_selected.emit((*next)->GetQuery());  --selected_button_; + keyboard_stole_focus_ = true;  }  break;  }  }  } -  if (event_type == nux::NUX_KEYDOWN && direction == nux::KEY_NAV_DOWN)  {  std::list<HudButton::Ptr>::reverse_iterator rit; @@ -679,6 +739,7 @@ nux::Area* View::FindKeyFocusArea(unsigned int event_type,  (*next)->fake_focused = true;  query_selected.emit((*next)->GetQuery());  ++selected_button_; + keyboard_stole_focus_ = true;  }  break;  } diff --git a/hud/HudView.h b/hud/HudView.h index 8e654b49f..982bafe24 100644 --- a/hud/HudView.h +++ b/hud/HudView.h @@ -85,6 +85,11 @@ private:  void ProcessGrowShrink(); + void MouseStealsHudButtonFocus(); + void LoseSelectedButtonFocus(); + void FindNewSelectedButton(); + void SelectLastFocusedButton(); +  std::string GetName() const;  void AddProperties(GVariantBuilder* builder);  IntrospectableList GetIntrospectableChildren(); @@ -116,6 +121,7 @@ private:  int selected_button_;  bool show_embedded_icon_;  bool activated_signal_sent_; + bool keyboard_stole_focus_;  }; diff --git a/launcher/AbstractVolumeMonitorWrapper.h b/launcher/AbstractVolumeMonitorWrapper.h index 155c0a8c7..a9ca1facc 100644 --- a/launcher/AbstractVolumeMonitorWrapper.h +++ b/launcher/AbstractVolumeMonitorWrapper.h @@ -27,7 +27,6 @@  #include <sigc++/trackable.h>  #include <UnityCore/GLibWrapper.h> -#include <UnityCore/GLibSignal.h>  namespace unity  { @@ -54,8 +53,8 @@ public:  sigc::signal<void, glib::Object<GVolume> const&> volume_removed;  }; -} // namespace launcher -} // namespace unity +} +} -#endif // UNITYSHELL_ABSTRACT_VOLUME_MONITOR_WRAPPER_H +#endif diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 3ac9e8db8..28bb87dde 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -37,14 +37,15 @@ set (LAUNCHER_SOURCES  DNDCollectionWindow.cpp  Decaymulator.cpp  DesktopLauncherIcon.cpp - DeviceLauncherIcon.cpp  DeviceLauncherSection.cpp - DevicesSettings.cpp + DeviceNotificationDisplayImp.cpp + DevicesSettingsImp.cpp  DndData.cpp  EdgeBarrierController.cpp  FavoriteStore.cpp  FavoriteStoreGSettings.cpp  FavoriteStorePrivate.cpp + FileManagerOpenerImp.cpp  HudLauncherIcon.cpp  Launcher.cpp  LauncherController.cpp @@ -71,6 +72,8 @@ set (LAUNCHER_SOURCES  SpacerLauncherIcon.cpp  Tooltip.cpp  TrashLauncherIcon.cpp + VolumeImp.cpp + VolumeLauncherIcon.cpp  VolumeMonitorWrapper.cpp  ) @@ -87,7 +90,7 @@ set (SWITCHER_SOURCES  add_library (switcher-lib STATIC ${SWITCHER_SOURCES})  add_dependencies (switcher-lib unity-core-${UNITY_API_VERSION} unity-shared) -#  +#  # Standalone variant  #  add_executable (launcher StandaloneLauncher.cpp) diff --git a/launcher/DeviceLauncherIcon.cpp b/launcher/DeviceLauncherIcon.cpp deleted file mode 100644 index af56343f0..000000000 --- a/launcher/DeviceLauncherIcon.cpp +++ /dev/null @@ -1,425 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2010 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: Neil Jagdish Patel <neil.patel@canonical.com> - */ - -#include "DeviceLauncherIcon.h" - -#include <algorithm> -#include <list> - -#include <glib/gi18n-lib.h> -#include <libnotify/notify.h> -#include <NuxCore/Logger.h> - -#include "DevicesSettings.h" -#include "unity-shared/IconLoader.h" -#include "unity-shared/ubus-server.h" -#include "unity-shared/UBusMessages.h" - -namespace unity -{ -namespace launcher -{ -namespace -{ - -nux::logging::Logger logger("unity.launcher"); - -const unsigned int volume_changed_timeout = 500; - -} - -DeviceLauncherIcon::DeviceLauncherIcon(glib::Object<GVolume> const& volume) - : SimpleLauncherIcon(IconType::DEVICE) - , volume_(volume) -{ - gsignals_.Add<void, GVolume*>(volume, "changed", sigc::mem_fun(this, &DeviceLauncherIcon::OnVolumeChanged)); - DevicesSettings::GetDefault().changed.connect(sigc::mem_fun(this, &DeviceLauncherIcon::OnSettingsChanged)); - - // Checks if in favorites! - glib::String uuid(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UUID)); - DeviceList favorites = DevicesSettings::GetDefault().GetFavorites(); - DeviceList::iterator pos = std::find(favorites.begin(), favorites.end(), uuid.Str()); - - keep_in_launcher_ = pos != favorites.end(); - - UpdateDeviceIcon(); - UpdateVisibility(); -} - -void DeviceLauncherIcon::OnVolumeChanged(GVolume* volume) -{ - if (!G_IS_VOLUME(volume)) - return; - - changed_timeout_.reset(new glib::Timeout(volume_changed_timeout, [this]() { - UpdateDeviceIcon(); - UpdateVisibility(); - return false; - })); -} - -void DeviceLauncherIcon::UpdateVisibility() -{ - switch (DevicesSettings::GetDefault().GetDevicesOption()) - { - case DevicesSettings::NEVER: - SetQuirk(Quirk::VISIBLE, false); - break; - case DevicesSettings::ONLY_MOUNTED: - if (keep_in_launcher_) - { - SetQuirk(Quirk::VISIBLE, true); - } - else - { - glib::Object<GMount> mount(g_volume_get_mount(volume_)); - SetQuirk(Quirk::VISIBLE, mount); - } - break; - case DevicesSettings::ALWAYS: - SetQuirk(Quirk::VISIBLE, true); - break; - } -} - -void DeviceLauncherIcon::UpdateDeviceIcon() -{ - name_ = glib::String(g_volume_get_name(volume_)).Str(); - - glib::Object<GIcon> icon(g_volume_get_icon(volume_)); - glib::String icon_string(g_icon_to_string(icon)); - - tooltip_text = name_; - icon_name = icon_string.Str(); - - SetQuirk(Quirk::RUNNING, false); -} - -bool -DeviceLauncherIcon::CanEject() -{ - return g_volume_can_eject(volume_); -} - -bool -DeviceLauncherIcon::CanStop() -{ - return g_drive_can_stop(g_volume_get_drive(volume_)); -} - -AbstractLauncherIcon::MenuItemsVector DeviceLauncherIcon::GetMenus() -{ - MenuItemsVector result; - glib::Object<DbusmenuMenuitem> menu_item; - glib::Object<GDrive> drive(g_volume_get_drive(volume_)); - typedef glib::Signal<void, DbusmenuMenuitem*, int> ItemSignal; - - // "Lock to Launcher"/"Unlock from Launcher" item - if (DevicesSettings::GetDefault().GetDevicesOption() == DevicesSettings::ONLY_MOUNTED - && drive && !g_drive_is_media_removable (drive)) - { - menu_item = dbusmenu_menuitem_new(); - - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, !keep_in_launcher_ ? _("Lock to Launcher") : _("Unlock from Launcher")); - dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); - dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_VISIBLE, true); - - gsignals_.Add(new ItemSignal(menu_item, DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, - sigc::mem_fun(this, &DeviceLauncherIcon::OnTogglePin))); - result.push_back(menu_item); - } - - // "Open" item - menu_item = dbusmenu_menuitem_new(); - - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Open")); - dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); - 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) { - ActivateLauncherIcon(ActionArg(ActionArg::OTHER, 0)); - })); - - result.push_back(menu_item); - - // "Eject" item - if (drive && g_drive_can_eject(drive)) - { - menu_item = dbusmenu_menuitem_new(); - - GList *list = g_drive_get_volumes(drive); - if (list) - { - if (!list->next) // If the list has only one item - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Eject")); - else - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Eject parent drive")); - - g_list_free_full(list, g_object_unref); - } - - dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); - 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) { - Eject(); - })); - - result.push_back(menu_item); - } - - // "Safely remove" item - if (drive && g_drive_can_stop(drive)) - { - menu_item = dbusmenu_menuitem_new(); - - GList *list = g_drive_get_volumes(drive); - if (list) - { - if (!list->next) // If the list has only one item - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Safely remove")); - else - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Safely remove parent drive")); - - g_list_free_full(list, g_object_unref); - } - - dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); - 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) { - StopDrive(); - })); - - result.push_back(menu_item); - } - - // "Unmount" item - if (!g_volume_can_eject(volume_)) // Don't need Unmount if can Eject - { - glib::Object<GMount> mount(g_volume_get_mount(volume_)); - - if (mount && g_mount_can_unmount(mount)) - { - menu_item = dbusmenu_menuitem_new(); - - dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Unmount")); - dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); - 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) { - Unmount(); - })); - - result.push_back(menu_item); - } - } - - return result; -} - -void DeviceLauncherIcon::ShowMount(GMount* mount) -{ - if (G_IS_MOUNT(mount)) - { - glib::Object<GFile> root(g_mount_get_root(mount)); - - if (G_IS_FILE(root.RawPtr())) - { - glib::String uri(g_file_get_uri(root)); - glib::Error error; - - g_app_info_launch_default_for_uri(uri.Value(), NULL, &error); - - if (error) - { - LOG_WARNING(logger) << "Cannot open volume '" << name_ - << "': Unable to show " << uri - << ": " << error; - } - } - else - { - LOG_WARNING(logger) << "Cannot open volume '" << name_ - << "': Mount has no root"; - } - } - else - { - LOG_WARNING(logger) << "Cannot open volume '" << name_ - << "': Mount-point is invalid"; - } -} - -void DeviceLauncherIcon::ActivateLauncherIcon(ActionArg arg) -{ - SimpleLauncherIcon::ActivateLauncherIcon(arg); - SetQuirk(Quirk::STARTING, true); - - glib::Object<GMount> mount(g_volume_get_mount(volume_)); - - if (G_IS_MOUNT(mount.RawPtr())) - ShowMount(mount); - else - g_volume_mount(volume_, - (GMountMountFlags)0, - NULL, - NULL, - (GAsyncReadyCallback)&DeviceLauncherIcon::OnMountReady, - this); -} - -void DeviceLauncherIcon::OnMountReady(GObject* object, - GAsyncResult* result, - DeviceLauncherIcon* self) -{ - glib::Error error; - - if (g_volume_mount_finish(self->volume_, result, &error)) - { - glib::Object<GMount> mount(g_volume_get_mount(self->volume_)); - self->ShowMount(mount); - } - else - { - LOG_WARNING(logger) << "Cannot open volume '" << self->name_ << "' : " << - (error ? error.Message() : "Mount operation failed"); - } -} - -void DeviceLauncherIcon::OnEjectReady(GObject* object, - GAsyncResult* result, - DeviceLauncherIcon* self) -{ - if (g_volume_eject_with_operation_finish(self->volume_, result, NULL)) - { - IconLoader::GetDefault().LoadFromGIconString(self->icon_name(), 48, - sigc::bind(sigc::mem_fun(self, &DeviceLauncherIcon::ShowNotification), self->name_)); - } -} - -void DeviceLauncherIcon::ShowNotification(std::string const& icon_name, - unsigned size, - glib::Object<GdkPixbuf> const& pixbuf, - std::string const& name) -{ - glib::Object<NotifyNotification> notification(notify_notification_new(name.c_str(), - _("The drive has been successfully ejected"), - NULL)); - - notify_notification_set_hint(notification, - "x-canonical-private-synchronous", - g_variant_new_boolean(TRUE)); - - if (GDK_IS_PIXBUF(pixbuf.RawPtr())) - notify_notification_set_image_from_pixbuf(notification, pixbuf); - - notify_notification_show(notification, NULL); -} - -void DeviceLauncherIcon::Eject() -{ - glib::Object<GMountOperation> mount_op(gtk_mount_operation_new(NULL)); - - g_volume_eject_with_operation(volume_, - (GMountUnmountFlags)0, - mount_op, - NULL, - (GAsyncReadyCallback)OnEjectReady, - this); -} - -void DeviceLauncherIcon::OnTogglePin(DbusmenuMenuitem* item, int time) -{ - glib::String uuid(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UUID)); - - keep_in_launcher_ = !keep_in_launcher_; - - if (!keep_in_launcher_) - { - // If the volume is not mounted hide the icon - glib::Object<GMount> mount(g_volume_get_mount(volume_)); - - if (!mount) - SetQuirk(Quirk::VISIBLE, false); - - // Remove from favorites - if (!uuid.Str().empty()) - DevicesSettings::GetDefault().RemoveFavorite(uuid.Str()); - } - else - { - if (!uuid.Str().empty()) - DevicesSettings::GetDefault().AddFavorite(uuid.Str()); - } -} - -void DeviceLauncherIcon::OnUnmountReady(GObject* object, - GAsyncResult* result, - DeviceLauncherIcon* self) -{ - if (G_IS_MOUNT(object)) - g_mount_unmount_with_operation_finish(G_MOUNT(object), result, NULL); -} - -void DeviceLauncherIcon::Unmount() -{ - glib::Object<GMount> mount(g_volume_get_mount(volume_)); - - if (mount) - { - glib::Object<GMountOperation> op(gtk_mount_operation_new(NULL)); - - g_mount_unmount_with_operation(mount, (GMountUnmountFlags)0, op, NULL, - (GAsyncReadyCallback)OnUnmountReady, this); - } -} - -void DeviceLauncherIcon::OnRemoved() -{ - Remove(); -} - -void DeviceLauncherIcon::StopDrive() -{ - glib::Object<GDrive> drive(g_volume_get_drive(volume_)); - glib::Object<GMountOperation> mount_op(gtk_mount_operation_new(NULL)); - - g_drive_stop(drive, (GMountUnmountFlags)0, mount_op, NULL, NULL, NULL); -} - -void DeviceLauncherIcon::OnSettingsChanged() -{ - // Checks if in favourites! - glib::String uuid(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UUID)); - DeviceList favorites = DevicesSettings::GetDefault().GetFavorites(); - DeviceList::iterator pos = std::find(favorites.begin(), favorites.end(), uuid.Str()); - - keep_in_launcher_ = pos != favorites.end(); - - UpdateVisibility(); -} - -std::string DeviceLauncherIcon::GetName() const -{ - return "DeviceLauncherIcon"; -} - -} // namespace launcher -} // namespace unity diff --git a/launcher/DeviceLauncherIcon.h b/launcher/DeviceLauncherIcon.h deleted file mode 100644 index c0c3ca9eb..000000000 --- a/launcher/DeviceLauncherIcon.h +++ /dev/null @@ -1,78 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2010 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: Neil Jagdish Patel <neil.patel@canonical.com> - */ - -#ifndef _DEVICE_LAUNCHER_ICON_H__H -#define _DEVICE_LAUNCHER_ICON_H__H - -#include <gio/gio.h> -#include <UnityCore/GLibWrapper.h> -#include <UnityCore/GLibSignal.h> - -#include "SimpleLauncherIcon.h" - -namespace unity -{ -namespace launcher -{ - -class DeviceLauncherIcon : public SimpleLauncherIcon -{ -public: - typedef nux::ObjectPtr<DeviceLauncherIcon> Ptr; - - DeviceLauncherIcon(glib::Object<GVolume> const& volume); - - void OnRemoved(); - bool CanEject(); - bool CanStop(); - void Eject(); - void StopDrive(); - -protected: - MenuItemsVector GetMenus(); - std::string GetName() const; - -private: - void UpdateVisibility(); - void UpdateDeviceIcon(); - void ActivateLauncherIcon(ActionArg arg); - void ShowMount(GMount* mount); - void Unmount(); - void OnTogglePin(DbusmenuMenuitem* item, int time); - void OnSettingsChanged(); - void ShowNotification(std::string const&, unsigned, glib::Object<GdkPixbuf> const&, std::string const&); - void OnVolumeChanged(GVolume* volume); - static void OnChanged(GVolume* volume, DeviceLauncherIcon* self); - static void OnMountReady(GObject* object, GAsyncResult* result, DeviceLauncherIcon* self); - static void OnEjectReady(GObject* object, GAsyncResult* result, DeviceLauncherIcon* self); - static void OnUnmountReady(GObject* object, GAsyncResult* result, DeviceLauncherIcon* self); - -private: - glib::Source::UniquePtr changed_timeout_; - glib::Object<GVolume> volume_; - glib::SignalManager gsignals_; - - std::string name_; - bool keep_in_launcher_; -}; - -} -} // namespace unity - -#endif // _DEVICE_LAUNCHER_ICON_H__H diff --git a/launcher/DeviceLauncherSection.cpp b/launcher/DeviceLauncherSection.cpp index 5fa8cbf36..b618f0cb9 100644 --- a/launcher/DeviceLauncherSection.cpp +++ b/launcher/DeviceLauncherSection.cpp @@ -18,19 +18,27 @@  */  #include "DeviceLauncherSection.h" +#include "DeviceNotificationDisplayImp.h" +#include "DevicesSettings.h" +#include "FileManagerOpenerImp.h" +#include "VolumeImp.h"  namespace unity  {  namespace launcher  { -DeviceLauncherSection::DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr volume_monitor) +DeviceLauncherSection::DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr volume_monitor, + DevicesSettings::Ptr devices_settings)  : monitor_(volume_monitor) + , devices_settings_(devices_settings) + , file_manager_opener_(new FileManagerOpenerImp) + , device_notification_display_(new DeviceNotificationDisplayImp)  {  monitor_->volume_added.connect(sigc::mem_fun(this, &DeviceLauncherSection::OnVolumeAdded));  monitor_->volume_removed.connect(sigc::mem_fun(this, &DeviceLauncherSection::OnVolumeRemoved)); -  - device_populate_idle_.Run([&] () { + + device_populate_idle_.Run([this] () {  PopulateEntries();  return false;  }); @@ -39,29 +47,21 @@ DeviceLauncherSection::DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr v  void DeviceLauncherSection::PopulateEntries()  {  for (auto volume : monitor_->GetVolumes()) - { - // Sanity check. Avoid duplicates. - if (map_.find(volume) != map_.end()) - continue; - - DeviceLauncherIcon::Ptr icon(new DeviceLauncherIcon(volume)); - - map_[volume] = icon; - IconAdded.emit(icon); - } + TryToCreateAndAddIcon(volume);  } -/* Uses a std::map to track all the volume icons shown and not shown. - * Keep in mind: when "volume-removed" is recevied we should erase - * the pair (GVolume - DeviceLauncherIcon) from the std::map to avoid leaks - */  void DeviceLauncherSection::OnVolumeAdded(glib::Object<GVolume> const& volume)  { - // Sanity check. Avoid duplicates. + TryToCreateAndAddIcon(volume); +} + +void DeviceLauncherSection::TryToCreateAndAddIcon(glib::Object<GVolume> volume) +{  if (map_.find(volume) != map_.end())  return; - DeviceLauncherIcon::Ptr icon(new DeviceLauncherIcon(volume)); + VolumeLauncherIcon::Ptr icon(new VolumeLauncherIcon(std::make_shared<VolumeImp>(volume, file_manager_opener_, device_notification_display_), + devices_settings_));  map_[volume] = icon;  IconAdded.emit(icon); @@ -73,12 +73,8 @@ void DeviceLauncherSection::OnVolumeRemoved(glib::Object<GVolume> const& volume)  // Sanity check  if (volume_it != map_.end()) - { - volume_it->second->OnRemoved();  map_.erase(volume_it); - }  } -} // namespace launcher -} // namespace unity - +} +} diff --git a/launcher/DeviceLauncherSection.h b/launcher/DeviceLauncherSection.h index 249939068..551fa4a2a 100644 --- a/launcher/DeviceLauncherSection.h +++ b/launcher/DeviceLauncherSection.h @@ -17,15 +17,19 @@  * Andrea Azzarone <andrea.azzarone@canonical.com>  */ -#ifndef _DEVICE_LAUNCHER_SECTION_H_ -#define _DEVICE_LAUNCHER_SECTION_H_ +#ifndef UNITYSHELL_DEVICE_LAUNCHER_SECTION_H +#define UNITYSHELL_DEVICE_LAUNCHER_SECTION_H  #include <map> +#include <memory>  #include <UnityCore/GLibSource.h> -#include "DeviceLauncherIcon.h"  #include "AbstractVolumeMonitorWrapper.h" +#include "DevicesSettings.h" +#include "DeviceNotificationDisplay.h" +#include "FileManagerOpener.h" +#include "VolumeLauncherIcon.h"  namespace unity  { @@ -35,7 +39,8 @@ namespace launcher  class DeviceLauncherSection : public sigc::trackable  {  public: - DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr volume_monitor); + DeviceLauncherSection(AbstractVolumeMonitorWrapper::Ptr volume_monitor, + DevicesSettings::Ptr devices_settings);  sigc::signal<void, AbstractLauncherIcon::Ptr> IconAdded; @@ -43,13 +48,18 @@ private:  void PopulateEntries();  void OnVolumeAdded(glib::Object<GVolume> const& volume);  void OnVolumeRemoved(glib::Object<GVolume> const& volume); + void TryToCreateAndAddIcon(glib::Object<GVolume> volume); - std::map<GVolume*, DeviceLauncherIcon::Ptr> map_; + std::map<GVolume*, VolumeLauncherIcon::Ptr> map_;  AbstractVolumeMonitorWrapper::Ptr monitor_; + DevicesSettings::Ptr devices_settings_; + FileManagerOpener::Ptr file_manager_opener_; + DeviceNotificationDisplay::Ptr device_notification_display_; +  glib::Idle device_populate_idle_;  };  } -} // namespace unity +} -#endif // _DEVICE_LAUNCHER_SECTION_H_ +#endif diff --git a/launcher/DeviceNotificationDisplay.h b/launcher/DeviceNotificationDisplay.h new file mode 100644 index 000000000..2c12a899a --- /dev/null +++ b/launcher/DeviceNotificationDisplay.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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_H +#define UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_H + +#include <boost/noncopyable.hpp> +#include <memory> +#include <string> + +namespace unity +{ +namespace launcher +{ + +class DeviceNotificationDisplay : private boost::noncopyable +{ +public: + typedef std::shared_ptr<DeviceNotificationDisplay> Ptr; + + virtual ~DeviceNotificationDisplay() {} + + virtual void Display(std::string const& icon_name, std::string const& volume_name) = 0; +}; + +} +} + +#endif diff --git a/launcher/DeviceNotificationDisplayImp.cpp b/launcher/DeviceNotificationDisplayImp.cpp new file mode 100644 index 000000000..cbb04c77d --- /dev/null +++ b/launcher/DeviceNotificationDisplayImp.cpp @@ -0,0 +1,82 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#include <gdk/gdk.h> +#include <glib/gi18n-lib.h> +#include <libnotify/notify.h> +#include <sigc++/sigc++.h> +#include <UnityCore/GLibWrapper.h> + +#include "DeviceNotificationDisplayImp.h" +#include "unity-shared/IconLoader.h" + +namespace unity +{ +namespace launcher +{ + +// +// Start private implementation +// +class DeviceNotificationDisplayImp::Impl +{ +public: + void Show(std::string const& icon_name, std::string const& volume_name) + { + int icon_size = 48; + IconLoader::GetDefault().LoadFromGIconString(icon_name, icon_size, + sigc::bind(sigc::mem_fun(this, &Impl::ShowNotificationWhenIconIsReady), volume_name)); + } + + void ShowNotificationWhenIconIsReady(std::string const& icon_name, + unsigned size, + glib::Object<GdkPixbuf> const& pixbuf, + std::string const& volume_name) + { + glib::Object<NotifyNotification> notification(notify_notification_new(volume_name.c_str(), + _("The drive has been successfully ejected"), + nullptr)); + + notify_notification_set_hint(notification, "x-canonical-private-synchronous", g_variant_new_boolean(TRUE)); + + if (GDK_IS_PIXBUF(pixbuf.RawPtr())) + notify_notification_set_image_from_pixbuf(notification, pixbuf); + + notify_notification_show(notification, nullptr); + } +}; + +// +// End private implementation +// + +DeviceNotificationDisplayImp::DeviceNotificationDisplayImp() + : pimpl(new Impl) +{} + +DeviceNotificationDisplayImp::~DeviceNotificationDisplayImp() +{} + +void DeviceNotificationDisplayImp::Display(std::string const& icon_name, std::string const& volume_name) +{ + pimpl->Show(icon_name, volume_name); +} + +} +} diff --git a/launcher/DeviceNotificationDisplayImp.h b/launcher/DeviceNotificationDisplayImp.h new file mode 100644 index 000000000..910951b15 --- /dev/null +++ b/launcher/DeviceNotificationDisplayImp.h @@ -0,0 +1,46 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_IMP_H +#define UNITYSHELL_DEVICE_NOTIFICATION_DISPLAY_IMP_H + +#include "DeviceNotificationDisplay.h" + +namespace unity +{ +namespace launcher +{ + +class DeviceNotificationDisplayImp : public DeviceNotificationDisplay +{ +public: + DeviceNotificationDisplayImp(); + virtual ~DeviceNotificationDisplayImp(); + + virtual void Display(std::string const& icon_name, std::string const& volume_name); + +private: + class Impl; + std::unique_ptr<Impl> pimpl; +}; + +} +} + +#endif diff --git a/launcher/DevicesSettings.cpp b/launcher/DevicesSettings.cpp deleted file mode 100644 index 7b09bf070..000000000 --- a/launcher/DevicesSettings.cpp +++ /dev/null @@ -1,143 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* - * Copyright (C) 2010 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: Andrea Azzarone <aazzarone@hotmail.it> - */ - -#include "DevicesSettings.h" - -#include <algorithm> - -namespace unity { - -namespace { - -const char* SETTINGS_NAME = "com.canonical.Unity.Devices"; - -void on_settings_updated(GSettings* settings, - const gchar* key, - DevicesSettings* self); - -} // anonymous namespace - -DevicesSettings& DevicesSettings::GetDefault() -{ - static DevicesSettings instance; - return instance; -} - -DevicesSettings::DevicesSettings() - : settings_(g_settings_new(SETTINGS_NAME)) - , ignore_signals_(false) - , devices_option_(ONLY_MOUNTED) -{ - - g_signal_connect(settings_, "changed", G_CALLBACK(on_settings_updated), this); - - Refresh(); -} - -void DevicesSettings::Refresh() -{ - gchar** favs = g_settings_get_strv(settings_, "favorites"); - - favorites_.clear(); - - for (int i = 0; favs[i] != NULL; i++) - favorites_.push_back(favs[i]); - - g_strfreev(favs); -} - -void DevicesSettings::AddFavorite(std::string const& uuid) -{ - if (uuid.empty()) - return; -  - favorites_.push_back(uuid); - - SaveFavorites(favorites_); - Refresh(); -} - -void DevicesSettings::RemoveFavorite(std::string const& uuid) -{ - if (uuid.empty()) - return; - - DeviceList::iterator pos = std::find(favorites_.begin(), favorites_.end(), uuid); - if (pos == favorites_.end()) - return; - - favorites_.erase(pos); - SaveFavorites(favorites_); - Refresh(); -} - -void DevicesSettings::SaveFavorites(DeviceList const& favorites) -{ - const int size = favorites.size(); - const char* favs[size + 1]; - favs[size] = NULL; - - int index = 0; - for (DeviceList::const_iterator i = favorites.begin(), end = favorites.end(); - i != end; ++i, ++index) - { - favs[index] = i->c_str(); - } - - ignore_signals_ = true; - if (!g_settings_set_strv(settings_, "favorites", favs)) - g_warning("Saving favorites failed."); - ignore_signals_ = false; -} - - -void DevicesSettings::Changed(std::string const& key) -{ - if (ignore_signals_) - return; -  - Refresh(); -  - changed.emit(); -} - -void DevicesSettings::SetDevicesOption(DevicesOption devices_option) -{ - if (devices_option == devices_option_) - return; - - devices_option_ = devices_option; - - changed.emit(); -} - -namespace { - -void on_settings_updated(GSettings* settings, - const gchar* key, - DevicesSettings* self) -{ - if (settings and key) { - self->Changed(key); - } -} - -} // anonymous namespace - -} // namespace unity diff --git a/launcher/DevicesSettings.h b/launcher/DevicesSettings.h index ad2a24de8..8dda12fc8 100644 --- a/launcher/DevicesSettings.h +++ b/launcher/DevicesSettings.h @@ -1,6 +1,6 @@  // -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-  /* - * Copyright (C) 2010 Canonical Ltd + * Copyright (C) 2010-12 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 @@ -14,61 +14,39 @@  * 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: Andrea Azzarone <aazzarone@hotmail.it> + * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com>  */ -#ifndef DEVICES_SETTINGS_H -#define DEVICES_SETTINGS_H +#ifndef UNITYSHELL_DEVICES_SETTINGS_H +#define UNITYSHELL_DEVICES_SETTINGS_H -#include <list> +#include <boost/noncopyable.hpp> +#include <memory>  #include <string> -#include <gio/gio.h> -#include <boost/utility.hpp> -#include <sigc++/sigc++.h> -#include <UnityCore/GLibWrapper.h> +#include <sigc++/signal.h> +#include <sigc++/trackable.h> -namespace unity { - -typedef std::list<std::string> DeviceList; +namespace unity +{ +namespace launcher +{ -class DevicesSettings : boost::noncopyable +class DevicesSettings : boost::noncopyable, public sigc::trackable  {  public: - typedef enum - { - NEVER = 0, - ONLY_MOUNTED, - ALWAYS + typedef std::shared_ptr<DevicesSettings> Ptr; - } DevicesOption; + virtual ~DevicesSettings() {}; - DevicesSettings(); -  - static DevicesSettings& GetDefault(); + virtual bool IsABlacklistedDevice(std::string const& uuid) const = 0; + virtual void TryToBlacklist(std::string const& uuid) = 0; + virtual void TryToUnblacklist(std::string const& uuid) = 0; - void SetDevicesOption(DevicesOption devices_option); - DevicesOption GetDevicesOption() { return devices_option_; }; - - DeviceList const& GetFavorites() { return favorites_; }; - void AddFavorite(std::string const& uuid); - void RemoveFavorite(std::string const& uuid); - - void Changed(std::string const& key); -  - // Signals  sigc::signal<void> changed; -  -private: - void Refresh(); - void SaveFavorites(DeviceList const& favorites); - - glib::Object<GSettings> settings_; - DeviceList favorites_; - bool ignore_signals_; - DevicesOption devices_option_;  }; -} // namespace unity +} +} -#endif // DEVICES_SETTINGS_H +#endif diff --git a/launcher/DevicesSettingsImp.cpp b/launcher/DevicesSettingsImp.cpp new file mode 100644 index 000000000..7933cd524 --- /dev/null +++ b/launcher/DevicesSettingsImp.cpp @@ -0,0 +1,151 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010-12 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#include <list> + +#include <gio/gio.h> +#include <NuxCore/Logger.h> + +#include "DevicesSettingsImp.h" +#include <UnityCore/GLibSignal.h> +#include <UnityCore/GLibWrapper.h> + +namespace unity +{ +namespace launcher +{ +namespace +{ + +nux::logging::Logger logger("unity.device.settings"); + +const std::string SETTINGS_NAME = "com.canonical.Unity.Devices"; +const std::string KEY_NAME = "blacklist"; + +} + +// +// Start private implementation +// +class DevicesSettingsImp::Impl +{ +public: + Impl(DevicesSettingsImp* parent) + : parent_(parent) + , settings_(g_settings_new(SETTINGS_NAME.c_str())) + { + DownloadBlacklist(); + ConnectSignals(); + } + + void ConnectSignals() + { + settings_changed_signal_.Connect(settings_, "changed::" + KEY_NAME, [this] (GSettings*, gchar*) { + DownloadBlacklist(); + parent_->changed.emit(); + }); + } + + void DownloadBlacklist() + { + std::shared_ptr<gchar*> downloaded_blacklist(g_settings_get_strv(settings_, KEY_NAME.c_str()), g_strfreev); + + blacklist_.clear(); + + auto downloaded_blacklist_raw = downloaded_blacklist.get(); + for (int i = 0; downloaded_blacklist_raw[i]; ++i) + blacklist_.push_back(downloaded_blacklist_raw[i]); + } + + void UploadBlacklist() + { + const int size = blacklist_.size(); + const char* blacklist_to_be_uploaded[size+1]; + + int index = 0; + for (auto item : blacklist_) + blacklist_to_be_uploaded[index++] = item.c_str(); + blacklist_to_be_uploaded[index] = nullptr; + + if (!g_settings_set_strv(settings_, KEY_NAME.c_str(), blacklist_to_be_uploaded)) + { + LOG_WARNING(logger) << "Saving blacklist failed."; + } + } + + bool IsABlacklistedDevice(std::string const& uuid) const + { + auto begin = std::begin(blacklist_); + auto end = std::end(blacklist_); + return std::find(begin, end, uuid) != end; + } + + void TryToBlacklist(std::string const& uuid) + { + if (uuid.empty() || IsABlacklistedDevice(uuid)) + return; + + blacklist_.push_back(uuid); + UploadBlacklist(); + } + + void TryToUnblacklist(std::string const& uuid) + { + if (uuid.empty() || !IsABlacklistedDevice(uuid)) + return; + + blacklist_.remove(uuid); + UploadBlacklist(); + } + + DevicesSettingsImp* parent_; + glib::Object<GSettings> settings_; + std::list<std::string> blacklist_; + glib::Signal<void, GSettings*, gchar*> settings_changed_signal_; + +}; + +// +// End private implementation +// + +DevicesSettingsImp::DevicesSettingsImp() + : pimpl(new Impl(this)) +{} + +DevicesSettingsImp::~DevicesSettingsImp() +{} + +bool DevicesSettingsImp::IsABlacklistedDevice(std::string const& uuid) const +{ + return pimpl->IsABlacklistedDevice(uuid); +} + +void DevicesSettingsImp::TryToBlacklist(std::string const& uuid) +{ + pimpl->TryToBlacklist(uuid); +} + +void DevicesSettingsImp::TryToUnblacklist(std::string const& uuid) +{ + pimpl->TryToUnblacklist(uuid); +} + +} // namespace launcher +} // namespace unity diff --git a/launcher/DevicesSettingsImp.h b/launcher/DevicesSettingsImp.h new file mode 100644 index 000000000..658ed4f71 --- /dev/null +++ b/launcher/DevicesSettingsImp.h @@ -0,0 +1,50 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_DEVICES_SETTINGS_IMP_H +#define UNITYSHELL_DEVICES_SETTINGS_IMP_H + +#include "DevicesSettings.h" + +namespace unity +{ +namespace launcher +{ + +class DevicesSettingsImp : public DevicesSettings +{ +public: + typedef std::shared_ptr<DevicesSettingsImp> Ptr;  + + DevicesSettingsImp(); + virtual ~DevicesSettingsImp(); + + virtual bool IsABlacklistedDevice(std::string const& uuid) const; + virtual void TryToBlacklist(std::string const& uuid); + virtual void TryToUnblacklist(std::string const& uuid); + +private: + class Impl; + std::unique_ptr<Impl> pimpl; +}; + +} +} + +#endif diff --git a/launcher/EdgeBarrierController.cpp b/launcher/EdgeBarrierController.cpp index 8bfa1ebac..4f549cc3d 100644 --- a/launcher/EdgeBarrierController.cpp +++ b/launcher/EdgeBarrierController.cpp @@ -155,6 +155,12 @@ void EdgeBarrierController::Impl::SetupBarriers(std::vector<nux::Geometry> const  void EdgeBarrierController::Impl::OnPointerBarrierEvent(PointerBarrierWrapper* owner, BarrierEvent::Ptr event)  { + if (owner->released) + { + BarrierRelease(owner, event->event_id); + return; + } +  unsigned int monitor = owner->index;  bool process = true; @@ -166,11 +172,7 @@ void EdgeBarrierController::Impl::OnPointerBarrierEvent(PointerBarrierWrapper* o  process = false;  } - if (process && owner->released) - { - BarrierRelease(owner, event->event_id); - } - else if (process && owner->x1 > 0) + if (process && owner->x1 > 0)  {  decaymulator_.value = decaymulator_.value + event->velocity; diff --git a/launcher/FileManagerOpener.h b/launcher/FileManagerOpener.h new file mode 100644 index 000000000..a318930bc --- /dev/null +++ b/launcher/FileManagerOpener.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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_FILEMANAGER_OPENER_H +#define UNITYSHELL_FILEMANAGER_OPENER_H + +#include <boost/noncopyable.hpp> +#include <memory> +#include <string> + +namespace unity +{ +namespace launcher +{ + +class FileManagerOpener : private boost::noncopyable +{ +public: + typedef std::shared_ptr<FileManagerOpener> Ptr; + + virtual ~FileManagerOpener() {} + + virtual void Open(std::string const& uri) = 0; +}; + +} +} + +#endif diff --git a/launcher/FileManagerOpenerImp.cpp b/launcher/FileManagerOpenerImp.cpp new file mode 100644 index 000000000..8fb2cd964 --- /dev/null +++ b/launcher/FileManagerOpenerImp.cpp @@ -0,0 +1,35 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#include <gio/gio.h> + +#include "FileManagerOpenerImp.h" + +namespace unity +{ +namespace launcher +{ + +void FileManagerOpenerImp::Open(std::string const& uri) +{ + g_app_info_launch_default_for_uri(uri. c_str(), nullptr, nullptr); +} + +} +} diff --git a/launcher/FileManagerOpenerImp.h b/launcher/FileManagerOpenerImp.h new file mode 100644 index 000000000..6a4d62562 --- /dev/null +++ b/launcher/FileManagerOpenerImp.h @@ -0,0 +1,39 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_FILEMANAGER_OPENER_IMP_H +#define UNITYSHELL_FILEMANAGER_OPENER_IMP_H + +#include "FileManagerOpener.h" + +namespace unity +{ +namespace launcher +{ + +class FileManagerOpenerImp : public FileManagerOpener +{ +public: + virtual void Open(std::string const& uri); +}; + +} +} + +#endif diff --git a/launcher/Launcher.cpp b/launcher/Launcher.cpp index 2ed206db8..7afeced30 100644 --- a/launcher/Launcher.cpp +++ b/launcher/Launcher.cpp @@ -92,7 +92,6 @@ const int MOUSE_DEADZONE = 15;  const float DRAG_OUT_PIXELS = 300.0f;  const std::string DND_CHECK_TIMEOUT = "dnd-check-timeout"; -const std::string STRUT_HACK_TIMEOUT = "strut-hack-timeout";  const std::string START_DRAGICON_TIMEOUT = "start-dragicon-timeout";  const std::string SCROLL_TIMEOUT = "scroll-timeout";  const std::string ANIMATION_IDLE = "animation-idle"; @@ -178,9 +177,8 @@ Launcher::Launcher(nux::BaseWindow* parent,  ql_manager.quicklist_closed.connect(sigc::mem_fun(this, &Launcher::RecvQuicklistClosed));  WindowManager& plugin_adapter = *(WindowManager::Default()); - plugin_adapter.window_mapped.connect(sigc::mem_fun(this, &Launcher::OnWindowMapped)); - plugin_adapter.window_unmapped.connect(sigc::mem_fun(this, &Launcher::OnWindowUnmapped)); - + plugin_adapter.window_mapped.connect(sigc::hide(sigc::mem_fun(this, &Launcher::DndTimeoutSetup))); + plugin_adapter.window_unmapped.connect(sigc::hide(sigc::mem_fun(this, &Launcher::DndTimeoutSetup)));  plugin_adapter.initiate_spread.connect(sigc::mem_fun(this, &Launcher::OnPluginStateChanged));  plugin_adapter.initiate_expo.connect(sigc::mem_fun(this, &Launcher::OnPluginStateChanged));  plugin_adapter.terminate_spread.connect(sigc::mem_fun(this, &Launcher::OnPluginStateChanged)); @@ -215,7 +213,7 @@ Launcher::Launcher(nux::BaseWindow* parent,  launcher_sheen_ = cache.FindTexture("dash_sheen", 0, 0, cb);  launcher_pressure_effect_ = cache.FindTexture("launcher_pressure_effect", 0, 0, cb); - options.changed.connect(sigc::mem_fun (this, &Launcher::OnOptionsChanged)); + options.changed.connect(sigc::mem_fun(this, &Launcher::OnOptionsChanged));  }  /* Introspection */ @@ -955,7 +953,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)); - color = color * (1.0f / max); + if (max > 0.0f) + { + color = color * (1.0f / max); + }  return color;  } @@ -1103,7 +1104,6 @@ void Launcher::RenderArgs(std::list<RenderArg> &launcher_args,  {  RenderArg arg;  AbstractLauncherIcon::Ptr icon = *it; -  FillRenderArg(icon, arg, center, parent_abs_geo, folding_threshold, folded_size, folded_spacing,  autohide_offset, folded_z_distance, animation_neg_rads, current);  arg.colorify = colorify; @@ -1394,27 +1394,6 @@ void Launcher::DndTimeoutSetup()  sources_.AddTimeout(200, cb_func, DND_CHECK_TIMEOUT);  } -void Launcher::OnWindowMapped(guint32 xid) -{ - //CompWindow* window = _screen->findWindow(xid); - //if (window && window->type() | CompWindowTypeDndMask) - //{ - DndTimeoutSetup(); - //} - - if (GetActionState() != ACTION_NONE) - ResetMouseDragState(); -} - -void Launcher::OnWindowUnmapped(guint32 xid) -{ - //CompWindow* window = _screen->findWindow(xid); - //if (window && window->type() | CompWindowTypeDndMask) - //{ - DndTimeoutSetup(); - //} -} -  void Launcher::OnPluginStateChanged()  {  _hide_machine.SetQuirk (LauncherHideMachine::EXPO_ACTIVE, WindowManager::Default ()->IsExpoActive ()); @@ -1428,21 +1407,9 @@ LauncherHideMode Launcher::GetHideMode() const  /* End Launcher Show/Hide logic */ -// Hacks around compiz failing to see the struts because the window was just mapped. -bool Launcher::StrutHack() -{ - _parent->InputWindowEnableStruts(false); - - if (options()->hide_mode == LAUNCHER_HIDE_NEVER) - _parent->InputWindowEnableStruts(true); - - return false; -} -  void Launcher::OnOptionsChanged(Options::Ptr options)  {  UpdateOptions(options); -  options->option_changed.connect(sigc::mem_fun(this, &Launcher::OnOptionChanged));  } @@ -1473,26 +1440,9 @@ void Launcher::ConfigureBarrier()  void Launcher::SetHideMode(LauncherHideMode hidemode)  { - if (hidemode != LAUNCHER_HIDE_NEVER) - { - _parent->InputWindowEnableStruts(false); - } - else - { - static bool first_time = true; - - _parent->EnableInputWindow(true, launcher::window_title, false, false); - - if (first_time && !sources_.GetSource(STRUT_HACK_TIMEOUT)) - { - sources_.AddTimeout(1000, sigc::mem_fun(this, &Launcher::StrutHack), STRUT_HACK_TIMEOUT); - first_time = false; - } - - _parent->InputWindowEnableStruts(true); - } - - _hide_machine.SetMode((LauncherHideMachine::HideMode) hidemode); + bool fixed_launcher = (hidemode == LAUNCHER_HIDE_NEVER); + _parent->InputWindowEnableStruts(fixed_launcher); + _hide_machine.SetMode(static_cast<LauncherHideMachine::HideMode>(hidemode));  EnsureAnimation();  } @@ -1719,6 +1669,11 @@ 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(); @@ -1798,9 +1753,8 @@ void Launcher::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw)  if (_drag_icon && _render_drag_window)  {  RenderIconToTexture(GfxContext, _drag_icon, _offscreen_drag_texture); - _drag_window->ShowWindow(true); -  _render_drag_window = false; + ShowDragWindow();  }  // clear region @@ -1995,9 +1949,7 @@ long Launcher::PostLayoutManagement(long LayoutResult)  void Launcher::OnDragWindowAnimCompleted()  { - if (_drag_window) - _drag_window->ShowWindow(false); - + HideDragWindow();  EnsureAnimation();  } @@ -2029,7 +1981,8 @@ void Launcher::StartIconDragRequest(int x, int y)  // FIXME: nux doesn't give nux::GetEventButton (button_flags) there, relying  // on an internal Launcher property then - if (drag_icon && _last_button_press == 1 && _model->IconHasSister(drag_icon)) + bool can_drag = (_model->IconHasSister(drag_icon) || drag_icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE); + if (drag_icon && _last_button_press == 1 && can_drag)  {  SetActionState(ACTION_DRAG_ICON);  StartIconDrag(drag_icon); @@ -2043,12 +1996,8 @@ void Launcher::StartIconDragRequest(int x, int y)  }  else  { - _drag_icon = NULL; - if (_drag_window) - { - _drag_window->ShowWindow(false); - _drag_window = nullptr; - } + _drag_icon = nullptr; + HideDragWindow();  }  } @@ -2060,11 +2009,7 @@ void Launcher::StartIconDrag(AbstractLauncherIcon::Ptr icon)  _hide_machine.SetQuirk(LauncherHideMachine::INTERNAL_DND_ACTIVE, true);  _drag_icon = icon; - if (_drag_window) - { - _drag_window->ShowWindow(false); - } - + HideDragWindow();  _offscreen_drag_texture = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableDeviceTexture(_icon_size, _icon_size, 1, nux::BITFMT_R8G8B8A8);  _drag_window = new LauncherDragWindow(_offscreen_drag_texture); @@ -2077,7 +2022,10 @@ void Launcher::EndIconDrag()  {  if (_drag_window)  { - AbstractLauncherIcon::Ptr hovered_icon = MouseIconIntersection(_mouse_position.x, _mouse_position.y); + AbstractLauncherIcon::Ptr hovered_icon; + + if (!_drag_window->Cancelled()) + hovered_icon = MouseIconIntersection(_mouse_position.x, _mouse_position.y);  if (hovered_icon && hovered_icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH)  { @@ -2085,20 +2033,21 @@ void Launcher::EndIconDrag()  launcher_removerequest.emit(_drag_icon); - _drag_window->ShowWindow(false); + HideDragWindow();  EnsureAnimation();  }  else  { - _model->Save(); - - _drag_window->SetAnimationTarget((int)(_drag_icon->GetCenter(monitor).x), - (int)(_drag_icon->GetCenter(monitor).y)); - _drag_window->StartAnimation(); + if (!_drag_window->Cancelled()) + _model->Save();  if (_drag_window->on_anim_completed.connected())  _drag_window->on_anim_completed.disconnect();  _drag_window->on_anim_completed = _drag_window->anim_completed.connect(sigc::mem_fun(this, &Launcher::OnDragWindowAnimCompleted)); + + auto const& icon_center = _drag_icon->GetCenter(monitor); + _drag_window->SetAnimationTarget(icon_center.x, icon_center.y), + _drag_window->StartAnimation();  }  } @@ -2111,61 +2060,87 @@ void Launcher::EndIconDrag()  ubus_.SendMessage(UBUS_LAUNCHER_ICON_END_DND);  } +void Launcher::ShowDragWindow() +{ + if (!_drag_window || _drag_window->IsVisible()) + return; + + _drag_window->GrabKeyboard(); + _drag_window->ShowWindow(true); + _drag_window->PushToFront(); + + bool is_before; + AbstractLauncherIcon::Ptr const& closer = _model->GetClosestIcon(_drag_icon, is_before); + _drag_window->drag_cancel_request.connect([this, closer, is_before] { + if (is_before) + _model->ReorderAfter(_drag_icon, closer); + else + _model->ReorderBefore(_drag_icon, closer, true); + + ResetMouseDragState(); + }); +} + +void Launcher::HideDragWindow() +{ + if (!_drag_window) + return; + + _drag_window->UnGrabKeyboard(); + _drag_window->ShowWindow(false); + _drag_window = nullptr; +} +  void Launcher::UpdateDragWindowPosition(int x, int y)  { - if (_drag_window) - { - nux::Geometry const& geo = _drag_window->GetGeometry(); - _drag_window->SetBaseXY(x - geo.width / 2, y - geo.height / 2); + if (!_drag_window) + return; + + auto const& icon_geo = _drag_window->GetGeometry(); + _drag_window->SetBaseXY(x - icon_geo.width / 2, y - icon_geo.height / 2); + + if (!_drag_icon) + return; + + auto const& launcher_geo = GetGeometry(); + auto 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); - AbstractLauncherIcon::Ptr hovered_icon = MouseIconIntersection((int)((GetGeometry().x + GetGeometry().width) / 2.0f), y - GetAbsoluteGeometry().y); + // 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; - struct timespec current; - clock_gettime(CLOCK_MONOTONIC, ¤t); - if (_drag_icon && hovered_icon && _drag_icon != hovered_icon) + if (hovered_icon && _drag_icon != hovered_icon) + { + if (progress >= 1.0f)  { - float progress = DragThresholdProgress(current); - - if (progress >= 1.0f) - _model->ReorderSmart(_drag_icon, hovered_icon, true); - else if (progress == 0.0f) { - if (_drag_icon->GetIconType() == hovered_icon->GetIconType()) { - _model->ReorderBefore(_drag_icon, hovered_icon, false); - } else { - // LauncherModel::ReorderBefore does not work on different icon types - // so if hovered_icon is of a different type than _drag_icon - // try to use LauncherModel::ReorderAfter with the icon that is before hovered_icon - AbstractLauncherIcon::Ptr iconBeforeHover; - LauncherModel::iterator it; - LauncherModel::iterator prevIt = _model->end(); - for (it = _model->begin(); it != _model->end(); ++it) - { - if (!(*it)->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE) || !(*it)->IsVisibleOnMonitor(monitor)) - continue; - - if ((*it) == hovered_icon) { - if (prevIt != _model->end()) { - iconBeforeHover = *prevIt; - } - break; - } - - prevIt = it; - } - - if (iconBeforeHover && _drag_icon != iconBeforeHover) { - _model->ReorderAfter(_drag_icon, iconBeforeHover); - } - } - } + _model->ReorderSmart(_drag_icon, hovered_icon, true); + } + else if (progress == 0.0f) + { + _model->ReorderBefore(_drag_icon, hovered_icon, false); + } + } + else if (!hovered_icon && progress == 0.0f) + { + // If no icon is hovered, then we can add our icon to the bottom + for (auto it = _model->main_rbegin(); it != _model->main_rend(); ++it) + { + auto const& icon = *it; - if (progress >= 1.0f) + if (!icon->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE) || + !icon->IsVisibleOnMonitor(monitor) || + icon->GetIconType() != _drag_icon->GetIconType())  { - _model->ReorderSmart(_drag_icon, hovered_icon, true); + continue;  } - else if (progress == 0.0f) + + if (y >= icon->GetCenter(monitor).y)  { - _model->ReorderBefore(_drag_icon, hovered_icon, false); + _model->ReorderAfter(_drag_icon, icon); + break;  }  }  } @@ -2581,7 +2556,7 @@ void Launcher::OnDNDDataCollected(const std::list<char*>& mimes)  for (auto it : _dnd_data.Uris())  { - if (g_str_has_suffix(it.c_str(), ".desktop")) + if (g_str_has_suffix(it.c_str(), ".desktop") || g_str_has_prefix(it.c_str(), "device://"))  {  _steal_drag = true;  break; @@ -2631,7 +2606,18 @@ void Launcher::DndReset()  for (auto it : *_model)  { - it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, is_overlay_open); + auto icon_type = it->GetIconType(); + + if (icon_type == AbstractLauncherIcon::IconType::HOME || + icon_type == AbstractLauncherIcon::IconType::HUD) + { + it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, false); + } + else + { + it->SetQuirk(AbstractLauncherIcon::Quirk::DESAT, is_overlay_open && !_hovered); + } +  it->SetQuirk(AbstractLauncherIcon::Quirk::PRESENTED, false);  } @@ -2686,7 +2672,7 @@ void Launcher::ProcessDndMove(int x, int y, std::list<char*> mimes)  // see if the launcher wants this one  for (auto it : _dnd_data.Uris())  { - if (g_str_has_suffix(it.c_str(), ".desktop")) + if (g_str_has_suffix(it.c_str(), ".desktop") || g_str_has_prefix(it.c_str(), "device://"))  {  _steal_drag = true;  break; @@ -2824,6 +2810,11 @@ void Launcher::ProcessDndDrop(int x, int y)  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); + }  }  }  else if (_dnd_hovered_icon && _drag_action != nux::DNDACTION_NONE) diff --git a/launcher/Launcher.h b/launcher/Launcher.h index 04524bd35..b4b1dad00 100644 --- a/launcher/Launcher.h +++ b/launcher/Launcher.h @@ -30,6 +30,7 @@  #include "PointerBarrier.h"  #include "unity-shared/AbstractIconRenderer.h"  #include "unity-shared/BackgroundEffectHelper.h" +#include "DevicesSettings.h"  #include "DNDCollectionWindow.h"  #include "DndData.h"  #include "EdgeBarrierController.h" @@ -82,6 +83,8 @@ public:  void SetModel(LauncherModel::Ptr model);  LauncherModel::Ptr GetModel() const; + void SetDevicesSettings(DevicesSettings::Ptr devices_settings); +  void StartKeyShowLauncher();  void EndKeyShowLauncher(); @@ -185,9 +188,6 @@ private:  void OnOptionChanged();  void UpdateOptions(Options::Ptr options); - void OnWindowMapped(guint32 xid); - void OnWindowUnmapped(guint32 xid); -  void OnDragStart(const nux::GestureEvent &event);  void OnDragUpdate(const nux::GestureEvent &event);  void OnDragFinish(const nux::GestureEvent &event); @@ -291,7 +291,7 @@ private:  void OnActionDone(GVariant* data); - AbstractLauncherIcon::Ptr MouseIconIntersection(int x, int y); + virtual AbstractLauncherIcon::Ptr MouseIconIntersection(int x, int y);  void EventLogic();  void MouseDownLogic(int x, int y, unsigned long button_flags, unsigned long key_flags);  void MouseUpLogic(int x, int y, unsigned long button_flags, unsigned long key_flags); @@ -299,7 +299,9 @@ private:  void StartIconDragRequest(int x, int y);  void StartIconDrag(AbstractLauncherIcon::Ptr icon);  void EndIconDrag(); + void ShowDragWindow();  void UpdateDragWindowPosition(int x, int y); + void HideDragWindow();  void ResetMouseDragState(); @@ -393,6 +395,8 @@ 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 87a3ce6e4..bcc372fcb 100644 --- a/launcher/LauncherController.cpp +++ b/launcher/LauncherController.cpp @@ -29,7 +29,7 @@  #include "LauncherOptions.h"  #include "BamfLauncherIcon.h"  #include "DesktopLauncherIcon.h" -#include "DeviceLauncherIcon.h" +#include "VolumeLauncherIcon.h"  #include "FavoriteStore.h"  #include "HudLauncherIcon.h"  #include "LauncherController.h" @@ -95,7 +95,8 @@ Controller::Impl::Impl(Display* display, Controller* parent)  , model_(new LauncherModel())  , sort_priority_(0)  , volume_monitor_(new VolumeMonitorWrapper) - , device_section_(volume_monitor_) + , devices_settings_(new DevicesSettingsImp) + , device_section_(volume_monitor_, devices_settings_)  , show_desktop_icon_(false)  , display_(display)  , matcher_(bamf_matcher_get_default()) @@ -267,6 +268,7 @@ Launcher* Controller::Impl::CreateLauncher(int monitor)  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); @@ -278,7 +280,7 @@ Launcher* Controller::Impl::CreateLauncher(int monitor)  launcher_window->SetBackgroundColor(nux::color::Transparent);  launcher_window->ShowWindow(true);  launcher_window->EnableInputWindow(true, launcher::window_title, false, false); - launcher_window->InputWindowEnableStruts(false); + launcher_window->InputWindowEnableStruts(parent_->options()->hide_mode == LAUNCHER_HIDE_NEVER);  launcher_window->SetEnterFocusInputArea(launcher);  launcher->launcher_addrequest.connect(sigc::mem_fun(this, &Impl::OnLauncherAddRequest)); @@ -424,10 +426,10 @@ void Controller::Impl::OnLauncherRemoveRequest(AbstractLauncherIcon::Ptr icon)  }  case AbstractLauncherIcon::IconType::DEVICE:  { - DeviceLauncherIcon* device_icon = dynamic_cast<DeviceLauncherIcon*>(icon.GetPointer()); + auto device_icon = dynamic_cast<VolumeLauncherIcon*>(icon.GetPointer());  if (device_icon && device_icon->CanEject()) - device_icon->Eject(); + device_icon->EjectAndShowNotification();  else if (device_icon && device_icon->CanStop())  device_icon->StopDrive(); @@ -686,10 +688,6 @@ void Controller::Impl::SetupBamf()  GList* apps, *l;  BamfApplication* app; - // Sufficiently large number such that we ensure proper sorting - // (avoids case where first item gets tacked onto end rather than start) - int priority = 100; -  FavoriteList const& favs = FavoriteStore::Instance().GetFavorites();  for (FavoriteList::const_iterator i = favs.begin(), end = favs.end(); @@ -699,9 +697,8 @@ void Controller::Impl::SetupBamf()  if (fav)  { - fav->SetSortPriority(priority); + fav->SetSortPriority(sort_priority_++);  RegisterIcon(fav); - priority++;  }  } diff --git a/launcher/LauncherController.h b/launcher/LauncherController.h index 3e5dec6b0..e18f2cae2 100644 --- a/launcher/LauncherController.h +++ b/launcher/LauncherController.h @@ -32,6 +32,7 @@ namespace unity  {  namespace launcher  { +  class AbstractLauncherIcon;  class Launcher;  class LauncherModel; diff --git a/launcher/LauncherControllerPrivate.h b/launcher/LauncherControllerPrivate.h index 2eb1a1f9a..cd730a37e 100644 --- a/launcher/LauncherControllerPrivate.h +++ b/launcher/LauncherControllerPrivate.h @@ -27,6 +27,7 @@  #include "AbstractLauncherIcon.h"  #include "DeviceLauncherSection.h" +#include "DevicesSettingsImp.h"  #include "EdgeBarrierController.h"  #include "LauncherController.h"  #include "Launcher.h" @@ -126,6 +127,7 @@ public:  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_; diff --git a/launcher/LauncherDragWindow.cpp b/launcher/LauncherDragWindow.cpp index b78e84132..53334ab89 100644 --- a/launcher/LauncherDragWindow.cpp +++ b/launcher/LauncherDragWindow.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,6 +15,7 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Jason Smith <jason.smith@canonical.com> +* Marco Trevisan <marco.trevisan@canonical.com>  */  #include <Nux/Nux.h> @@ -23,6 +24,7 @@  #include <Nux/TextureArea.h>  #include "LauncherDragWindow.h" +#include "unity-shared/WindowManager.h"  namespace unity  { @@ -33,22 +35,45 @@ NUX_IMPLEMENT_OBJECT_TYPE(LauncherDragWindow);  LauncherDragWindow::LauncherDragWindow(nux::ObjectPtr<nux::IOpenGLBaseTexture> icon)  : nux::BaseWindow("") + , _cancelled(false)  , _icon(icon)  {  SetBaseSize(_icon->GetWidth(), _icon->GetHeight()); + + key_down.connect([this] (unsigned long, unsigned long keysym, unsigned long, const char*, unsigned short) { + if (keysym == NUX_VK_ESCAPE) + CancelDrag(); + }); + + auto wm = WindowManager::Default(); + wm->window_mapped.connect(sigc::hide(sigc::mem_fun(this, &LauncherDragWindow::CancelDrag))); + wm->window_unmapped.connect(sigc::hide(sigc::mem_fun(this, &LauncherDragWindow::CancelDrag)));  }  LauncherDragWindow::~LauncherDragWindow()  {  if (on_anim_completed.connected())  on_anim_completed.disconnect(); + + UnGrabKeyboard();  } -bool LauncherDragWindow::Animating() +bool LauncherDragWindow::Animating() const  {  return bool(animation_timer_);  } +bool LauncherDragWindow::Cancelled() const +{ + return _cancelled; +} + +void LauncherDragWindow::CancelDrag() +{ + _cancelled = true; + drag_cancel_request.emit(); +} +  void LauncherDragWindow::SetAnimationTarget(int x, int y)  {  _animation_target = nux::Point2(x, y); @@ -86,8 +111,8 @@ bool LauncherDragWindow::OnAnimationTimeout()  if (new_geo.x == target_x && new_geo.y == target_y)  { - anim_completed.emit();  animation_timer_.reset(); + anim_completed.emit();  return false;  } @@ -118,5 +143,15 @@ LauncherDragWindow::DrawContent(nux::GraphicsEngine& GfxContext, bool force_draw  GfxContext.PopClippingRectangle();  } +bool LauncherDragWindow::InspectKeyEvent(unsigned int event_type, unsigned int keysym, const char* character) +{ + return (event_type == nux::NUX_KEYDOWN); +} + +bool LauncherDragWindow::AcceptKeyNavFocus() +{ + return true; +} +  } // namespace launcher  } // namespace unity diff --git a/launcher/LauncherDragWindow.h b/launcher/LauncherDragWindow.h index 51041d36c..cda37d342 100644 --- a/launcher/LauncherDragWindow.h +++ b/launcher/LauncherDragWindow.h @@ -37,7 +37,6 @@ class LauncherDragWindow : public nux::BaseWindow  NUX_DECLARE_OBJECT_TYPE(LauncherDragWindow, nux::BaseWindow);  public:  LauncherDragWindow(nux::ObjectPtr<nux::IOpenGLBaseTexture> icon); -  ~LauncherDragWindow();  void DrawContent(nux::GraphicsEngine& gfxContext, bool forceDraw); @@ -45,14 +44,22 @@ public:  void SetAnimationTarget(int x, int y);  void StartAnimation(); - bool Animating(); + bool Animating() const; + bool Cancelled() const;  sigc::signal<void> anim_completed; + sigc::signal<void> drag_cancel_request;  sigc::connection on_anim_completed; +protected: + bool InspectKeyEvent(unsigned int event_type, unsigned int keysym, const char* character); + bool AcceptKeyNavFocus(); +  private:  bool OnAnimationTimeout(); + void CancelDrag(); + bool _cancelled;  nux::ObjectPtr<nux::IOpenGLBaseTexture> _icon;  nux::Point2 _animation_target;  glib::Source::UniquePtr animation_timer_; diff --git a/launcher/LauncherModel.cpp b/launcher/LauncherModel.cpp index 15583508b..8e01465dd 100644 --- a/launcher/LauncherModel.cpp +++ b/launcher/LauncherModel.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,6 +15,7 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Jason Smith <jason.smith@canonical.com> + * Marco Trevisan <marco.trevisan@canonical.com>  */  #include "LauncherModel.h" @@ -30,8 +31,7 @@ namespace launcher  LauncherModel::LauncherModel()  : selection_(0) -{ -} +{}  std::string LauncherModel::GetName() const  { @@ -54,12 +54,12 @@ unity::debug::Introspectable::IntrospectableList LauncherModel::GetIntrospectabl  return introspection_results_;  } -bool LauncherModel::IconShouldShelf(AbstractLauncherIcon::Ptr icon) const +bool LauncherModel::IconShouldShelf(AbstractLauncherIcon::Ptr const& icon) const  {  return icon->GetIconType() == AbstractLauncherIcon::IconType::TRASH;  } -bool LauncherModel::CompareIcons(AbstractLauncherIcon::Ptr first, AbstractLauncherIcon::Ptr second) +bool LauncherModel::CompareIcons(AbstractLauncherIcon::Ptr const& first, AbstractLauncherIcon::Ptr const& second)  {  if (first->GetIconType() < second->GetIconType())  return true; @@ -69,36 +69,43 @@ bool LauncherModel::CompareIcons(AbstractLauncherIcon::Ptr first, AbstractLaunch  return first->SortPriority() < second->SortPriority();  } -bool -LauncherModel::Populate() +void LauncherModel::PopulatePart(iterator begin, iterator end)  { - Base copy = _inner; - - _inner.clear(); + AbstractLauncherIcon::Ptr prev_icon; + for (auto it = begin; it != end; ++it) + { + auto const& icon = *it; + _inner.push_back(icon); - iterator it, it2; + if (prev_icon) + { + // Ensuring that the current icon has higher priority than previous one + if (icon->SortPriority() < prev_icon->SortPriority()) + { + int new_priority = prev_icon->SortPriority() + 1; + icon->SetSortPriority(new_priority); + } + } - int i = 0; - for (it = main_begin(); it != main_end(); ++it) - { - _inner.push_back(*it); - (*it)->SetSortPriority(i); - ++i; + prev_icon = icon;  } +} - for (it = shelf_begin(); it != shelf_end(); ++it) - { - _inner.push_back(*it); - (*it)->SetSortPriority(i); - ++i; - } +bool LauncherModel::Populate() +{ + Base copy = _inner; + _inner.clear(); + PopulatePart(main_begin(), main_end()); + PopulatePart(shelf_begin(), shelf_end());  return copy.size() == _inner.size() && !std::equal(begin(), end(), copy.begin());  } -void -LauncherModel::AddIcon(AbstractLauncherIcon::Ptr icon) +void LauncherModel::AddIcon(AbstractLauncherIcon::Ptr const& icon)  { + if (!icon || std::find(begin(), end(), icon) != end()) + return; +  if (IconShouldShelf(icon))  _inner_shelf.push_back(icon);  else @@ -113,8 +120,7 @@ LauncherModel::AddIcon(AbstractLauncherIcon::Ptr icon)  icon->on_icon_removed_connection = icon->remove.connect(sigc::mem_fun(this, &LauncherModel::OnIconRemove));  } -void -LauncherModel::RemoveIcon(AbstractLauncherIcon::Ptr icon) +void LauncherModel::RemoveIcon(AbstractLauncherIcon::Ptr const& icon)  {  size_t size; @@ -130,23 +136,20 @@ LauncherModel::RemoveIcon(AbstractLauncherIcon::Ptr icon)  }  } -void -LauncherModel::OnIconRemove(AbstractLauncherIcon::Ptr icon) +void LauncherModel::OnIconRemove(AbstractLauncherIcon::Ptr const& icon)  { - timeouts_.AddTimeout(1000, [&, icon] { + timeouts_.AddTimeout(1000, [this, icon] {  RemoveIcon(icon);  return false;  });  } -void -LauncherModel::Save() +void LauncherModel::Save()  {  saved.emit();  } -void -LauncherModel::Sort() +void LauncherModel::Sort()  {  std::stable_sort(_inner_shelf.begin(), _inner_shelf.end(), &LauncherModel::CompareIcons);  std::stable_sort(_inner_main.begin(), _inner_main.end(), &LauncherModel::CompareIcons); @@ -155,15 +158,14 @@ LauncherModel::Sort()  order_changed.emit();  } -bool -LauncherModel::IconHasSister(AbstractLauncherIcon::Ptr icon) const +bool LauncherModel::IconHasSister(AbstractLauncherIcon::Ptr const& icon) const  { + if (!icon) + return false; +  const_iterator it;  const_iterator end; - if (icon && icon->GetIconType() == AbstractLauncherIcon::IconType::DEVICE) - return true; -  if (IconShouldShelf(icon))  {  it = _inner_shelf.begin(); @@ -177,17 +179,16 @@ LauncherModel::IconHasSister(AbstractLauncherIcon::Ptr icon) const  for (; it != end; ++it)  { - AbstractLauncherIcon::Ptr iter_icon = *it; - if ((iter_icon != icon) - && iter_icon->GetIconType() == icon->GetIconType()) + AbstractLauncherIcon::Ptr const& iter_icon = *it; + + if (iter_icon != icon && iter_icon->GetIconType() == icon->GetIconType())  return true;  }  return false;  } -void -LauncherModel::ReorderAfter(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon::Ptr other) +void LauncherModel::ReorderAfter(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other)  {  if (icon == other || icon.IsNull() || other.IsNull())  return; @@ -195,32 +196,20 @@ LauncherModel::ReorderAfter(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon  if (icon->GetIconType() != other->GetIconType())  return; - int i = 0; - for (LauncherModel::iterator it = begin(); it != end(); ++it) - { - if ((*it) == icon) - continue; + icon->SetSortPriority(other->SortPriority() + 1); - if ((*it) == other) - { - (*it)->SetSortPriority(i); - ++i; - - icon->SetSortPriority(i); - ++i; - } - else - { - (*it)->SetSortPriority(i); - ++i; - } + 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; + icon_it->SetSortPriority(new_priority);  }  Sort();  } -void -LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon::Ptr other, bool save) +void LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate)  {  if (icon == other || icon.IsNull() || other.IsNull())  return; @@ -228,43 +217,45 @@ LauncherModel::ReorderBefore(AbstractLauncherIcon::Ptr icon, AbstractLauncherIco  if (icon->GetIconType() != other->GetIconType())  return; - int i = 0; - int j = 0; - for (auto icon_it : _inner) + bool found_target = false; + bool center = false; + + for (auto const& icon_it : _inner)  {  if (icon_it == icon)  { - j++; + center = !center;  continue;  } + int new_priority = icon_it->SortPriority() + (found_target ? 1 : -1); + icon_it->SetSortPriority(new_priority); +  if (icon_it == other)  { - icon->SetSortPriority(i); - if (i != j && save) + if (animate && center)  icon_it->SaveCenter(); - i++; - icon_it->SetSortPriority(i); - if (i != j && save) + center = !center; + new_priority = new_priority - 1; + icon->SetSortPriority(new_priority); + + if (animate && center)  icon_it->SaveCenter(); - i++; + + found_target = true;  }  else  { - icon_it->SetSortPriority(i); - if (i != j && save) + if (animate && center)  icon_it->SaveCenter(); - i++;  } - j++;  }  Sort();  } -void -LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon::Ptr other, bool save) +void LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate)  {  if (icon == other || icon.IsNull() || other.IsNull())  return; @@ -272,49 +263,41 @@ LauncherModel::ReorderSmart(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon  if (icon->GetIconType() != other->GetIconType())  return; - int i = 0; - int j = 0; - bool skipped = false; - for (auto icon_it : _inner) + bool found_icon = false; + bool found_target = false; + bool center = false; + + for (auto const& icon_it : _inner)  {  if (icon_it == icon)  { - skipped = true; - j++; + found_icon = true; + center = !center;  continue;  } + int new_priority = icon_it->SortPriority() + (found_target ? 1 : -1); + icon_it->SetSortPriority(new_priority); +  if (icon_it == other)  { - if (!skipped) - { - icon->SetSortPriority(i); - if (i != j && save) - icon_it->SaveCenter(); - i++; - } + if (animate && center) + icon_it->SaveCenter(); + + center = !center; + new_priority = new_priority + (found_icon ? 1 : -1); + icon->SetSortPriority(new_priority); - icon_it->SetSortPriority(i); - if (i != j && save) + if (animate && center)  icon_it->SaveCenter(); - i++; - if (skipped) - { - icon->SetSortPriority(i); - if (i != j && save) - icon_it->SaveCenter(); - i++; - } + found_target = true;  }  else  { - icon_it->SetSortPriority(i); - if (i != j && save) + if (animate && center)  icon_it->SaveCenter(); - i++;  } - j++;  }  Sort(); @@ -357,7 +340,7 @@ void LauncherModel::SelectNext()  if (temp >= Size())  temp = 0; - if (_inner[temp]->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)) + if (_inner[temp]->IsVisible())  {  selection_ = temp;  selection_changed.emit(Selection()); @@ -377,7 +360,7 @@ void LauncherModel::SelectPrevious()  if (temp < 0)  temp = Size() - 1; - if (_inner[temp]->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)) + if (_inner[temp]->IsVisible())  {  selection_ = temp;  selection_changed.emit(Selection()); @@ -387,23 +370,74 @@ void LauncherModel::SelectPrevious()  }  } +AbstractLauncherIcon::Ptr LauncherModel::GetClosestIcon(AbstractLauncherIcon::Ptr const& icon, bool& is_before) const +{ + AbstractLauncherIcon::Ptr prev, next; + bool found_target = false; + + for (auto const& current : _inner) + { + if (current->GetIconType() != icon->GetIconType()) + continue; + + if (!found_target) + { + if (current == icon) + { + found_target = true; + + if (prev) + break; + } + else + { + prev = current; + } + } + else + { + next = current; + break; + } + } + + is_before = next.IsNull(); + + return is_before ? prev : next; +} + +int LauncherModel::IconIndex(AbstractLauncherIcon::Ptr const& target) const +{ + int pos = 0; + bool found = false; + + for (auto const& icon : _inner) + { + if (icon == target) + { + found = true; + break; + } + + ++pos; + } + + return found ? pos : -1; +}  /* iterators */ -LauncherModel::iterator -LauncherModel::begin() +LauncherModel::iterator LauncherModel::begin()  {  return _inner.begin();  } -LauncherModel::iterator -LauncherModel::end() +LauncherModel::iterator LauncherModel::end()  {  return _inner.end();  } -LauncherModel::iterator -LauncherModel::at(int index) +LauncherModel::iterator LauncherModel::at(int index)  {  LauncherModel::iterator it;  int i; @@ -418,62 +452,52 @@ LauncherModel::at(int index)  return (LauncherModel::iterator)NULL;  } -LauncherModel::reverse_iterator -LauncherModel::rbegin() +LauncherModel::reverse_iterator LauncherModel::rbegin()  {  return _inner.rbegin();  } -LauncherModel::reverse_iterator -LauncherModel::rend() +LauncherModel::reverse_iterator LauncherModel::rend()  {  return _inner.rend();  } -LauncherModel::iterator -LauncherModel::main_begin() +LauncherModel::iterator LauncherModel::main_begin()  {  return _inner_main.begin();  } -LauncherModel::iterator -LauncherModel::main_end() +LauncherModel::iterator LauncherModel::main_end()  {  return _inner_main.end();  } -LauncherModel::reverse_iterator -LauncherModel::main_rbegin() +LauncherModel::reverse_iterator LauncherModel::main_rbegin()  {  return _inner_main.rbegin();  } -LauncherModel::reverse_iterator -LauncherModel::main_rend() +LauncherModel::reverse_iterator LauncherModel::main_rend()  {  return _inner_main.rend();  } -LauncherModel::iterator -LauncherModel::shelf_begin() +LauncherModel::iterator LauncherModel::shelf_begin()  {  return _inner_shelf.begin();  } -LauncherModel::iterator -LauncherModel::shelf_end() +LauncherModel::iterator LauncherModel::shelf_end()  {  return _inner_shelf.end();  } -LauncherModel::reverse_iterator -LauncherModel::shelf_rbegin() +LauncherModel::reverse_iterator LauncherModel::shelf_rbegin()  {  return _inner_shelf.rbegin();  } -LauncherModel::reverse_iterator -LauncherModel::shelf_rend() +LauncherModel::reverse_iterator LauncherModel::shelf_rend()  {  return _inner_shelf.rend();  } diff --git a/launcher/LauncherModel.h b/launcher/LauncherModel.h index e6295b6cf..19385e251 100644 --- a/launcher/LauncherModel.h +++ b/launcher/LauncherModel.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: Jason Smith <jason.smith@canonical.com> + * Marco Trevisan <marco.trevisan@canonical.com>  */  #ifndef LAUNCHERMODEL_H @@ -41,20 +42,18 @@ public:  LauncherModel(); - void AddIcon(AbstractLauncherIcon::Ptr icon); - void RemoveIcon(AbstractLauncherIcon::Ptr icon); + void AddIcon(AbstractLauncherIcon::Ptr const& icon); + void RemoveIcon(AbstractLauncherIcon::Ptr const& icon);  void Save();  void Sort();  int Size() const; - void OnIconRemove(AbstractLauncherIcon::Ptr icon); + bool IconHasSister(AbstractLauncherIcon::Ptr const& icon) const; + int IconIndex(AbstractLauncherIcon::Ptr const& icon) const; - bool IconHasSister(AbstractLauncherIcon::Ptr icon) const; - - void ReorderAfter(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon::Ptr other); - void ReorderBefore(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon::Ptr other, bool save); - - void ReorderSmart(AbstractLauncherIcon::Ptr icon, AbstractLauncherIcon::Ptr other, bool save); + void ReorderAfter(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other); + void ReorderBefore(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate); + void ReorderSmart(AbstractLauncherIcon::Ptr const& icon, AbstractLauncherIcon::Ptr const& other, bool animate);  AbstractLauncherIcon::Ptr Selection() const;  int SelectionIndex() const; @@ -62,6 +61,8 @@ public:  void SelectNext();  void SelectPrevious(); + AbstractLauncherIcon::Ptr GetClosestIcon(AbstractLauncherIcon::Ptr const& icon, bool& is_before) const; +  iterator begin();  iterator end();  iterator at(int index); @@ -78,17 +79,17 @@ public:  reverse_iterator shelf_rbegin();  reverse_iterator shelf_rend(); - sigc::signal<void, AbstractLauncherIcon::Ptr> icon_added; - sigc::signal<void, AbstractLauncherIcon::Ptr> icon_removed; + sigc::signal<void, AbstractLauncherIcon::Ptr const&> icon_added; + sigc::signal<void, AbstractLauncherIcon::Ptr const&> icon_removed; + sigc::signal<void, AbstractLauncherIcon::Ptr const&> selection_changed;  sigc::signal<void> order_changed;  sigc::signal<void> saved; - sigc::signal<void, AbstractLauncherIcon::Ptr> selection_changed; - IntrospectableList GetIntrospectableChildren();  protected:  // Introspectable methods  std::string GetName() const;  void AddProperties(GVariantBuilder* builder); + IntrospectableList GetIntrospectableChildren();  private:  Base _inner; @@ -99,10 +100,10 @@ private:  glib::SourceManager timeouts_;  bool Populate(); - - bool IconShouldShelf(AbstractLauncherIcon::Ptr icon) const; - - static bool CompareIcons(AbstractLauncherIcon::Ptr first, AbstractLauncherIcon::Ptr second); + void PopulatePart(iterator begin, iterator end); + void OnIconRemove(AbstractLauncherIcon::Ptr const& icon); + bool IconShouldShelf(AbstractLauncherIcon::Ptr const& icon) const; + static bool CompareIcons(AbstractLauncherIcon::Ptr const& first, AbstractLauncherIcon::Ptr const& second);  /* Template Methods */  public: diff --git a/launcher/MockLauncherIcon.h b/launcher/MockLauncherIcon.h index ba3f30ce4..b52cb272b 100644 --- a/launcher/MockLauncherIcon.h +++ b/launcher/MockLauncherIcon.h @@ -44,12 +44,12 @@ class MockLauncherIcon : public AbstractLauncherIcon  {  NUX_DECLARE_OBJECT_TYPE(MockLauncherIcon, AbstractLauncherIcon);  public: - MockLauncherIcon() - : icon_(0) + MockLauncherIcon(IconType type = IconType::APPLICATION) + : type_(type) + , sort_priority_(0) + , icon_(0)  {  tooltip_text = "Mock Icon"; - sort_priority_ = 0; - type_ = IconType::APPLICATION;  for (unsigned i = 0; i < unsigned(Quirk::LAST); ++i)  { @@ -58,7 +58,7 @@ public:  }  std::string GetName() const { return "MockLauncherIcon"; } -  +  void AddProperties(GVariantBuilder* builder) {}  void HideTooltip() {} @@ -130,11 +130,16 @@ public:  return false;  } - void SetCenter(nux::Point3 center, int monitor, nux::Geometry geo) {} + void SetCenter(nux::Point3 center, int monitor, nux::Geometry geo) + { + center.x += geo.x; + center.y += geo.y; + center_[monitor] = center; + }  nux::Point3 GetCenter(int monitor)  { - return nux::Point3(); + return center_[monitor];  }  nux::Point3 GetSavedCenter(int monitor) @@ -288,9 +293,9 @@ public:  bool IsVisible() const { return false; }  void AboutToRemove() {} -  +  void Stick(bool save = true) {} -  +  void UnStick() {}  private: @@ -341,11 +346,12 @@ private:  return result;  } - nux::BaseTexture* icon_; - int sort_priority_;  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_;  };  } diff --git a/launcher/SwitcherController.cpp b/launcher/SwitcherController.cpp index a4ecaf104..910c728d1 100644 --- a/launcher/SwitcherController.cpp +++ b/launcher/SwitcherController.cpp @@ -46,7 +46,7 @@ namespace switcher  Controller::Controller(unsigned int load_timeout)  : timeout_length(75)  , detail_on_timeout(true) - , detail_timeout_length(250) + , detail_timeout_length(500)  , initial_detail_timeout_length(1500)  , construct_timeout_(load_timeout)  , main_layout_(nullptr) diff --git a/launcher/Volume.h b/launcher/Volume.h new file mode 100644 index 000000000..0675a14f7 --- /dev/null +++ b/launcher/Volume.h @@ -0,0 +1,62 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_VOLUME_H +#define UNITYSHELL_VOLUME_H + +#include <boost/noncopyable.hpp> +#include <memory> +#include <sigc++/signal.h> +#include <sigc++/trackable.h> +#include <string> + +namespace unity +{ +namespace launcher +{ + +class Volume : private boost::noncopyable, public sigc::trackable +{ +public: + typedef std::shared_ptr<Volume> Ptr; + + virtual ~Volume() {} + + virtual bool CanBeEjected() const = 0; + virtual bool CanBeRemoved() const = 0; + virtual bool CanBeStopped() const = 0; + virtual std::string GetName() const = 0; + virtual std::string GetIconName() const = 0; + virtual std::string GetIdentifier() const = 0; + virtual bool HasSiblings() const = 0; + virtual bool IsMounted() const = 0; + + virtual void EjectAndShowNotification() = 0; + virtual void MountAndOpenInFileManager() = 0; + virtual void StopDrive() = 0; + virtual void Unmount() = 0; + + sigc::signal<void> changed; + sigc::signal<void> removed; +}; + +} +} + +#endif diff --git a/launcher/VolumeImp.cpp b/launcher/VolumeImp.cpp new file mode 100644 index 000000000..f3899289a --- /dev/null +++ b/launcher/VolumeImp.cpp @@ -0,0 +1,294 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#include <gio/gio.h> +#include <gtk/gtk.h> +#include <UnityCore/GLibSignal.h> + +#include "VolumeImp.h" + +namespace unity +{ +namespace launcher +{ + +// +// Start private implementation +// + +class VolumeImp::Impl +{ +public: + Impl(glib::Object<GVolume> const& volume, + FileManagerOpener::Ptr const& file_manager_opener, + DeviceNotificationDisplay::Ptr const& device_notification_display, + VolumeImp* parent) + : parent_(parent) + , cancellable_(g_cancellable_new()) + , volume_(volume) + , file_manager_opener_(file_manager_opener) + , device_notification_display_(device_notification_display) + { + signal_volume_changed_.Connect(volume_, "changed", [this] (GVolume*) { + parent_->changed.emit(); + }); + + signal_volume_removed_.Connect(volume_, "removed", [this] (GVolume*) { + parent_->removed.emit(); + }); + } + + ~Impl() + { + g_cancellable_cancel(cancellable_); + } + + bool CanBeEjected() const + { + return g_volume_can_eject(volume_) != FALSE; + } + + bool CanBeRemoved() const + { + glib::Object<GDrive> drive(g_volume_get_drive(volume_)); + return drive && g_drive_is_media_removable(drive) != FALSE; + } + + bool CanBeStopped() const + { + glib::Object<GDrive> drive(g_volume_get_drive(volume_)); + return drive && g_drive_can_stop(drive) != FALSE; + } + + std::string GetName() const + { + return glib::String(g_volume_get_name(volume_)).Str(); + } + + std::string GetIconName() const + { + glib::Object<GIcon> icon(g_volume_get_icon(volume_)); + return glib::String(g_icon_to_string(icon)).Str(); + } + + std::string GetIdentifier() const + { + return glib::String(g_volume_get_identifier(volume_, G_VOLUME_IDENTIFIER_KIND_UUID)).Str(); + } + + bool HasSiblings() const + { + glib::Object<GDrive> drive(g_volume_get_drive(volume_)); + + if (!drive) + return false; + + GList* volumes = g_drive_get_volumes(drive); + bool has_sibilings = volumes && volumes->next; + + if (volumes) + g_list_free_full(volumes, g_object_unref); + + return has_sibilings; + } + + bool IsMounted() const + { + glib::Object<GMount> mount(g_volume_get_mount(volume_)); + return static_cast<bool>(mount); + } + + void EjectAndShowNotification() + { + if (!CanBeEjected()) + return; + + glib::Object<GMountOperation> mount_op(gtk_mount_operation_new(nullptr)); + + g_volume_eject_with_operation(volume_, + (GMountUnmountFlags)0, + mount_op, + nullptr, + (GAsyncReadyCallback)OnEjectReady, + this); + } + + static void OnEjectReady(GObject* object, GAsyncResult* result, Impl* self) + { + if (g_volume_eject_with_operation_finish(self->volume_, result, nullptr)) + { + self->device_notification_display_->Display(self->GetIconName(), self->GetName()); + } + } + + void MountAndOpenInFileManager() + { + if (!IsMounted()) + MountAndOnFinishOpenInFileManager(); + else + OpenInFileManager(); + } + + void MountAndOnFinishOpenInFileManager() + { + g_volume_mount(volume_, + (GMountMountFlags) 0, + nullptr, + nullptr, + (GAsyncReadyCallback) &Impl::OnMountFinish, + this); + } + + static void OnMountFinish(GObject* object, + GAsyncResult* result, + Impl* self) + { + if (g_volume_mount_finish(self->volume_, result, nullptr)) + self->OpenInFileManager(); + } + + void OpenInFileManager() + { + file_manager_opener_->Open(GetUri()); + } + + std::string GetUri() + { + glib::Object<GMount> mount(g_volume_get_mount(volume_)); + glib::Object<GFile> root(g_mount_get_root(mount)); + + if (root.IsType(G_TYPE_FILE)) + return glib::String(g_file_get_uri(root)).Str(); + else + return std::string(); + } + + void StopDrive() + { + if (!CanBeStopped()) + return; + + glib::Object<GDrive> drive(g_volume_get_drive(volume_)); + glib::Object<GMountOperation> mount_op(gtk_mount_operation_new(NULL)); + + g_drive_stop(drive, + (GMountUnmountFlags)0, + mount_op, + nullptr, nullptr, nullptr); + } + + void Unmount() + { + if (!IsMounted()) + return; + + glib::Object<GMount> mount(g_volume_get_mount(volume_)); + glib::Object<GMountOperation> op(gtk_mount_operation_new(nullptr)); + + g_mount_unmount_with_operation(mount, + (GMountUnmountFlags)0, + op, + nullptr, nullptr, nullptr); + } + + VolumeImp* parent_; + glib::Object<GCancellable> cancellable_; + glib::Object<GVolume> volume_; + FileManagerOpener::Ptr file_manager_opener_; + DeviceNotificationDisplay::Ptr device_notification_display_; + + glib::Signal<void, GVolume*> signal_volume_changed_; + glib::Signal<void, GVolume*> signal_volume_removed_; +}; + +// +// End private implementation +// + +VolumeImp::VolumeImp(glib::Object<GVolume> const& volume, + FileManagerOpener::Ptr const& file_manager_opener, + DeviceNotificationDisplay::Ptr const& device_notification_display) + : pimpl(new Impl(volume, file_manager_opener, device_notification_display, this)) +{} + +VolumeImp::~VolumeImp() +{} + +bool VolumeImp::CanBeEjected() const +{ + return pimpl->CanBeEjected(); +} + +bool VolumeImp::CanBeRemoved() const +{ + return pimpl->CanBeRemoved(); +} + +bool VolumeImp::CanBeStopped() const +{ + return pimpl->CanBeStopped(); +} + +std::string VolumeImp::GetName() const +{ + return pimpl->GetName(); +} + +std::string VolumeImp::GetIconName() const +{ + return pimpl->GetIconName(); +} + +std::string VolumeImp::GetIdentifier() const +{ + return pimpl->GetIdentifier(); +} + +bool VolumeImp::HasSiblings() const +{ + return pimpl->HasSiblings(); +} + +bool VolumeImp::IsMounted() const +{ + return pimpl->IsMounted(); +} + +void VolumeImp::MountAndOpenInFileManager() +{ + pimpl->MountAndOpenInFileManager(); +} + +void VolumeImp::EjectAndShowNotification() +{ + pimpl->EjectAndShowNotification(); +} + +void VolumeImp::StopDrive() +{ + pimpl->StopDrive(); +} + +void VolumeImp::Unmount() +{ + pimpl->Unmount(); +} + +} // namespace launcher +} // namespace unity diff --git a/launcher/VolumeImp.h b/launcher/VolumeImp.h new file mode 100644 index 000000000..0d8cc2112 --- /dev/null +++ b/launcher/VolumeImp.h @@ -0,0 +1,68 @@ +// -*- 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_VOLUME_IMP_H +#define UNITYSHELL_VOLUME_IMP_H + +#include <memory> + +#include <UnityCore/GLibWrapper.h> + +#include "DeviceNotificationDisplay.h" +#include "FileManagerOpener.h" +#include "Volume.h" + +namespace unity +{ +namespace launcher +{ + +class VolumeImp : public Volume +{ +public: + typedef std::shared_ptr<VolumeImp> Ptr; + + VolumeImp(glib::Object<GVolume> const& volume, + FileManagerOpener::Ptr const& file_manager_opener, + DeviceNotificationDisplay::Ptr const& device_notification_display); + virtual ~VolumeImp(); + + virtual bool CanBeEjected() const; + virtual bool CanBeRemoved() const; + virtual bool CanBeStopped() const; + virtual std::string GetName() const; + virtual std::string GetIconName() const; + virtual std::string GetIdentifier() const; + virtual bool HasSiblings() const; + virtual bool IsMounted() const; + + virtual void EjectAndShowNotification(); + virtual void MountAndOpenInFileManager(); + virtual void StopDrive(); + virtual void Unmount(); + +private: + class Impl; + std::unique_ptr<Impl> pimpl; +}; + +} +} + +#endif diff --git a/launcher/VolumeLauncherIcon.cpp b/launcher/VolumeLauncherIcon.cpp new file mode 100644 index 000000000..bba622e9b --- /dev/null +++ b/launcher/VolumeLauncherIcon.cpp @@ -0,0 +1,305 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 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: Neil Jagdish Patel <neil.patel@canonical.com> + * Andrea Azzarone <andrea.azzarone@canonical.com> + */ + + +#include <glib/gi18n-lib.h> +#include <NuxCore/Logger.h> +#include <UnityCore/GLibSignal.h> + +#include "DevicesSettings.h" +#include "Volume.h" +#include "VolumeLauncherIcon.h" + +namespace unity +{ +namespace launcher +{ +namespace +{ + +nux::logging::Logger logger("unity.launcher"); + +const unsigned int volume_changed_timeout = 500; + +} + +// +// Start private implementation +// +class VolumeLauncherIcon::Impl +{ +public: + typedef glib::Signal<void, DbusmenuMenuitem*, int> ItemSignal; + + Impl(Volume::Ptr const& volume, + DevicesSettings::Ptr const& devices_settings, + VolumeLauncherIcon* parent) + : parent_(parent) + , volume_(volume) + , devices_settings_(devices_settings) + { + UpdateIcon(); + UpdateVisibility(); + ConnectSignals(); + } + + ~Impl() + {  + volume_changed_conn_.disconnect(); + volume_removed_conn_.disconnect(); + settings_changed_conn_.disconnect(); + } + + void UpdateIcon() + { + parent_->tooltip_text = volume_->GetName(); + parent_->icon_name = volume_->GetIconName(); + + parent_->SetQuirk(Quirk::RUNNING, false); + } + + void UpdateVisibility() + { + UpdateKeepInLauncher(); + parent_->SetQuirk(Quirk::VISIBLE, keep_in_launcher_); + } + + void UpdateKeepInLauncher() + { + auto identifier = volume_->GetIdentifier(); + keep_in_launcher_ = !devices_settings_->IsABlacklistedDevice(identifier); + } + + void ConnectSignals() + { + volume_changed_conn_ = volume_->changed.connect(sigc::mem_fun(this, &Impl::OnVolumeChanged)); + volume_removed_conn_ = volume_->removed.connect(sigc::mem_fun(this, &Impl::OnVolumeRemoved)); + settings_changed_conn_ = devices_settings_->changed.connect(sigc::mem_fun(this, &Impl::OnSettingsChanged)); + } + + void OnVolumeChanged() + { + UpdateIcon(); + } + + void OnVolumeRemoved() + { + if (devices_settings_->IsABlacklistedDevice(volume_->GetIdentifier())) + devices_settings_->TryToUnblacklist(volume_->GetIdentifier()); + + parent_->Remove(); + } + + void OnSettingsChanged() + { + UpdateVisibility(); + } + + bool CanEject() const + { + return volume_->CanBeEjected(); + } + + void EjectAndShowNotification() + { + return volume_->EjectAndShowNotification(); + } + + bool CanStop() const + { + return volume_->CanBeStopped(); + } + + void StopDrive() + { + volume_->StopDrive(); + } + + void ActivateLauncherIcon(ActionArg arg) + { + parent_->SimpleLauncherIcon::ActivateLauncherIcon(arg); + volume_->MountAndOpenInFileManager(); + } + + MenuItemsVector GetMenus() + { + MenuItemsVector result; + + AppendUnlockFromLauncherItem(result); + AppendOpenItem(result); + AppendEjectItem(result); + AppendSafelyRemoveItem(result); + AppendUnmountItem(result); + + return result; + } + + void AppendUnlockFromLauncherItem(MenuItemsVector& menu) + { + if (volume_->GetIdentifier().empty()) + return; + + glib::Object<DbusmenuMenuitem> menu_item(dbusmenu_menuitem_new()); + + dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Unlock from Launcher")); + dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); + 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(); + devices_settings_->TryToBlacklist(identifier); + })); + + menu.push_back(menu_item); + } + + void AppendOpenItem(MenuItemsVector& menu) + { + glib::Object<DbusmenuMenuitem> menu_item(dbusmenu_menuitem_new()); + + dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Open")); + dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); + 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) { + volume_->MountAndOpenInFileManager(); + })); + + menu.push_back(menu_item); + } + + void AppendEjectItem(MenuItemsVector& menu) + { + if (!volume_->CanBeEjected()) + return; + + glib::Object<DbusmenuMenuitem> menu_item(dbusmenu_menuitem_new()); + + dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, volume_->HasSiblings() ? _("Eject parent drive") : _("Eject")); + dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); + 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) { + volume_->EjectAndShowNotification(); + })); + + menu.push_back(menu_item); + } + + void AppendSafelyRemoveItem(MenuItemsVector& menu) + { + if (!volume_->CanBeStopped()) + return; + + glib::Object<DbusmenuMenuitem> menu_item(dbusmenu_menuitem_new()); + + dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, volume_->HasSiblings() ? _("Safely remove parent drive") : _("Safely remove")); + dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); + 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) { + volume_->StopDrive(); + })); + + menu.push_back(menu_item); + } + + void AppendUnmountItem(MenuItemsVector& menu) + { + if (!volume_->IsMounted() || volume_->CanBeEjected() || volume_->CanBeStopped()) + return; + + glib::Object<DbusmenuMenuitem> menu_item(dbusmenu_menuitem_new()); + + dbusmenu_menuitem_property_set(menu_item, DBUSMENU_MENUITEM_PROP_LABEL, _("Unmount")); + dbusmenu_menuitem_property_set_bool(menu_item, DBUSMENU_MENUITEM_PROP_ENABLED, true); + 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) { + volume_->Unmount(); + })); + + menu.push_back(menu_item); + } + + VolumeLauncherIcon* parent_; + bool keep_in_launcher_; + Volume::Ptr volume_; + DevicesSettings::Ptr devices_settings_; + + glib::SignalManager gsignals_; + sigc::connection settings_changed_conn_; + sigc::connection volume_changed_conn_; + sigc::connection volume_removed_conn_; +}; + +// +// End private implementation +// + +VolumeLauncherIcon::VolumeLauncherIcon(Volume::Ptr const& volume, + DevicesSettings::Ptr const& devices_settings) + : SimpleLauncherIcon(IconType::DEVICE) + , pimpl_(new Impl(volume, devices_settings, this)) +{} + +VolumeLauncherIcon::~VolumeLauncherIcon() +{} + +bool VolumeLauncherIcon::CanEject() const +{ + return pimpl_->CanEject(); +} + +void VolumeLauncherIcon::EjectAndShowNotification() +{ + pimpl_->EjectAndShowNotification(); +} + +bool VolumeLauncherIcon::CanStop() const +{ + return pimpl_->CanStop(); +} + +void VolumeLauncherIcon::StopDrive() +{ + return pimpl_->StopDrive(); +} + +void VolumeLauncherIcon::ActivateLauncherIcon(ActionArg arg) +{ + pimpl_->ActivateLauncherIcon(arg); +} + +AbstractLauncherIcon::MenuItemsVector VolumeLauncherIcon::GetMenus() +{ + return pimpl_->GetMenus(); +} + +// +// Introspection +// +std::string VolumeLauncherIcon::GetName() const +{ + return "VolumeLauncherIcon"; +} + +} // namespace launcher +} // namespace unity diff --git a/launcher/VolumeLauncherIcon.h b/launcher/VolumeLauncherIcon.h new file mode 100644 index 000000000..48e3e975f --- /dev/null +++ b/launcher/VolumeLauncherIcon.h @@ -0,0 +1,62 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* + * Copyright (C) 2010 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: Neil Jagdish Patel <neil.patel@canonical.com> + * Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#ifndef UNITYSHELL_VOLUME_LAUNCHER_ICON_H +#define UNITYSHELL_VOLUME_LAUNCHER_ICON_H + +#include "Volume.h" +#include "DevicesSettings.h" +#include "SimpleLauncherIcon.h" + +namespace unity +{ +namespace launcher +{ + +class VolumeLauncherIcon : public SimpleLauncherIcon +{ +public: + typedef nux::ObjectPtr<VolumeLauncherIcon> Ptr; + + VolumeLauncherIcon(Volume::Ptr const& volume, + DevicesSettings::Ptr const& devices_settings); + virtual ~VolumeLauncherIcon(); + + bool CanEject() const; // TODO: rename to public virtual bool IsTrashable(); + void EjectAndShowNotification(); // TODO: rename to private virtual void DoDropToTrash(); + bool CanStop() const; + void StopDrive(); + MenuItemsVector GetMenus(); + +protected: + virtual void ActivateLauncherIcon(ActionArg arg); + + // Introspection + virtual std::string GetName() const; + +private: + class Impl; + std::shared_ptr<Impl> pimpl_; +}; + +} +} + +#endif diff --git a/manual-tests/Hud.txt b/manual-tests/Hud.txt index d3d4ac445..1bf527b5d 100644 --- a/manual-tests/Hud.txt +++ b/manual-tests/Hud.txt @@ -1,6 +1,6 @@  Alt+Enter Crash  --------------- -Tests that Alt+Enter does not cause unity to crash (LP: #960957) +Test that Alt+Enter does not cause Compiz/Unity to crash (LP: #960957)  Setup: @@ -11,4 +11,4 @@ Expected Result:  Nothing happens  Wrong Result: - Unity/compiz crashes + Unity/Compiz crashes diff --git a/manual-tests/Launcher.txt b/manual-tests/Launcher.txt index adad15ece..0b1206518 100644 --- a/manual-tests/Launcher.txt +++ b/manual-tests/Launcher.txt @@ -92,6 +92,7 @@ This test verifies that dragging icons to the trash removes them from the  launcher, and that the animation is smooth.  Setup: +#. Ensure that you have multiple applications running or pinned to the launcher  Actions:  #. Move mouse pointer over an application or device icon (not BFB, Workspace switcher or trash) @@ -110,6 +111,7 @@ Dragging icons to reorder - initial position  This test is all position of the dragged image in relation to the mouse pointer.  Setup: +#. Ensure that you have multiple applications running or pinned to the launcher  Actions:  #. Move the mouse so it is over a launcher icon for an application @@ -128,6 +130,7 @@ This test is all about the smoothness of the animation around the reordering  of icons in the launcher.  Setup: +#. Ensure that you have multiple applications running or pinned to the launcher  Actions:  #. Move the mouse so it is over a launcher icon for an application @@ -135,13 +138,15 @@ Actions:  #. Drag the icon up and down over the icons in the launcher  Expected Result: - * As the centre of the dragged icon passes the mid-point of the next item - above or below it, the icon being dragged over moves and is replaced by the - blank "space". + * As the center of the dragged icon passes the mid-point of the next item + above or below it, the icon being dragged over moves and is replaced by the + blank "space".  * The animation should be smooth  * If the dragged icon is moved rapidly, multiple icons can be moving either - upwards or downwards as the space is moved into place under the dragged icon - * The BFB and the switcher icons do not move + upwards or downwards as the space is moved into place under the dragged icon + * Dragging the second application icon (the third icon of the launcher) below + the BFB or above the workspace switcher should be possible. + * The BFB and the workspace switcher icons do not move  Dragging icons to reorder - away from launcher @@ -149,6 +154,7 @@ Dragging icons to reorder - away from launcher  This test is about reordering the icons without the animation showing.  Setup: +#. Ensure that you have multiple applications running or pinned to the launcher  Actions:  #. Move the mouse so it is over a launcher icon for an application @@ -161,7 +167,7 @@ Expected Result:  * As the icon is dragged away from the launcher, the "space" is closed up  and replaced with a grey line.  * As the icon is dragged up and down, the line moves between other launcher - icons. + icons of the same type.  * When released, the icon "flies" back into the launcher, a spaces opens for  it, and the any pips for running apps show again. @@ -171,6 +177,7 @@ Dragged launcher icons out of the launcher are properly drawn  This test ensures that the launcher icons out of the launcher are properly drawn  Setup: +#. Ensure that you have multiple applications running or pinned to the launcher  Actions:  #. Move the mouse so it is over a launcher icon for an application @@ -677,3 +684,21 @@ Actions:  Expected Results:  * The gedit window must not flicker behind the launcher. + + +Custom background color works +----------------------------- +This test ensures that the custom background color (set via ccsm) works correctly. + +Setup:  +#. Ensure that ccsm is installed (package compizconfig-settings-manager) + +Actions:  +#. Open ccsm +#. Click on Ubuntu Unity Plugin +#. Choose Experimental tab, click on Background Color, set Color name to #000000 and Opacity to 180 + +Expected Result: + The background color of the Launcher is black, and the icons are still visible. + The Launcher must stay black when Dash or HUD are opened, and the color must + be persistent between sessions (logout/login). diff --git a/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.cpp b/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.cpp index ad840204c..e16cb89c3 100644 --- a/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.cpp +++ b/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.cpp @@ -223,9 +223,9 @@ UnityMTGrabHandlesScreen::raiseHandle (const boost::shared_ptr <const unity::MT:  void  UnityMTGrabHandlesScreen::handleEvent(XEvent* event)  { - CompWindow* w, *oldPrev, *oldNext; + CompWindow* w; - w = oldPrev = oldNext = NULL; + w = NULL;  switch (event->type)  { @@ -438,29 +438,19 @@ UnityMTGrabHandlesWindow::getOutputExtents(CompWindowExtents& output)  bool  UnityMTGrabHandlesWindow::glDraw(const GLMatrix& transform, -#ifdef USE_MODERN_COMPIZ_GL  const GLWindowPaintAttrib& attrib, -#else - GLFragment::Attrib& fragment, -#endif  const CompRegion& region,  unsigned int mask)  {  /* Draw the window on the bottom, we will be drawing the  * handles on top */ -#ifdef USE_MODERN_COMPIZ_GL  bool status = gWindow->glDraw(transform, attrib, region, mask); -#else - bool status = gWindow->glDraw(transform, fragment, region, mask); -#endif  if (mHandles && mHandles->visible())  {  unsigned int allowedHandles = unity::MT::getLayoutForMask (window->state (), window->actions ());  unsigned int handle = 0; - UMTGH_SCREEN (screen); -  for(unity::MT::TextureLayout layout : mHandles->layout (allowedHandles))  {  /* We want to set the geometry of the handle to the window @@ -472,17 +462,11 @@ UnityMTGrabHandlesWindow::glDraw(const GLMatrix& transform,  GLTexture::MatrixList matl;  GLTexture::Matrix mat = tex->matrix();  CompRegion paintRegion(region); -#ifdef USE_MODERN_COMPIZ_GL  GLWindowPaintAttrib wAttrib(attrib); -#endif  /* We can reset the window geometry since it will be  * re-added later */ -#ifdef USE_MODERN_COMPIZ_GL  gWindow->vertexBuffer()->begin(); -#else - gWindow->geometry().reset(); -#endif  /* Not sure what this does, but it is necessary  * (adjusts for scale?) */ @@ -498,35 +482,22 @@ UnityMTGrabHandlesWindow::glDraw(const GLMatrix& transform,  * dim (so we get a nice render for things like  * wobbly etc etc */  gWindow->glAddGeometry(matl, reg, paintRegion); -#ifdef USE_MODERN_COMPIZ_GL - gWindow->vertexBuffer()->end(); - wAttrib.opacity = mHandles->opacity(); -#else - /* Did it succeed? */ - if (gWindow->geometry().vertices) - { - fragment.setOpacity(mHandles->opacity()); - /* Texture rendering set-up */ - us->gScreen->setTexEnvMode(GL_MODULATE); -#endif + +	if (gWindow->vertexBuffer()->end()) +	{ + wAttrib.opacity = mHandles->opacity(); +  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);  /* Draw the dim texture with all of it's modified  * geometry glory */  gWindow->glDrawTexture(tex, -#ifdef USE_MODERN_COMPIZ_GL  transform, wAttrib, -#else - fragment, -#endif  mask | PAINT_WINDOW_BLEND_MASK  | PAINT_WINDOW_TRANSLUCENT_MASK |  PAINT_WINDOW_TRANSFORMED_MASK);  /* Texture rendering tear-down */  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -#ifndef USE_MODERN_COMPIZ_GL - us->gScreen->setTexEnvMode(GL_REPLACE);  } -#endif  }  handle++; diff --git a/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.h b/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.h index 9cd99b9d1..f0d3776e5 100644 --- a/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.h +++ b/plugins/unity-mt-grab-handles/src/unity-mt-grab-handles.h @@ -229,11 +229,7 @@ public:  void moveNotify(int dx, int dy, bool immediate);  bool glDraw(const GLMatrix&, -#ifdef USE_MODERN_COMPIZ_GL  const GLWindowPaintAttrib&, -#else - GLFragment::Attrib&, -#endif  const CompRegion&,  unsigned int); diff --git a/plugins/unitydialog/src/unitydialog.cpp b/plugins/unitydialog/src/unitydialog.cpp index 91f7f235e..3951b65f7 100644 --- a/plugins/unitydialog/src/unitydialog.cpp +++ b/plugins/unitydialog/src/unitydialog.cpp @@ -420,12 +420,8 @@ UnityDialogWindow::glAddGeometry(const GLTexture::MatrixList& matrices,  /* Collect textures */  void  UnityDialogWindow::glDrawTexture(GLTexture* texture, -#ifdef USE_MODERN_COMPIZ_GL  const GLMatrix &transform,  const GLWindowPaintAttrib &attrib, -#else - GLFragment::Attrib& fa, -#endif  unsigned int mask)  {  unity::PaintInfoCollector::Active ()->processTexture (texture); @@ -448,16 +444,12 @@ unity::GeometryCollection::status ()  collectedMinVertices.size () == collectedMatrixLists.size ());  } -void +bool  unity::GeometryCollection::addGeometryForWindow (CompWindow *w, const CompRegion &paintRegion)  {  /* We can reset the window geometry since it will be  * re-added later */ -#ifdef USE_MODERN_COMPIZ_GL  GLWindow::get (w)->vertexBuffer()->begin(); -#else - GLWindow::get (w)->geometry().reset(); -#endif  for (unsigned int i = 0; i < collectedMatrixLists.size (); i++)  { @@ -472,9 +464,7 @@ unity::GeometryCollection::addGeometryForWindow (CompWindow *w, const CompRegion  GLWindow::get (w)->glAddGeometry(matl, reg, paintRegion, min, max);  } -#ifdef USE_MODERN_COMPIZ_GL - GLWindow::get (w)->vertexBuffer()->end(); -#endif + return GLWindow::get (w)->vertexBuffer()->end();  }  void @@ -511,9 +501,7 @@ unity::TexGeometryCollection::setTexture (GLTexture *tex)  void  unity::TexGeometryCollection::addGeometriesAndDrawTextureForWindow(CompWindow *w, -#ifdef USE_MODERN_COMPIZ_GL  const GLMatrix &transform, -#endif  unsigned int mask)  {  if (mTexture && mGeometries.status ()) @@ -524,47 +512,25 @@ unity::TexGeometryCollection::addGeometriesAndDrawTextureForWindow(CompWindow *w  if (mask & PAINT_WINDOW_TRANSFORMED_MASK)  paintRegion = infiniteRegion; - mGeometries.addGeometryForWindow (w, paintRegion); - -#ifdef USE_MODERN_COMPIZ_GL - UnityDialogScreen *uds = UnityDialogScreen::get (screen); - GLWindowPaintAttrib attrib (gWindow->lastPaintAttrib()); - unsigned int glDrawTextureIndex = gWindow->glDrawTextureGetCurrentIndex(); - /* Texture rendering set-up */ -// uds->gScreen->setTexEnvMode(GL_MODULATE); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - /* Draw the dim texture with all of it's modified - * geometry glory */ - gWindow->glDrawTextureSetCurrentIndex(MAXSHORT); - gWindow->glDrawTexture(mTexture, transform, attrib, mask - | PAINT_WINDOW_BLEND_MASK - | PAINT_WINDOW_TRANSLUCENT_MASK - | PAINT_WINDOW_TRANSFORMED_MASK); - gWindow->glDrawTextureSetCurrentIndex(glDrawTextureIndex); - /* Texture rendering tear-down */ - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - uds->gScreen->setTexEnvMode(GL_REPLACE); -#else - if (gWindow->geometry().vertices) + if (mGeometries.addGeometryForWindow (w, paintRegion))  { -	UnityDialogScreen *uds = UnityDialogScreen::get (screen); -	GLFragment::Attrib fa (gWindow->lastPaintAttrib()); -	unsigned int glDrawTextureIndex = gWindow->glDrawTextureGetCurrentIndex(); -	/* Texture rendering set-up */ -	uds->gScreen->setTexEnvMode(GL_MODULATE); -	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -	/* Draw the dim texture with all of it's modified - * geometry glory */ -	gWindow->glDrawTextureSetCurrentIndex(MAXSHORT); -	gWindow->glDrawTexture(mTexture, fa, mask | PAINT_WINDOW_BLEND_MASK - | PAINT_WINDOW_TRANSLUCENT_MASK | - PAINT_WINDOW_TRANSFORMED_MASK); -	gWindow->glDrawTextureSetCurrentIndex(glDrawTextureIndex); -	/* Texture rendering tear-down */ -	glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); -	uds->gScreen->setTexEnvMode(GL_REPLACE); + UnityDialogScreen *uds = UnityDialogScreen::get (screen); + GLWindowPaintAttrib attrib (gWindow->lastPaintAttrib()); + unsigned int glDrawTextureIndex = gWindow->glDrawTextureGetCurrentIndex(); + /* Texture rendering set-up */ + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + /* Draw the dim texture with all of it's modified + * geometry glory */ + gWindow->glDrawTextureSetCurrentIndex(MAXSHORT); + gWindow->glDrawTexture(mTexture, transform, attrib, mask + | PAINT_WINDOW_BLEND_MASK + | PAINT_WINDOW_TRANSLUCENT_MASK + | PAINT_WINDOW_TRANSFORMED_MASK); + gWindow->glDrawTextureSetCurrentIndex(glDrawTextureIndex); + /* Texture rendering tear-down */ + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + uds->gScreen->setTexEnvMode(GL_REPLACE);  } -#endif  }  } @@ -619,17 +585,11 @@ unity::PaintInfoCollector::processTexture (GLTexture *tex)  void  unity::PaintInfoCollector::drawGeometriesForWindow(CompWindow *w, -#ifdef USE_MODERN_COMPIZ_GL  const GLMatrix &transform, -#endif  unsigned int pm)  {  for (unity::TexGeometryCollection &tcg : mCollection) -#if USE_MODERN_COMPIZ_GL  tcg.addGeometriesAndDrawTextureForWindow (w, transform, pm); -#else - tcg.addGeometriesAndDrawTextureForWindow (w, pm); -#endif  }  unity::PaintInfoCollector * unity::PaintInfoCollector::active_collector = NULL; @@ -644,11 +604,7 @@ unity::PaintInfoCollector::Active ()  bool  UnityDialogWindow::glDraw(const GLMatrix& transform, -#ifdef USE_MODERN_COMPIZ_GL  const GLWindowPaintAttrib& attrib, -#else - GLFragment::Attrib& fragment, -#endif  const CompRegion& region,  unsigned int mask)  { @@ -660,11 +616,7 @@ UnityDialogWindow::glDraw(const GLMatrix& transform,  /* Draw the window on the bottom, we will be drawing the  * dim render on top */  bool status = gWindow->glDraw(transform, -#ifdef USE_MODERN_COMPIZ_GL  attrib, -#else - fragment, -#endif  region, mask);  UNITY_DIALOG_SCREEN(screen); @@ -673,17 +625,11 @@ UnityDialogWindow::glDraw(const GLMatrix& transform,  {  GLTexture::MatrixList matl;  GLTexture::Matrix mat = tex->matrix(); -#ifdef USE_MODERN_COMPIZ_GL  GLWindowPaintAttrib wAttrib(attrib); -#endif  /* We can reset the window geometry since it will be  * re-added later */ -#ifdef USE_MODERN_COMPIZ_GL  gWindow->vertexBuffer()->begin(); -#else - gWindow->geometry().reset(); -#endif  /* Scale the dim render by the ratio of dim size  * to window size */ @@ -704,48 +650,25 @@ UnityDialogWindow::glDraw(const GLMatrix& transform,  * dim (so we get a nice render for things like  * wobbly etc etc */  gWindow->glAddGeometry(matl, reg, paintRegion); -#ifdef USE_MODERN_COMPIZ_GL - gWindow->vertexBuffer()->end(); -#endif - -#ifdef USE_MODERN_COMPIZ_GL - unsigned int glDrawTextureIndex = gWindow->glDrawTextureGetCurrentIndex(); - wAttrib.opacity = mShadeProgress; - /* Texture rendering set-up */ -// uds->gScreen->setTexEnvMode(GL_MODULATE); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - /* Draw the dim texture with all of it's modified - * geometry glory */ - gWindow->glDrawTextureSetCurrentIndex(MAXSHORT); - gWindow->glDrawTexture(tex, transform, attrib, mask - | PAINT_WINDOW_BLEND_MASK - | PAINT_WINDOW_TRANSLUCENT_MASK - | PAINT_WINDOW_TRANSFORMED_MASK); - gWindow->glDrawTextureSetCurrentIndex(glDrawTextureIndex); - /* Texture rendering tear-down */ - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - uds->gScreen->setTexEnvMode(GL_REPLACE); -#else - /* Did it succeed? */ - if (gWindow->geometry().vertices) + if (gWindow->vertexBuffer()->end())  {  unsigned int glDrawTextureIndex = gWindow->glDrawTextureGetCurrentIndex(); - fragment.setOpacity(mShadeProgress); + wAttrib.opacity = mShadeProgress;  /* Texture rendering set-up */ - uds->gScreen->setTexEnvMode(GL_MODULATE); + // uds->gScreen->setTexEnvMode(GL_MODULATE);  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);  /* Draw the dim texture with all of it's modified  * geometry glory */  gWindow->glDrawTextureSetCurrentIndex(MAXSHORT); - gWindow->glDrawTexture(tex, fragment, mask | PAINT_WINDOW_BLEND_MASK - | PAINT_WINDOW_TRANSLUCENT_MASK | - PAINT_WINDOW_TRANSFORMED_MASK); + gWindow->glDrawTexture(tex, transform, attrib, mask + | PAINT_WINDOW_BLEND_MASK + | PAINT_WINDOW_TRANSLUCENT_MASK + | PAINT_WINDOW_TRANSFORMED_MASK);  gWindow->glDrawTextureSetCurrentIndex(glDrawTextureIndex);  /* Texture rendering tear-down */  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);  uds->gScreen->setTexEnvMode(GL_REPLACE);  } -#endif  }  for (CompWindow* w : mTransients) @@ -759,9 +682,7 @@ UnityDialogWindow::glDraw(const GLMatrix& transform,  pc.collect();  pc.drawGeometriesForWindow (window, -#ifdef USE_MODERN_COMPIZ_GL  transform, -#endif  mask);  }  } diff --git a/plugins/unitydialog/src/unitydialog.h b/plugins/unitydialog/src/unitydialog.h index eee9adea7..4f3f26338 100644 --- a/plugins/unitydialog/src/unitydialog.h +++ b/plugins/unitydialog/src/unitydialog.h @@ -40,7 +40,7 @@ namespace unity  GeometryCollection ();  bool status (); - void addGeometryForWindow (CompWindow *, const CompRegion &paintRegion); + bool addGeometryForWindow (CompWindow *, const CompRegion &paintRegion);  void addGeometry (const GLTexture::MatrixList &ml, 	const CompRegion &r, 	int min, @@ -64,13 +64,9 @@ namespace unity 	int max);  void setTexture (GLTexture *); -#ifdef USE_MODERN_COMPIZ_GL  void addGeometriesAndDrawTextureForWindow (CompWindow *w,  const GLMatrix &transform, - unsigned int mask); -#else - void addGeometriesAndDrawTextureForWindow (CompWindow *, unsigned int pm); -#endif + unsigned int mask);  private:  GLTexture* mTexture; @@ -84,13 +80,9 @@ namespace unity  PaintInfoCollector (CompWindow *w);  void collect (); -#ifdef USE_MODERN_COMPIZ_GL  void drawGeometriesForWindow (CompWindow *w,  const GLMatrix &transform,  unsigned int pm); -#else - void drawGeometriesForWindow (CompWindow *w, unsigned int pm); -#endif  void processGeometry (const GLTexture::MatrixList &ml,  const CompRegion &r, @@ -255,11 +247,7 @@ public:  bool  glDraw(const GLMatrix&, -#ifdef USE_MODERN_COMPIZ_GL  const GLWindowPaintAttrib&, -#else - GLFragment::Attrib&, -#endif  const CompRegion&, unsigned int);  bool @@ -275,12 +263,8 @@ public:  void  glDrawTexture(GLTexture* texture, -#ifdef USE_MODERN_COMPIZ_GL  const GLMatrix& transform,  const GLWindowPaintAttrib& attrib, -#else - GLFragment::Attrib& attrib, -#endif  unsigned int mask); diff --git a/plugins/unityshell/CMakeLists.txt b/plugins/unityshell/CMakeLists.txt index eaf2047a3..c6ad6f75d 100644 --- a/plugins/unityshell/CMakeLists.txt +++ b/plugins/unityshell/CMakeLists.txt @@ -6,7 +6,7 @@ set (COMPIZ_PLUGIN_INSTALL_TYPE "package")  compiz_plugin (unityshell  PKGDEPS ${UNITY_PLUGIN_DEPS} - PLUGINDEPS composite opengl compiztoolbox + PLUGINDEPS composite opengl compiztoolbox scale  CFLAGSADD "-DINSTALLPREFIX='\"${CMAKE_INSTALL_PREFIX}\"' -DPKGDATADIR='\"${PKGDATADIR}\"' -I${CMAKE_BINARY_DIR} -I${CMAKE_SOURCE_DIR} ${BOOT_LOGGER_FLAG} -DGETTEXT_PACKAGE='\"unity\"' ${MAINTAINER_CFLAGS} -I${CMAKE_SOURCE_DIR}/dash/ -I${CMAKE_SOURCE_DIR}/launcher/ -I${CMAKE_SOURCE_DIR}/hud/ -I${CMAKE_SOURCE_DIR}/panel/ -I${CMAKE_SOURCE_DIR}/shortcuts/ -I${CMAKE_SOURCE_DIR}/unity-shared/"  LIBDIRS "${CMAKE_BINARY_DIR}/UnityCore"  ) diff --git a/plugins/unityshell/src/ScreenEffectFramebufferObject.cpp b/plugins/unityshell/src/ScreenEffectFramebufferObject.cpp deleted file mode 100644 index de9be43f3..000000000 --- a/plugins/unityshell/src/ScreenEffectFramebufferObject.cpp +++ /dev/null @@ -1,243 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* Compiz unity plugin - * unity.h - * - * Copyright (c) 2010-11 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 3 - * of the License, or (at your option) any later version. - * - * 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. - * - * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com> - */ - -#ifndef USE_GLES -#include "ScreenEffectFramebufferObject.h" -#include "BackgroundEffectHelper.h" -#include <NuxCore/Logger.h> -#include <dlfcn.h> - -namespace -{ - nux::logging::Logger logger ("unity.screeneffectframebufferobject"); -} - -void unity::ScreenEffectFramebufferObject::paint (const nux::Geometry &output) -{ - /* Draw the bit of the relevant framebuffer for each output */ - - glPushAttrib (GL_VIEWPORT_BIT); - glViewport (0, 0, mScreenSize.width, mScreenSize.height); - - if (mFBTexture) - { - glEnable (GL_TEXTURE_2D); - activeTexture (GL_TEXTURE0_ARB); - glBindTexture (GL_TEXTURE_2D, mFBTexture); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glPushAttrib (GL_SCISSOR_BIT); - glEnable (GL_SCISSOR_TEST); - - glScissor (output.x, mScreenSize.height - (output.y + output.height), - output.width, output.height); - - /* FIXME: This needs to be GL_TRIANGLE_STRIP */ - glBegin (GL_QUADS); - glTexCoord2f (0, 1); - glVertex2i (mGeometry.x, mGeometry.y); - glTexCoord2f (0, 0); - glVertex2i (mGeometry.x, mGeometry.y + mGeometry.height); - glTexCoord2f (1, 0); - glVertex2i (mGeometry.x + mGeometry.width, mGeometry.y + mGeometry.height); - glTexCoord2f (1, 1); - glVertex2i (mGeometry.x + mGeometry.width, mGeometry.y); - glEnd (); - - activeTexture (GL_TEXTURE0_ARB); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glBindTexture (GL_TEXTURE_2D, 0); - glDisable (GL_TEXTURE_2D); - glPopAttrib (); - } - glPopAttrib (); -} - -void unity::ScreenEffectFramebufferObject::onScreenSizeChanged(const nux::Geometry& screenSize) -{ - mScreenSize = screenSize; -} - - -void unity::ScreenEffectFramebufferObject::unbind () -{ - if (!mBoundCnt) - return; - - mBoundCnt--; - - (*bindFramebuffer) (GL_FRAMEBUFFER_EXT, 0); - - glDrawBuffer (GL_BACK); - glReadBuffer (GL_BACK); - - /* Matches the viewport set we did in ::bind () */ - glPopAttrib (); - -} - -bool unity::ScreenEffectFramebufferObject::status () -{ - return mFboStatus; -} - -void unity::ScreenEffectFramebufferObject::bind (const nux::Geometry &output) -{ - /* Very important! - * Don't bind unless BackgroundEffectHelper says it's necessary. - * Because binding has a severe impact on graphics performance and we - * can't afford to do it every frame. (LP: #861061) (LP: #987304) - */ - if (!BackgroundEffectHelper::HasDirtyHelpers()) - return; - - /* Clear the error bit */ - glGetError (); - - if (!mFBTexture) - { - glGenTextures (1, &mFBTexture); - - glBindTexture (GL_TEXTURE_2D, mFBTexture); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, mGeometry.width, mGeometry.height, 0, GL_BGRA, -#if IMAGE_BYTE_ORDER == MSBFirst - GL_UNSIGNED_INT_8_8_8_8_REV, -#else - GL_UNSIGNED_BYTE, -#endif - NULL); - - glBindTexture (GL_TEXTURE_2D, 0); - - if (glGetError () != GL_NO_ERROR) - { - mFboHandle = 0; - mFboStatus = false; - return; - } - } - - (*bindFramebuffer) (GL_FRAMEBUFFER_EXT, mFboHandle); - - (*framebufferTexture2D) (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, - GL_TEXTURE_2D, mFBTexture, 0); - - (*framebufferTexture2D) (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, - GL_TEXTURE_2D, 0, 0); - - /* Ensure that a framebuffer is actually available */ - if (!mFboStatus) - { - GLint status = (*checkFramebufferStatus) (GL_DRAW_FRAMEBUFFER); - - if (status != GL_FRAMEBUFFER_COMPLETE) - { - switch (status) - { - case GL_FRAMEBUFFER_UNDEFINED: - LOG_WARN (logger) << "no window"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT: - LOG_WARN (logger) << "attachment incomplete"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: - LOG_WARN (logger) << "no buffers attached to fbo"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER: - LOG_WARN (logger) << "some attachment in glDrawBuffers doesn't exist in FBO"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER: - LOG_WARN (logger) << "some attachment in glReadBuffers doesn't exist in FBO"; - break; - case GL_FRAMEBUFFER_UNSUPPORTED: - LOG_WARN (logger) << "unsupported internal format"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE: - LOG_WARN (logger) << "different levels of sampling for each attachment"; - break; - case GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS: - LOG_WARN (logger) << "number of layers is different"; - break; - default: - LOG_WARN (logger) << "unable to bind the framebuffer for an unknown reason"; - break; - } - - bindFramebuffer (GL_FRAMEBUFFER_EXT, 0); - deleteFramebuffers (1, &mFboHandle); - - glDrawBuffer (GL_BACK); - glReadBuffer (GL_BACK); - - mFboHandle = 0; - - mFboStatus = false; - } - else - mFboStatus = true; - } - - if (mFboStatus) - { - glPushAttrib (GL_VIEWPORT_BIT); - - glViewport (output.x, - mScreenSize.height - (output.y + output.height), - output.width, - output.height); - } - - mBoundCnt++; -} - - -unity::ScreenEffectFramebufferObject::ScreenEffectFramebufferObject (GLXGetProcAddressProc p, const nux::Geometry &geom) - : getProcAddressGLX (p) - , mFboStatus (false) - , mFBTexture (0) - , mGeometry (geom) - , mBoundCnt (0) - , mScreenSize (geom) -{ - activeTexture = (GLActiveTextureProc) (*getProcAddressGLX) ((GLubyte *) "glActiveTexture"); - genFramebuffers = (GLGenFramebuffersProc) (*getProcAddressGLX) ((GLubyte *)"glGenFramebuffersEXT"); - deleteFramebuffers = (GLDeleteFramebuffersProc) (*getProcAddressGLX) ((GLubyte *)"glDeleteFramebuffersEXT"); - bindFramebuffer = (GLBindFramebufferProc) (*getProcAddressGLX) ((GLubyte *)"glBindFramebufferEXT"); - checkFramebufferStatus = (GLCheckFramebufferStatusProc) (*getProcAddressGLX) ((GLubyte *) "glCheckFramebufferStatusEXT"); - framebufferTexture2D = (GLFramebufferTexture2DProc) (*getProcAddressGLX) ((GLubyte *) "glFramebufferTexture2DEXT"); -  - (*genFramebuffers) (1, &mFboHandle); -} - -unity::ScreenEffectFramebufferObject::~ScreenEffectFramebufferObject () -{ - (*deleteFramebuffers) (1, &mFboHandle); - - if (mFBTexture) - glDeleteTextures (1, &mFBTexture); -} - -#endif // USE_GLES - diff --git a/plugins/unityshell/src/ScreenEffectFramebufferObject.h b/plugins/unityshell/src/ScreenEffectFramebufferObject.h deleted file mode 100644 index 9ccfb9a92..000000000 --- a/plugins/unityshell/src/ScreenEffectFramebufferObject.h +++ /dev/null @@ -1,89 +0,0 @@ -// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- -/* Compiz unity plugin - * unity.h - * - * Copyright (c) 2010-11 Canonical Ltd. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 3 - * of the License, or (at your option) any later version. - * - * 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. - * - * Authored By: Sam Spilsbury <sam.spilsbury@canonical.com> - */ - -#ifndef UNITY_SCREENEFFECT_FRAMEBUFFER_H -#define UNITY_SCREENEFFECT_FRAMEBUFFER_H - -#ifndef USE_MODERN_COMPIZ_GL -#include <Nux/Nux.h> - -namespace unity -{ -class ScreenEffectFramebufferObject -{ -public: - - typedef boost::shared_ptr <ScreenEffectFramebufferObject> Ptr; - typedef void (*FuncPtr) (void); - typedef FuncPtr (*GLXGetProcAddressProc) (const GLubyte *procName); - - ScreenEffectFramebufferObject (GLXGetProcAddressProc, const nux::Geometry &geom); - ~ScreenEffectFramebufferObject (); - -public: - - void bind (const nux::Geometry &geom); - void unbind (); - - bool status (); - void paint (const nux::Geometry &geom); - bool bound () { return mBoundCnt > 0; } - - GLuint texture () { return mFBTexture; } -  - void onScreenSizeChanged (const nux::Geometry &screenSize); - -private: - - FuncPtr getProcAddr (const std::string &); - - typedef void (*GLActiveTextureProc) (GLenum texture); - typedef void (*GLGenFramebuffersProc) (GLsizei n, - GLuint *framebuffers); - typedef void (*GLDeleteFramebuffersProc) (GLsizei n, - GLuint *framebuffers); - typedef void (*GLBindFramebufferProc) (GLenum target, - GLuint framebuffer); - typedef GLenum (*GLCheckFramebufferStatusProc) (GLenum target); - typedef void (*GLFramebufferTexture2DProc) (GLenum target, - GLenum attachment, - GLenum textarget, - GLuint texture, - GLint level); -  - GLXGetProcAddressProc getProcAddressGLX; - GLActiveTextureProc activeTexture; - GLGenFramebuffersProc genFramebuffers; - GLDeleteFramebuffersProc deleteFramebuffers; - GLBindFramebufferProc bindFramebuffer; - GLCheckFramebufferStatusProc checkFramebufferStatus; - GLFramebufferTexture2DProc framebufferTexture2D; - /* compiz fbo handle that goes through to nux */ - GLuint mFboHandle; // actual handle to the framebuffer_ext - bool mFboStatus; // did the framebuffer texture bind succeed - GLuint mFBTexture; - nux::Geometry mGeometry; - unsigned int mBoundCnt; -  - nux::Geometry mScreenSize; -}; -} // namespace unity - -#endif // USE_MODERN_COMPIZ_GL -#endif diff --git a/plugins/unityshell/src/WindowMinimizeSpeedController.cpp b/plugins/unityshell/src/WindowMinimizeSpeedController.cpp new file mode 100644 index 000000000..66f28d8fc --- /dev/null +++ b/plugins/unityshell/src/WindowMinimizeSpeedController.cpp @@ -0,0 +1,108 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* Compiz unity plugin + * unity.h + * + * Copyright (c) 2010-11 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * 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. + * + * Your own copyright notice would go above. You are free to choose whatever + * licence you want, just take note that some compiz code is GPL and you will + * not be able to re-use it if you want to use a different licence. + */ + +#include <gio/gio.h> +#include <NuxCore/Logger.h> + +#include "WindowMinimizeSpeedController.h" + +namespace +{ + +nux::logging::Logger logger ("unity.WindowMinimizeSpeedController"); + +namespace local +{ +const std::string UNITY_SCHEMA = "com.canonical.Unity"; +} +} + +WindowMinimizeSpeedController::WindowMinimizeSpeedController() + : _settings(g_settings_new(local::UNITY_SCHEMA.c_str())) + , _minimize_count(g_settings_get_int(_settings, "minimize-count")) + , _minimize_speed_threshold(g_settings_get_int(_settings, "minimize-speed-threshold")) + , _minimize_slow_duration(g_settings_get_int(_settings, "minimize-slow-duration")) + , _minimize_fast_duration(g_settings_get_int(_settings, "minimize-fast-duration")) +{ + _minimize_count_changed.Connect(_settings, "changed::minimize-count", + [&] (GSettings*, gchar* name) { + _minimize_count = g_settings_get_int(_settings, name); + SetDuration(); + }); + _minimize_speed_threshold_changed.Connect(_settings, "changed::minimize-speed-threshold", + [&] (GSettings*, gchar* name) { + _minimize_speed_threshold = g_settings_get_int(_settings, name); + SetDuration(); + }); + _minimize_fast_duration_changed.Connect(_settings, "changed::minimize-fast-duration", + [&] (GSettings*, gchar* name) { + _minimize_fast_duration = g_settings_get_int(_settings, name); + SetDuration(); + }); + _minimize_slow_duration_changed.Connect(_settings, "changed::minimize-slow-duration", + [&] (GSettings*, gchar* name) { + _minimize_slow_duration = g_settings_get_int(_settings, name); + SetDuration(); + }); +} + +void WindowMinimizeSpeedController::UpdateCount() +{ + if (_minimize_count < _minimize_speed_threshold) { + _minimize_count += 1; + g_settings_set_int(_settings, "minimize-count", _minimize_count); + } +} + +int WindowMinimizeSpeedController::getDuration() +{ + return mDuration; +} + +void WindowMinimizeSpeedController::SetDuration() +{ + /* Perform some sanity checks on the configuration values */ + if (_minimize_fast_duration > _minimize_slow_duration) + { + LOG_WARN(logger) << "Configuration mismatch: minimize-fast-duration ("  + << _minimize_fast_duration + << ") is longer than minimize-slow-duration (" + << _minimize_slow_duration << "). Not changing speed."; + return; + } +  + if (_minimize_count < 0) + _minimize_count = 0; + if (_minimize_count > _minimize_speed_threshold) + _minimize_count = _minimize_speed_threshold; +  + /* Adjust the speed so that it gets linearly closer to maximum speed as we + approach the threshold */ + int speed_range = _minimize_slow_duration - _minimize_fast_duration; + float position = (_minimize_speed_threshold <= 0) ? 1.0 : + static_cast<float>(_minimize_count) / _minimize_speed_threshold; + int duration = _minimize_slow_duration - std::ceil(position * speed_range); +  + if (duration != mDuration) { + mDuration = duration; + DurationChanged.emit(); + }  +} diff --git a/plugins/unityshell/src/WindowMinimizeSpeedController.h b/plugins/unityshell/src/WindowMinimizeSpeedController.h new file mode 100644 index 000000000..49bcbad14 --- /dev/null +++ b/plugins/unityshell/src/WindowMinimizeSpeedController.h @@ -0,0 +1,57 @@ +// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*- +/* Compiz unity plugin + * unity.h + * + * Copyright (c) 2010-11 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * 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. + * + * Your own copyright notice would go above. You are free to choose whatever + * licence you want, just take note that some compiz code is GPL and you will + * not be able to re-use it if you want to use a different licence. + */ + +#ifndef WINDOWMINIMIZESPEEDCONTROLLER_H +#define WINDOWMINIMIZESPEEDCONTROLLER_H + +#include <core/core.h> +#include <UnityCore/GLibWrapper.h> +#include <UnityCore/GLibSignal.h> +#include <sigc++/sigc++.h> + +typedef struct _GSettings GSettings; + +using namespace unity; + +class WindowMinimizeSpeedController +{ +public: + WindowMinimizeSpeedController(); + void UpdateCount(); + int getDuration(); + sigc::signal<void> DurationChanged; +  +private: + void SetDuration(); + + glib::Object<GSettings> _settings; + int _minimize_count; + int _minimize_speed_threshold; + int _minimize_slow_duration; + int _minimize_fast_duration; + glib::Signal<void, GSettings*, gchar* > _minimize_count_changed; + glib::Signal<void, GSettings*, gchar* > _minimize_speed_threshold_changed; + glib::Signal<void, GSettings*, gchar* > _minimize_slow_duration_changed; + glib::Signal<void, GSettings*, gchar* > _minimize_fast_duration_changed; + int mDuration; +}; + +#endif // WINDOWMINIMIZESPEEDCONTROLLER_H diff --git a/plugins/unityshell/src/unitya11y.cpp b/plugins/unityshell/src/unitya11y.cpp index 9a8c143d9..1035af609 100644 --- a/plugins/unityshell/src/unitya11y.cpp +++ b/plugins/unityshell/src/unitya11y.cpp @@ -20,6 +20,7 @@  #include <gio/gio.h>  #include <gmodule.h>  #include <stdio.h> +#include <atk-bridge.h>  #include "unitya11y.h"  #include "unitya11ytests.h" @@ -62,12 +63,6 @@ static GHashTable* accessible_table = NULL;  static gboolean a11y_initialized = FALSE; -#define INIT_METHOD "gnome_accessibility_module_init" -#define DESKTOP_SCHEMA "org.gnome.desktop.interface" -#define ACCESSIBILITY_ENABLED_KEY "toolkit-accessibility" -#define AT_SPI_SCHEMA "org.a11y.atspi" -#define ATK_BRIDGE_LOCATION_KEY "atk-bridge-location" -  static void  unity_a11y_restore_environment(void)  { @@ -82,102 +77,6 @@ load_unity_atk_util(nux::WindowThread* wt)  g_type_class_unref(g_type_class_ref(UNITY_TYPE_UTIL_ACCESSIBLE));  } -/* This method is required because g_setting_new aborts if the schema - is not present. */ -static gboolean -has_gsettings_schema(const gchar* schema) -{ - const char* const* list_schemas = NULL; - gboolean found = FALSE; - int i = 0; - - list_schemas = g_settings_list_schemas(); - for (i = 0; list_schemas [i]; i++) - { - if (!g_strcmp0(list_schemas[i], schema)) - { - found = TRUE; - break; - } - } - - return found; -} - -static gboolean -should_enable_a11y(void) -{ - GSettings* desktop_settings = NULL; - gboolean value = FALSE; - - if (!has_gsettings_schema(DESKTOP_SCHEMA)) - return FALSE; - - desktop_settings = g_settings_new(DESKTOP_SCHEMA); - value = g_settings_get_boolean(desktop_settings, ACCESSIBILITY_ENABLED_KEY); - - g_object_unref(desktop_settings); - - return value; -} - -static gchar* -get_atk_bridge_path(void) -{ - GSettings* atspi_settings = NULL; - GVariant *variant = NULL; - char* value = NULL; - - if (!has_gsettings_schema(AT_SPI_SCHEMA)) - return NULL; - - atspi_settings = g_settings_new(AT_SPI_SCHEMA); - variant = g_settings_get_value (atspi_settings, ATK_BRIDGE_LOCATION_KEY); - value = g_variant_dup_bytestring (variant, NULL); - - g_variant_unref (variant); - g_object_unref(atspi_settings); - - return value; -} - -static gboolean -a11y_invoke_module(const char* module_path) -{ - GModule* handle; - void (*invoke_fn)(void); - - if (!module_path) - { - g_warning("Accessibility: invalid module path (NULL)"); - - return FALSE; - } - - if (!(handle = g_module_open(module_path, (GModuleFlags)0))) - { - g_warning("Accessibility: failed to load module '%s': '%s'", - module_path, g_module_error()); - - return FALSE; - } - - if (!g_module_symbol(handle, INIT_METHOD, (gpointer*)&invoke_fn)) - { - g_warning("Accessibility: error library '%s' does not include " - "method '%s' required for accessibility support", - module_path, INIT_METHOD); - g_module_close(handle); - - return FALSE; - } - - invoke_fn(); - - return TRUE; -} - -/********************************************************************************/  /*  * In order to avoid the atk-bridge loading and the GAIL  * initialization during the gtk_init, it is required to set some @@ -194,35 +93,19 @@ unity_a11y_preset_environment(void)  /*  * Initializes the accessibility (ATK) support on Unity  * - * It loads the atk-bridge if required. It checks: - * * If the proper gsettings keys are set - * * Loads the proper AtkUtil implementation  */  void  unity_a11y_init(nux::WindowThread* wt)  { - gchar* bridge_path = NULL; - - unity_a11y_restore_environment(); - - if (!should_enable_a11y()) + if (a11y_initialized)  return; + unity_a11y_restore_environment();  load_unity_atk_util(wt); + atk_bridge_adaptor_init(NULL, NULL); + atk_get_root(); - bridge_path = get_atk_bridge_path(); - - if (a11y_invoke_module(bridge_path)) - { - g_debug("Unity Oneiric accessibility started, using bridge on %s", - bridge_path); - - atk_get_root(); - - a11y_initialized = TRUE; - } - - g_free(bridge_path); + a11y_initialized = TRUE;  // NOTE: we run the unit tests manually while developing by  // uncommenting this. Take a look at the explanation in the diff --git a/plugins/unityshell/src/unityshell.cpp b/plugins/unityshell/src/unityshell.cpp index 17a2bb9c2..0ec87f497 100644 --- a/plugins/unityshell/src/unityshell.cpp +++ b/plugins/unityshell/src/unityshell.cpp @@ -29,7 +29,6 @@  #include "Launcher.h"  #include "LauncherIcon.h"  #include "LauncherController.h" -#include "DevicesSettings.h"  #include "PluginAdapter.h"  #include "QuicklistManager.h"  #include "StartupNotifyService.h" @@ -44,6 +43,9 @@  #include <gdk/gdk.h>  #include <gdk/gdkx.h>  #include <libnotify/notify.h> +#include <cairo-xlib-xrender.h> + +#include <text/text.h>  #include <sstream>  #include <memory> @@ -81,6 +83,9 @@ nux::logging::Logger logger("unity.shell");  UnityScreen* uScreen = 0; +const unsigned int SCALE_CLOSE_ICON_SIZE = 19; +const unsigned int SCALE_ITEMS_PADDING = 5; +  void reset_glib_logging();  void configure_logging();  void capture_g_log_calls(const gchar* log_domain, @@ -119,9 +124,6 @@ UnityScreen::UnityScreen(CompScreen* screen)  , allowWindowPaint(false)  , _key_nav_mode_requested(false)  , _last_output(nullptr) -#ifndef USE_MODERN_COMPIZ_GL - , _active_fbo (0) -#endif  , grab_index_ (0)  , painting_tray_ (false)  , last_scroll_event_(0) @@ -129,6 +131,8 @@ UnityScreen::UnityScreen(CompScreen* screen)  , panel_texture_has_changed_(true)  , paint_panel_(false)  , scale_just_activated_(false) + , scale_highlighted_window_(0) + , minimize_speed_controller(new WindowMinimizeSpeedController())  {  Timer timer;  #ifndef USE_GLES @@ -243,25 +247,6 @@ UnityScreen::UnityScreen(CompScreen* screen)  uScreen = this;  _in_paint = false; -#ifndef USE_MODERN_COMPIZ_GL - void *dlhand = dlopen ("libunityshell.so", RTLD_LAZY); - - if (dlhand) - { - dlerror (); - glXGetProcAddressP = (ScreenEffectFramebufferObject::GLXGetProcAddressProc) dlsym (dlhand, "glXGetProcAddress"); - if (dlerror () != NULL) - glXGetProcAddressP = NULL; - } - - if (GL::fbo) - { - nux::Geometry geometry (0, 0, screen->width (), screen->height ()); - uScreen->_fbo = ScreenEffectFramebufferObject::Ptr (new ScreenEffectFramebufferObject (glXGetProcAddressP, geometry)); - uScreen->_fbo->onScreenSizeChanged (geometry); - } -#endif -  optionSetShowHudInitiate(boost::bind(&UnityScreen::ShowHudInitiate, this, _1, _2, _3));  optionSetShowHudTerminate(boost::bind(&UnityScreen::ShowHudTerminate, this, _1, _2, _3));  optionSetBackgroundColorNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); @@ -281,7 +266,6 @@ UnityScreen::UnityScreen(CompScreen* screen)  optionSetIconSizeNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));  optionSetAutohideAnimationNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2));  optionSetDashBlurExperimentalNotify(boost::bind(&UnityScreen::optionChanged, this, _1, _2)); - optionSetDevicesOptionNotify(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)); @@ -386,6 +370,11 @@ UnityScreen::UnityScreen(CompScreen* screen)  }  panel::Style::Instance().changed.connect(sigc::mem_fun(this, &UnityScreen::OnPanelStyleChanged)); + WindowManager::Default()->terminate_spread.connect([this] { scale_highlighted_window_ = 0; }); + + minimize_speed_controller->DurationChanged.connect( + sigc::mem_fun(this, &UnityScreen::OnMinimizeDurationChanged) + );  }  UnityScreen::~UnityScreen() @@ -495,17 +484,6 @@ void UnityScreen::nuxPrologue()  glMatrixMode(GL_MODELVIEW);  glPushMatrix(); - -#ifndef USE_MODERN_COMPIZ_GL - /* This is needed to Fix a crash in glDrawArrays with the NVIDIA driver - * see bugs #1031554 and #982626. - * The NVIDIA driver looks to see if the legacy GL_VERTEX_ARRAY, - * GL_TEXTURE_COORDINATES_ARRAY and other such client states are enabled - * first before checking if a vertex buffer is bound and will prefer the - * client buffers over the the vertex buffer object. */ - glDisableClientState(GL_VERTEX_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); -#endif  #endif  glGetError(); @@ -514,9 +492,6 @@ void UnityScreen::nuxPrologue()  void UnityScreen::nuxEpilogue()  {  #ifndef USE_GLES -#ifndef USE_MODERN_COMPIZ_GL - (*GL::bindFramebuffer)(GL_FRAMEBUFFER_EXT, _active_fbo); -#endif  glMatrixMode(GL_PROJECTION);  glLoadIdentity(); @@ -538,19 +513,9 @@ void UnityScreen::nuxEpilogue()  glPopAttrib(); -#ifndef USE_MODERN_COMPIZ_GL - /* Re-enable the client states that have been disabled in nuxPrologue, for - * NVIDIA compatibility reasons */ - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); -#endif + glDepthRange(0, 1);  #else -#ifdef USE_GLES  glDepthRangef(0, 1); -#else - glDepthRange(0, 1); -#endif - //glViewport(-1, -1, 2, 2);  gScreen->resetRasterPos();  #endif @@ -562,86 +527,9 @@ void UnityScreen::setPanelShadowMatrix(const GLMatrix& matrix)  panel_shadow_matrix_ = matrix;  } +/* Currently unimplemented */  void UnityScreen::paintPanelShadow(const GLMatrix& matrix)  { -#ifndef USE_MODERN_COMPIZ_GL - if (sources_.GetSource(local::RELAYOUT_TIMEOUT)) - return; - - if (PluginAdapter::Default()->IsExpoActive()) - return; - - CompOutput* output = _last_output; - float vc[4]; - float h = 20.0f; - float w = 1.0f; - float panel_h = panel_style_.panel_height; - - float x1 = output->x(); - float y1 = output->y() + panel_h; - float x2 = x1 + output->width(); - float y2 = y1 + h; - - glPushMatrix (); - glLoadMatrixf (panel_shadow_matrix_.getMatrix ()); - - vc[0] = x1; - vc[1] = x2; - vc[2] = y1; - vc[3] = y2; - - // compiz doesn't use the same method of tracking monitors as our toolkit - // we need to make sure we properly associate with the right monitor - int current_monitor = -1; - auto monitors = UScreen::GetDefault()->GetMonitors(); - int i = 0; - for (auto monitor : monitors) - { - if (monitor.x == output->x() && monitor.y == output->y()) - { - current_monitor = i; - break; - } - i++; - } - - if (!(launcher_controller_->IsOverlayOpen() && current_monitor == dash_monitor_) - && panel_controller_->opacity() > 0.0f) - { - foreach(GLTexture * tex, _shadow_texture) - { - glEnable(GL_BLEND); - glColor4f(1.0f, 1.0f, 1.0f, panel_controller_->opacity()); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - GL::activeTexture(GL_TEXTURE0_ARB); - tex->enable(GLTexture::Fast); - - glTexParameteri(tex->target(), GL_TEXTURE_WRAP_S, GL_REPEAT); - - glBegin(GL_QUADS); - { - glTexCoord2f(COMP_TEX_COORD_X(tex->matrix(), 0), COMP_TEX_COORD_Y(tex->matrix(), 0)); - glVertex2f(vc[0], vc[2]); - - glTexCoord2f(COMP_TEX_COORD_X(tex->matrix(), 0), COMP_TEX_COORD_Y(tex->matrix(), h)); - glVertex2f(vc[0], vc[3]); - - glTexCoord2f(COMP_TEX_COORD_X(tex->matrix(), w), COMP_TEX_COORD_Y(tex->matrix(), h)); - glVertex2f(vc[1], vc[3]); - - glTexCoord2f(COMP_TEX_COORD_X(tex->matrix(), w), COMP_TEX_COORD_Y(tex->matrix(), 0)); - glVertex2f(vc[1], vc[2]); - } - glEnd(); - - tex->disable(); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glDisable(GL_BLEND); - } - } - glPopMatrix(); -#else  return;  if (sources_.GetSource(local::RELAYOUT_TIMEOUT)) @@ -735,7 +623,6 @@ void UnityScreen::paintPanelShadow(const GLMatrix& matrix)  }  }  nuxEpilogue(); -#endif  }  void @@ -756,90 +643,32 @@ UnityScreen::OnPanelStyleChanged()  panel_texture_has_changed_ = true;  } -#ifdef USE_MODERN_COMPIZ_GL  void UnityScreen::paintDisplay() -#else -void UnityScreen::paintDisplay(const CompRegion& region, const GLMatrix& transform, unsigned int mask) -#endif  {  CompOutput *output = _last_output; -#ifndef USE_MODERN_COMPIZ_GL - bool was_bound = _fbo->bound (); - - if (nux::GetGraphicsDisplay()->GetGraphicsEngine()->UsingGLSLCodePath()) - { - if (was_bound && launcher_controller_->IsOverlayOpen() && paint_panel_) - { - if (panel_texture_has_changed_ || !panel_texture_.IsValid()) - { - panel_texture_.Release(); - - nux::NBitmapData* bitmap = panel::Style::Instance().GetBackground(screen->width (), screen->height(), 1.0f); - nux::BaseTexture* texture2D = nux::GetGraphicsDisplay()->GetGpuDevice()->CreateSystemCapableTexture(); - if (bitmap && texture2D) - { - texture2D->Update(bitmap); - panel_texture_ = texture2D->GetDeviceTexture(); - texture2D->UnReference(); - delete bitmap; - } - panel_texture_has_changed_ = false; - } - - if (panel_texture_.IsValid()) - { - nux::GetGraphicsDisplay()->GetGraphicsEngine()->ResetModelViewMatrixStack(); - nux::GetGraphicsDisplay()->GetGraphicsEngine()->Push2DTranslationModelViewMatrix(0.0f, 0.0f, 0.0f); - nux::GetGraphicsDisplay()->GetGraphicsEngine()->ResetProjectionMatrix(); - nux::GetGraphicsDisplay()->GetGraphicsEngine()->SetOrthographicProjectionMatrix(screen->width (), screen->height()); - - nux::TexCoordXForm texxform; - int panel_height = panel_style_.panel_height; - nux::GetGraphicsDisplay()->GetGraphicsEngine()->QRP_GLSL_1Tex(0, 0, screen->width (), panel_height, panel_texture_, texxform, nux::color::White); - } - } - } + DrawTopPanelBackground(); - _fbo->unbind (); + auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); - /* Draw the bit of the relevant framebuffer for each output */ - - if (was_bound) - { - GLMatrix sTransform; - sTransform.toScreenSpace (&screen->fullscreenOutput (), -DEFAULT_Z_CAMERA); - glPushMatrix (); - glLoadMatrixf (sTransform.getMatrix ()); - _fbo->paint (nux::Geometry (output->x (), output->y (), output->width (), output->height ())); - glPopMatrix (); - } - - nux::ObjectPtr<nux::IOpenGLBaseTexture> device_texture = - nux::GetGraphicsDisplay()->GetGpuDevice()->CreateTexture2DFromID(_fbo->texture(), - screen->width (), screen->height(), 1, nux::BITFMT_R8G8B8A8); -#else  nux::ObjectPtr<nux::IOpenGLTexture2D> device_texture = - nux::GetGraphicsDisplay()->GetGpuDevice()->CreateTexture2DFromID(gScreen->fbo ()->tex ()->name (), - screen->width(), screen->height(), 1, nux::BITFMT_R8G8B8A8); -#endif + gpu_device->CreateTexture2DFromID(gScreen->fbo ()->tex ()->name (), + screen->width(), screen->height(), 1, nux::BITFMT_R8G8B8A8); - nux::GetGraphicsDisplay()->GetGpuDevice()->backup_texture0_ = device_texture; + gpu_device->backup_texture0_ = device_texture; - nux::Geometry geo = nux::Geometry (0, 0, screen->width (), screen->height ()); - nux::Geometry oGeo = nux::Geometry (output->x (), output->y (), output->width (), output->height ()); + nux::Geometry geo(0, 0, screen->width (), screen->height ()); + nux::Geometry outputGeo(output->x (), output->y (), output->width (), output->height ());  BackgroundEffectHelper::monitor_rect_ = geo; -#ifdef USE_MODERN_COMPIZ_GL  GLint fboID;  // Nux renders to the referenceFramebuffer when it's embedded.  glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fboID); - wt->GetWindowCompositor().SetReferenceFramebuffer(fboID, oGeo); -#endif + wt->GetWindowCompositor().SetReferenceFramebuffer(fboID, outputGeo);  nuxPrologue();  _in_paint = true; - wt->RenderInterfaceFromForeignCmd (&oGeo); + wt->RenderInterfaceFromForeignCmd (&outputGeo);  _in_paint = false;  nuxEpilogue(); @@ -853,56 +682,28 @@ void UnityScreen::paintDisplay(const CompRegion& region, const GLMatrix& transfo  {  GLMatrix oTransform;  UnityWindow *uTrayWindow = UnityWindow::get (tray); -#ifndef USE_MODERN_COMPIZ_GL - GLFragment::Attrib attrib (uTrayWindow->gWindow->lastPaintAttrib()); -#else  GLWindowPaintAttrib attrib (uTrayWindow->gWindow->lastPaintAttrib()); -#endif  unsigned int oldGlAddGeometryIndex = uTrayWindow->gWindow->glAddGeometryGetCurrentIndex ();  unsigned int oldGlDrawIndex = uTrayWindow->gWindow->glDrawGetCurrentIndex (); -#ifndef USE_MODERN_COMPIZ_GL - unsigned int oldGlDrawGeometryIndex = uTrayWindow->gWindow->glDrawGeometryGetCurrentIndex (); -#endif -#ifndef USE_MODERN_COMPIZ_GL - attrib.setOpacity (OPAQUE); - attrib.setBrightness (BRIGHT); - attrib.setSaturation (COLOR); -#else  attrib.opacity = OPAQUE;  attrib.brightness = BRIGHT;  attrib.saturation = COLOR; -#endif  oTransform.toScreenSpace (output, -DEFAULT_Z_CAMERA); -#ifndef USE_MODERN_COMPIZ_GL - glPushMatrix (); - glLoadMatrixf (oTransform.getMatrix ()); -#endif -  painting_tray_ = true;  /* force the use of the core functions */  uTrayWindow->gWindow->glDrawSetCurrentIndex (MAXSHORT);  uTrayWindow->gWindow->glAddGeometrySetCurrentIndex ( MAXSHORT); -#ifndef USE_MODERN_COMPIZ_GL - uTrayWindow->gWindow->glDrawGeometrySetCurrentIndex (MAXSHORT); -#endif  uTrayWindow->gWindow->glDraw (oTransform, attrib, infiniteRegion,  PAINT_WINDOW_TRANSFORMED_MASK |  PAINT_WINDOW_BLEND_MASK |  PAINT_WINDOW_ON_TRANSFORMED_SCREEN_MASK); -#ifndef USE_MODERN_COMPIZ_GL - uTrayWindow->gWindow->glDrawGeometrySetCurrentIndex (oldGlDrawGeometryIndex); -#endif  uTrayWindow->gWindow->glAddGeometrySetCurrentIndex (oldGlAddGeometryIndex);  uTrayWindow->gWindow->glDrawSetCurrentIndex (oldGlDrawIndex);  painting_tray_ = false; - -#ifndef USE_MODERN_COMPIZ_GL - glPopMatrix (); -#endif  }  }  } @@ -927,6 +728,53 @@ void UnityScreen::paintDisplay(const CompRegion& region, const GLMatrix& transfo  didShellRepaint = true;  } +void UnityScreen::DrawTopPanelBackground() +{ + auto graphics_engine = nux::GetGraphicsDisplay()->GetGraphicsEngine(); + + if (!graphics_engine->UsingGLSLCodePath() || !launcher_controller_->IsOverlayOpen() || !paint_panel_) + return; + + if (TopPanelBackgroundTextureNeedsUpdate()) + UpdateTopPanelBackgroundTexture(); + + if (panel_texture_.IsValid()) + { + graphics_engine->ResetModelViewMatrixStack(); + graphics_engine->Push2DTranslationModelViewMatrix(0.0f, 0.0f, 0.0f); + graphics_engine->ResetProjectionMatrix(); + graphics_engine->SetOrthographicProjectionMatrix(screen->width (), screen->height()); + + nux::TexCoordXForm texxform; + int panel_height = panel_style_.panel_height; + graphics_engine->QRP_GLSL_1Tex(0, 0, screen->width (), panel_height, panel_texture_, texxform, nux::color::White); + } +} + +bool UnityScreen::TopPanelBackgroundTextureNeedsUpdate() const +{ + return panel_texture_has_changed_ || !panel_texture_.IsValid(); +} + +void UnityScreen::UpdateTopPanelBackgroundTexture() +{ + auto gpu_device = nux::GetGraphicsDisplay()->GetGpuDevice(); + auto &panel_style = panel::Style::Instance(); + + panel_texture_.Release(); + + std::unique_ptr<nux::NBitmapData> bitmap(panel_style.GetBackground(screen->width(), screen->height(), 1.0f)); + nux::ObjectPtr<nux::BaseTexture> texture2D(gpu_device->CreateSystemCapableTexture()); + + if (bitmap && texture2D) + { + texture2D->Update(bitmap.get()); + panel_texture_ = texture2D->GetDeviceTexture(); + } + + panel_texture_has_changed_ = false; +} +  bool UnityScreen::forcePaintOnTop ()  {  return !allowWindowPaint || @@ -1055,8 +903,8 @@ void UnityScreen::leaveShowDesktopMode (CompWindow *w)  void UnityWindow::enterShowDesktop ()  {  if (!mShowdesktopHandler) - mShowdesktopHandler = new ShowdesktopHandler (static_cast <ShowdesktopHandlerWindowInterface *> (this), - static_cast <compiz::WindowInputRemoverLockAcquireInterface *> (this)); + mShowdesktopHandler.reset(new ShowdesktopHandler(static_cast <ShowdesktopHandlerWindowInterface *>(this), + static_cast <compiz::WindowInputRemoverLockAcquireInterface *>(this)));  window->setShowDesktopMode (true);  mShowdesktopHandler->FadeOut (); @@ -1177,15 +1025,14 @@ ShowdesktopHandlerWindowInterface::PostPaintAction UnityWindow::DoHandleAnimatio  return action;  } -void UnityWindow::DoAddDamage () +void UnityWindow::DoAddDamage()  { - cWindow->addDamage (); + cWindow->addDamage();  }  void UnityWindow::DoDeleteHandler ()  { - delete mShowdesktopHandler; - mShowdesktopHandler = NULL; + mShowdesktopHandler.reset();  window->updateFrameRegion ();  } @@ -1207,15 +1054,74 @@ UnityWindow::GetNoCoreInstanceMask ()  return PAINT_WINDOW_NO_CORE_INSTANCE_MASK;  } -void UnityWindow::handleEvent (XEvent *event) +bool UnityWindow::handleEvent(XEvent *event)  { - if (screen->XShape () && - event->type == screen->shapeEvent () + ShapeNotify && - !event->xany.send_event) + bool handled = false; + + switch(event->type)  { - if (mShowdesktopHandler) - mShowdesktopHandler->HandleShapeEvent (); + case MotionNotify: + if (close_icon_state_ != panel::WindowState::PRESSED) + { + panel::WindowState old_state = close_icon_state_; + + if (close_button_geo_.IsPointInside(event->xmotion.x_root, event->xmotion.y_root)) + { + close_icon_state_ = panel::WindowState::PRELIGHT; + } + else + { + close_icon_state_ = panel::WindowState::NORMAL; + } + + if (old_state != close_icon_state_) + DoAddDamage(); + } + break; + + case ButtonPress: + if (event->xbutton.button == Button1 && + close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) + { + close_icon_state_ = panel::WindowState::PRESSED; + DoAddDamage(); + handled = true; + } + break; + + case ButtonRelease: + { + bool was_pressed = (close_icon_state_ == panel::WindowState::PRESSED); + + if (close_icon_state_ != panel::WindowState::NORMAL) + { + close_icon_state_ = panel::WindowState::NORMAL; + DoAddDamage(); + } + + if (was_pressed) + { + if (close_button_geo_.IsPointInside(event->xbutton.x_root, event->xbutton.y_root)) + window->close(0); + + handled = true; + } + } + break; + + default: + if (!event->xany.send_event && screen->XShape() && + event->type == screen->shapeEvent() + ShapeNotify) + { + if (mShowdesktopHandler) + { + mShowdesktopHandler->HandleShapeEvent(); + handled = true; + } + }  } + + return handled;  }  bool UnityScreen::shellCouldBeHidden(CompOutput const& output) @@ -1281,26 +1187,6 @@ bool UnityScreen::glPaintOutput(const GLScreenPaintAttrib& attrib,  _last_output = output;  paint_panel_ = false; -#ifndef USE_MODERN_COMPIZ_GL - /* bind the framebuffer here - * - it will be unbound and flushed - * to the backbuffer when some - * plugin requests to draw a - * a transformed screen or when - * we have finished this draw cycle. - * once an fbo is bound any further - * attempts to bind it will only increment - * its bind reference so make sure that - * you always unbind as much as you bind - * - * But NOTE: It is only safe to bind the FBO if !shellCouldBeHidden. - * Otherwise it's possible painting won't occur and that would - * confuse the state of the FBO. - */ - if (doShellRepaint && !shellCouldBeHidden(*output)) - _fbo->bind (nux::Geometry (output->x (), output->y (), output->width (), output->height ())); -#endif -  // CompRegion has no clear() method. So this is the fastest alternative.  fullscreenRegion = CompRegion();  nuxRegion = CompRegion(); @@ -1312,11 +1198,7 @@ bool UnityScreen::glPaintOutput(const GLScreenPaintAttrib& attrib,  doShellRepaint = false;  if (doShellRepaint) -#ifdef USE_MODERN_COMPIZ_GL  paintDisplay(); -#else - paintDisplay(region, transform, mask); -#endif  return ret;  } @@ -1346,10 +1228,6 @@ void UnityScreen::preparePaint(int ms)  for (ShowdesktopHandlerWindowInterface *wi : ShowdesktopHandler::animating_windows)  wi->HandleAnimations (ms); -#ifndef USE_MODERN_COMPIZ_GL - compizDamageNux(cScreen->currentDamage()); -#endif -  didShellRepaint = false;  firstWindowAboveShell = NULL;  } @@ -1476,7 +1354,6 @@ void UnityScreen::compizDamageNux(CompRegion const& damage)  /* Grab changed nux regions and add damage rects for them */  void UnityScreen::nuxDamageCompiz()  { -#ifdef USE_MODERN_COMPIZ_GL  /*  * If Nux is going to redraw anything then we have to tell Compiz to  * redraw everything. This is because Nux has a bad habit (bug??) of drawing @@ -1496,60 +1373,6 @@ void UnityScreen::nuxDamageCompiz()  cScreen->damageScreen();  cScreen->damageRegionSetEnabled(this, true);  } - -#else - - /* - * WARNING: Nux bug LP: #1014610 (unbounded DrawList growth) will cause - * this code to be called far too often in some cases and - * Unity will appear to freeze for a while. Please ensure you - * have Nux 3.0+ with the fix for LP: #1014610. - */ - - if (!launcher_controller_ || !dash_controller_) - return; - - CompRegion nux_damage; - - std::vector<nux::Geometry> const& dirty = wt->GetDrawList(); - - for (auto const& geo : dirty) - nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height); - - if (launcher_controller_->IsOverlayOpen()) - { - nux::BaseWindow* dash_window = dash_controller_->window(); - nux::Geometry const& geo = dash_window->GetAbsoluteGeometry(); - nux_damage += CompRegion(geo.x, geo.y, geo.width, geo.height); - } - - auto const& launchers = launcher_controller_->launchers(); - for (auto const& launcher : launchers) - { - if (!launcher->Hidden()) - { - nux::ObjectPtr<nux::View> tooltip = launcher->GetActiveTooltip(); - - if (tooltip) - { - nux::Geometry const& g = tooltip->GetAbsoluteGeometry(); - nux_damage += CompRegion(g.x, g.y, g.width, g.height); - } - - nux::ObjectPtr<LauncherDragWindow> const& dragged_icon = launcher->GetDraggedIcon(); - - if (dragged_icon) - { - nux::Geometry const& g = dragged_icon->GetAbsoluteGeometry(); - nux_damage += CompRegion(g.x, g.y, g.width, g.height); - } - } - } - - cScreen->damageRegionSetEnabled(this, false); - cScreen->damageRegion(nux_damage); - cScreen->damageRegionSetEnabled(this, true); -#endif  }  /* handle X Events */ @@ -1564,9 +1387,6 @@ void UnityScreen::handleEvent(XEvent* event)  PluginAdapter::Default()->OnScreenGrabbed();  else if (event->xfocus.mode == NotifyUngrab)  PluginAdapter::Default()->OnScreenUngrabbed(); -#ifndef USE_MODERN_COMPIZ_GL - cScreen->damageScreen(); // evil hack -#endif  if (_key_nav_mode_requested)  {  // Close any overlay that is open. @@ -1579,12 +1399,25 @@ void UnityScreen::handleEvent(XEvent* event)  }  _key_nav_mode_requested = false;  break; + case MotionNotify: + if (scale_highlighted_window_ && PluginAdapter::Default()->IsScaleActive()) + { + if (CompWindow *w = screen->findWindow(scale_highlighted_window_)) + skip_other_plugins = UnityWindow::get(w)->handleEvent(event); + } + break;  case ButtonPress:  if (super_keypressed_)  {  launcher_controller_->KeyNavTerminate(false);  EnableCancelAction(CancelActionTarget::LAUNCHER_SWITCHER, false);  } + if (scale_highlighted_window_ && PluginAdapter::Default()->IsScaleActive()) + { + if (CompWindow *w = screen->findWindow(scale_highlighted_window_)) + skip_other_plugins = UnityWindow::get(w)->handleEvent(event); + } +  break;  case ButtonRelease:  if (switcher_controller_ && switcher_controller_->Visible()) @@ -1604,6 +1437,11 @@ void UnityScreen::handleEvent(XEvent* event)  }  }  } + else if (scale_highlighted_window_ && PluginAdapter::Default()->IsScaleActive()) + { + if (CompWindow *w = screen->findWindow(scale_highlighted_window_)) + UnityWindow::get(w)->handleEvent(event); + }  break;  case KeyPress:  { @@ -1731,8 +1569,8 @@ void UnityScreen::handleCompizEvent(const char* plugin,  ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);  } - if (PluginAdapter::Default()->IsScaleActive() && - g_strcmp0(plugin, "scale") == 0 && super_keypressed_) + if (PluginAdapter::Default()->IsScaleActive() && g_strcmp0(plugin, "scale") == 0 && + super_keypressed_)  {  scale_just_activated_ = true;  } @@ -2513,11 +2351,7 @@ bool UnityWindow::glDraw(const GLMatrix& matrix,  !uScreen->fullscreenRegion.contains(window->geometry())  )  { -#ifdef USE_MODERN_COMPIZ_GL  uScreen->paintDisplay(); -#else - uScreen->paintDisplay(region, matrix, mask); -#endif  }  if (window->type() == CompWindowTypeDesktopMask) @@ -2542,6 +2376,40 @@ bool UnityWindow::glDraw(const GLMatrix& matrix,  }  void +UnityScreen::OnMinimizeDurationChanged () +{ + /* Update the compiz plugin setting with the new computed speed so that it + * will be used in the following minimizations */ + CompPlugin *p = CompPlugin::find("animation"); + if (p) + { + CompOption::Vector &opts = p->vTable->getOptions(); + + for (CompOption &o : opts) + { + if (o.name () == std::string ("minimize_durations")) + { + /* minimize_durations is a list value, but minimize applies only to + * normal windows, so there's always one value */ + CompOption::Value& value = o.value(); + CompOption::Value::Vector& list = value.list(); + CompOption::Value::Vector::iterator i = list.begin(); + if (i != list.end()) { + i->set(minimize_speed_controller->getDuration()); + } + value.set(list);  + screen->setOptionForPlugin(p->vTable->name().c_str(), + o.name().c_str(), value); + break; + } + } + } + else { + LOG_WARN(logger) << "Animation plugin not found. Can't set minimize speed."; + } +} + +void  UnityWindow::minimize ()  {  if (!window->managed ()) @@ -2657,6 +2525,11 @@ void UnityWindow::windowNotify(CompWindowNotify n)  case CompWindowNotifyBeforeDestroy:  being_destroyed.emit();  break; + case CompWindowNotifyMinimize: + /* Updating the count in dconf will trigger a "changed" signal to which + * the method setting the new animation speed is attached */ + UnityScreen::get(screen)->minimize_speed_controller->UpdateCount(); + break;  default:  break;  } @@ -2925,9 +2798,6 @@ void UnityScreen::optionChanged(CompOption* opt, UnityshellOptions::Options num)  case UnityshellOptions::AutomaximizeValue:  PluginAdapter::Default()->SetCoverageAreaBeforeAutomaximize(optionGetAutomaximizeValue() / 100.0f);  break; - case UnityshellOptions::DevicesOption: - unity::DevicesSettings::GetDefault().SetDevicesOption((unity::DevicesSettings::DevicesOption) optionGetDevicesOption()); - break;  case UnityshellOptions::AltTabTimeout:  switcher_controller_->detail_on_timeout = optionGetAltTabTimeout();  case UnityshellOptions::AltTabBiasViewport: @@ -2998,14 +2868,6 @@ void UnityScreen::Relayout()  if (!needsRelayout)  return; -#ifndef USE_MODERN_COMPIZ_GL - if (GL::fbo) - { - uScreen->_fbo = ScreenEffectFramebufferObject::Ptr (new ScreenEffectFramebufferObject (glXGetProcAddressP, geometry)); - uScreen->_fbo->onScreenSizeChanged (geometry); - } -#endif -  UScreen *uscreen = UScreen::GetDefault();  int primary_monitor = uscreen->GetPrimaryMonitor();  auto geo = uscreen->GetMonitorGeometry(primary_monitor); @@ -3120,78 +2982,124 @@ void UnityScreen::InitHints()  {  // TODO move category text into a vector... + // Compiz' plug-in names + static const std::string COMPIZ_CORE_PLUGIN_NAME = "core"; + static const std::string COMPIZ_EXPO_PLUGIN_NAME = "expo"; + static const std::string COMPIZ_GRID_PLUGIN_NAME = "grid"; + static const std::string COMPIZ_MOVE_PLUGIN_NAME = "move"; + static const std::string COMPIZ_RESIZE_PLUGIN_NAME = "resize"; + static const std::string COMPIZ_SCALE_PLUGIN_NAME = "scale"; + static const std::string COMPIZ_UNITYSHELL_PLUGIN_NAME = "unityshell"; + static const std::string COMPIZ_WALL_PLUGIN_NAME = "wall"; + + // Compiz Core Options + static const std::string COMPIZ_CORE_OPTION_SHOW_DESKTOP_KEY = "show_desktop_key"; + static const std::string COMPIZ_CORE_OPTION_MAXIMIZE_WINDOW_KEY = "maximize_window_key"; + static const std::string COMPIZ_CORE_OPTION_UNMAXIMIZE_WINDOW_KEY = "unmaximize_window_key"; + static const std::string COMPIZ_CORE_OPTION_CLOSE_WINDOW_KEY = "close_window_key"; + static const std::string COMPIZ_CORE_OPTION_WINDOW_MENU_KEY = "window_menu_key"; + + // Compiz Expo Options + static const std::string COMPIZ_EXPO_OPTION_EXPO_KEY = "expo_key"; + + // Compiz Grid Options + static const std::string COMPIZ_GRID_OPTION_PUT_LEFT_KEY = "put_left_key"; + + // Compiz Move Options + static const std::string COMPIZ_MOVE_OPTION_INITIATE_BUTTON = "initiate_button"; + + // Compiz Resize Options + static const std::string COMPIZ_RESIZE_OPTION_INITIATE_BUTTON = "initiate_button"; + + // Compiz Scale Options + static const std::string COMPIZ_SCALE_OPTION_INITIATE_ALL_KEY = "initiate_all_key"; + + // Compiz Unityshell Options + static const std::string COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER = "show_launcher"; + static const std::string COMPIZ_UNITYSHELL_OPTION_KEYBOARD_FOCUS = "keyboard_focus"; + static const std::string COMPIZ_UNITYSHELL_OPTION_LAUNCHER_SWITCHER_FORWARD = "launcher_switcher_forward"; + static const std::string COMPIZ_UNITYSHELL_OPTION_SHOW_HUD = "show_hud"; + static const std::string COMPIZ_UNITYSHELL_OPTION_PANEL_FIRST_MENU = "panel_first_menu"; + static const std::string COMPIZ_UNITYSHELL_OPTION_ALT_TAB_FORWARD = "alt_tab_forward"; + static const std::string COMPIZ_UNITYSHELL_OPTION_ALT_TAB_NEXT_WINDOW = "alt_tab_next_window"; + + // Compiz Wall Options + static const std::string COMPIZ_WALL_OPTION_LEFT_KEY = "left_key"; + static const std::string COMPIZ_WALL_OPTION_LEFT_WINDOW_KEY = "left_window_key"; + +  // Launcher... - std::string const launcher(_("Launcher")); + static const std::string launcher(_("Launcher"));  hints_.push_back(std::make_shared<shortcut::Hint>(launcher, "", _(" (Hold)"),  _("Opens the Launcher, displays shortcuts."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher" )); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(launcher, "", "",  _("Opens Launcher keyboard navigation mode."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "keyboard_focus")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_KEYBOARD_FOCUS));  hints_.push_back(std::make_shared<shortcut::Hint>(launcher, "", "",  _("Switches applications via the Launcher."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "launcher_switcher_forward")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_LAUNCHER_SWITCHER_FORWARD));  hints_.push_back(std::make_shared<shortcut::Hint>(launcher, "", _(" + 1 to 9"),  _("Same as clicking on a Launcher icon."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(launcher, "", _(" + Shift + 1 to 9"),  _("Opens a new window in the app."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(launcher, "", " + T",  _("Opens the Trash."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  // Dash... - std::string const dash( _("Dash")); + static const std::string dash( _("Dash"));  hints_.push_back(std::make_shared<shortcut::Hint>(dash, "", _(" (Tap)"),  _("Opens the Dash Home."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(dash, "", " + A",  _("Opens the Dash App Lens."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(dash, "", " + F",  _("Opens the Dash Files Lens."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(dash, "", " + M",  _("Opens the Dash Music Lens."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(dash, "", " + V",  _("Opens the Dash Video Lens."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_launcher")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_LAUNCHER));  hints_.push_back(std::make_shared<shortcut::Hint>(dash, "", "",  _("Switches between Lenses."), @@ -3209,15 +3117,15 @@ void UnityScreen::InitHints()  _("Enter")));  // Menu Bar - std::string const menubar(_("HUD & Menu Bar")); + static const std::string menubar(_("HUD & Menu Bar"));  hints_.push_back(std::make_shared<shortcut::Hint>(menubar, "", _(" (Tap)"),  _("Opens the HUD."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "show_hud")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_SHOW_HUD)); - hints_.push_back(std::make_shared<shortcut::Hint>(menubar, "", " (Hold)", + hints_.push_back(std::make_shared<shortcut::Hint>(menubar, "", _(" (Hold)"),  _("Reveals the application menu."),  shortcut::HARDCODED_OPTION,  "Alt")); @@ -3225,8 +3133,8 @@ void UnityScreen::InitHints()  hints_.push_back(std::make_shared<shortcut::Hint>(menubar, "", "",  _("Opens the indicator menu."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "panel_first_menu")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_PANEL_FIRST_MENU));  hints_.push_back(std::make_shared<shortcut::Hint>(menubar, "", "",  _("Moves focus between indicators."), @@ -3234,19 +3142,19 @@ void UnityScreen::InitHints()  _("Cursor Left or Right")));  // Switching - std::string const switching(_("Switching")); + static const std::string switching(_("Switching"));  hints_.push_back(std::make_shared<shortcut::Hint>(switching, "", "",  _("Switches between applications."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "alt_tab_forward")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_ALT_TAB_FORWARD));  hints_.push_back(std::make_shared<shortcut::Hint>(switching, "", "",  _("Switches windows of current applications."),  shortcut::COMPIZ_KEY_OPTION, - "unityshell", - "alt_tab_next_window")); + COMPIZ_UNITYSHELL_PLUGIN_NAME, + COMPIZ_UNITYSHELL_OPTION_ALT_TAB_NEXT_WINDOW));  hints_.push_back(std::make_shared<shortcut::Hint>(switching, "", "",  _("Moves the focus."), @@ -3254,69 +3162,71 @@ void UnityScreen::InitHints()  _("Cursor Left or Right")));  // Workspaces - std::string const workspaces(_("Workspaces")); + static const std::string workspaces(_("Workspaces"));  hints_.push_back(std::make_shared<shortcut::Hint>(workspaces, "", "",  _("Switches between workspaces."),  shortcut::COMPIZ_KEY_OPTION, - "expo", - "expo_key")); + COMPIZ_EXPO_PLUGIN_NAME, + COMPIZ_EXPO_OPTION_EXPO_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(workspaces, "", _(" + Arrow Keys"),  _("Switches workspaces."),  shortcut::COMPIZ_METAKEY_OPTION, - "wall", - "left_key")); + COMPIZ_WALL_PLUGIN_NAME, + COMPIZ_WALL_OPTION_LEFT_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(workspaces, "", _(" + Arrow Keys"),  _("Moves focused window to another workspace."),  shortcut::COMPIZ_METAKEY_OPTION, - "wall", - "left_window_key")); + COMPIZ_WALL_PLUGIN_NAME, + COMPIZ_WALL_OPTION_LEFT_WINDOW_KEY));  // Windows - std::string const windows(_("Windows")); + static const std::string windows(_("Windows")); +  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Spreads all windows in the current workspace."),  shortcut::COMPIZ_KEY_OPTION, - "scale", - "initiate_all_key")); + COMPIZ_SCALE_PLUGIN_NAME, + COMPIZ_SCALE_OPTION_INITIATE_ALL_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Minimises all windows."),  shortcut::COMPIZ_KEY_OPTION, - "core", - "show_desktop_key")); + COMPIZ_CORE_PLUGIN_NAME, + COMPIZ_CORE_OPTION_SHOW_DESKTOP_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Maximises the current window."),  shortcut::COMPIZ_KEY_OPTION, - "core", - "maximize_window_key")); + COMPIZ_CORE_PLUGIN_NAME, + COMPIZ_CORE_OPTION_MAXIMIZE_WINDOW_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Restores or minimises the current window."),  shortcut::COMPIZ_KEY_OPTION, - "core", - "unmaximize_window_key")); + COMPIZ_CORE_PLUGIN_NAME, + COMPIZ_CORE_OPTION_UNMAXIMIZE_WINDOW_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", _(" or Right"),  _("Semi-maximise the current window."),  shortcut::COMPIZ_KEY_OPTION, - "grid", - "put_left_key")); + COMPIZ_GRID_PLUGIN_NAME, + COMPIZ_GRID_OPTION_PUT_LEFT_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Closes the current window."),  shortcut::COMPIZ_KEY_OPTION, - "core", - "close_window_key")); + COMPIZ_CORE_PLUGIN_NAME, + COMPIZ_CORE_OPTION_CLOSE_WINDOW_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Opens the window accessibility menu."), - shortcut::HARDCODED_OPTION, - _("Alt + Space"))); + shortcut::COMPIZ_KEY_OPTION, + COMPIZ_CORE_PLUGIN_NAME, + COMPIZ_CORE_OPTION_WINDOW_MENU_KEY));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", "",  _("Places the window in corresponding position."), @@ -3326,14 +3236,14 @@ void UnityScreen::InitHints()  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", _(" Drag"),  _("Moves the window."),  shortcut::COMPIZ_MOUSE_OPTION, - "move", - "initiate_button")); + COMPIZ_MOVE_PLUGIN_NAME, + COMPIZ_MOVE_OPTION_INITIATE_BUTTON));  hints_.push_back(std::make_shared<shortcut::Hint>(windows, "", _(" Drag"),  _("Resizes the window."),  shortcut::COMPIZ_MOUSE_OPTION, - "resize", - "initiate_button")); + COMPIZ_RESIZE_PLUGIN_NAME, + COMPIZ_RESIZE_OPTION_INITIATE_BUTTON));  }  void UnityScreen::InitGesturesSupport() @@ -3363,16 +3273,65 @@ void UnityScreen::InitGesturesSupport()  }  /* Window init */ +GLTexture::List UnityWindow::close_normal_tex_; +GLTexture::List UnityWindow::close_prelight_tex_; +GLTexture::List UnityWindow::close_pressed_tex_; + +struct UnityWindow::CairoContext +{ + CairoContext(int width, int height) + : pixmap_(XCreatePixmap(screen->dpy(), screen->root(), width, height, 32)) + , texture_(GLTexture::bindPixmapToTexture(pixmap_, width, height, 32)) + , surface_(nullptr) + , cr_(nullptr) + { + Screen *xscreen = ScreenOfDisplay(screen->dpy(), screen->screenNum()); + XRenderPictFormat* format = XRenderFindStandardFormat(screen->dpy(), PictStandardARGB32); + + if (texture_.empty()) + return; + + surface_ = cairo_xlib_surface_create_with_xrender_format(screen->dpy(), pixmap_, + xscreen, format, + width, height); + cr_ = cairo_create(surface_); + + // clear + cairo_save(cr_); + cairo_set_operator(cr_, CAIRO_OPERATOR_CLEAR); + cairo_paint(cr_); + cairo_restore(cr_); + } + + ~CairoContext () + { + if (cr_) + cairo_destroy(cr_); + + if (surface_) + cairo_surface_destroy(surface_); + + texture_.clear(); + + if (pixmap_) + XFreePixmap(screen->dpy (), pixmap_); + } + + Pixmap pixmap_; + GLTexture::List texture_; + cairo_surface_t* surface_; + cairo_t *cr_; +}; +  UnityWindow::UnityWindow(CompWindow* window)  : BaseSwitchWindow (dynamic_cast<BaseSwitchScreen *> (UnityScreen::get (screen)), window)  , PluginClassHandler<UnityWindow, CompWindow>(window)  , window(window)  , gWindow(GLWindow::get(window)) - , mMinimizeHandler() - , mShowdesktopHandler(nullptr)  {  WindowInterface::setHandler(window);  GLWindowInterface::setHandler(gWindow); + ScaleWindowInterface::setHandler (ScaleWindow::get (window));  if (UnityScreen::get (screen)->optionGetShowMinimizedWindows () &&  window->mapNum ()) @@ -3415,6 +3374,320 @@ UnityWindow::UnityWindow(CompWindow* window)  }  }  } + + WindowManager::Default()->initiate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnInitiateSpreed)); + WindowManager::Default()->terminate_spread.connect(sigc::mem_fun(this, &UnityWindow::OnTerminateSpreed)); +} + +void +UnityWindow::DrawTexture(GLTexture* icon, + const GLWindowPaintAttrib& attrib, + const GLMatrix& transform, + unsigned int mask, + float x, float y, + int &maxWidth, int &maxHeight) +{ + if (icon) + { + int width, height; + width = icon->width(); + height = icon->height(); + + if (height > maxHeight) + maxHeight = height; + + if (width > maxWidth) + maxWidth = width; + + CompRegion iconReg(0, 0, width, height); + GLTexture::MatrixList ml(1); + + ml[0] = icon->matrix(); + gWindow->vertexBuffer()->begin(); + if (width && height) + gWindow->glAddGeometry(ml, iconReg, iconReg); + + if (gWindow->vertexBuffer()->end()) + { + GLMatrix wTransform(transform); + + wTransform.translate(x, y, 0.0f); + + gWindow->glDrawTexture(icon, wTransform, attrib, mask); + } + } +} + +void +UnityWindow::RenderText(UnityWindow::CairoContext const& context, + float x, float y, + float maxWidth, float maxHeight) +{ + panel::Style& style = panel::Style::Instance(); + std::string fontDescription(style.GetFontDescription(panel::PanelItem::TITLE)); + + glib::Object<PangoLayout> layout(pango_cairo_create_layout(context.cr_)); + std::shared_ptr<PangoFontDescription> font(pango_font_description_from_string(fontDescription.c_str()), + pango_font_description_free); + + pango_layout_set_font_description(layout, font.get()); + + GdkScreen* gdkScreen = gdk_screen_get_default(); + PangoContext* pCxt = pango_layout_get_context(layout); + int dpi = style.GetTextDPI(); + + pango_cairo_context_set_font_options(pCxt, gdk_screen_get_font_options(gdkScreen)); + pango_cairo_context_set_resolution(pCxt, dpi / static_cast<float>(PANGO_SCALE)); + pango_layout_context_changed(layout); + + pango_layout_set_height(layout, maxHeight); + pango_layout_set_width(layout, -1); //avoid wrap lines + pango_layout_set_auto_dir(layout, false); + pango_layout_set_text(layout, + WindowManager::Default()->GetWindowName(window->id()).c_str(), + -1); + + /* update the size of the pango layout */ + pango_cairo_update_layout(context.cr_, layout); + cairo_set_operator(context.cr_, CAIRO_OPERATOR_OVER); + cairo_set_source_rgba(context.cr_, + 1.0, + 1.0, + 1.0, + 1.0); + + // alignment + PangoRectangle lRect; + int textWidth, textHeight; + + pango_layout_get_extents(layout, NULL, &lRect); + textWidth = lRect.width / PANGO_SCALE; + textHeight = lRect.height / PANGO_SCALE; + + y = ((maxHeight - textHeight) / 2.0) + y; + cairo_translate(context.cr_, x, y); + + if (textWidth > maxWidth) + { + // apply a fade effect in the right corner + const int outPixels = textWidth - maxWidth; + const int fadingPixels = 35; + const int fadingWidth = outPixels < fadingPixels ? outPixels : fadingPixels; + + cairo_push_group(context.cr_); + pango_cairo_show_layout(context.cr_, layout); + cairo_pop_group_to_source(context.cr_); + + std::shared_ptr<cairo_pattern_t> linpat(cairo_pattern_create_linear(maxWidth - fadingWidth, + y, maxWidth, y), + cairo_pattern_destroy); + cairo_pattern_add_color_stop_rgba(linpat.get(), 0, 0, 0, 0, 1); + cairo_pattern_add_color_stop_rgba(linpat.get(), 1, 0, 0, 0, 0); + cairo_mask(context.cr_, linpat.get()); + } + else + { + pango_cairo_show_layout(context.cr_, layout); + } +} + +void +UnityWindow::DrawWindowDecoration(GLWindowPaintAttrib const& attrib, + GLMatrix const& transform, + unsigned int mask, + bool highlighted, + int x, int y, unsigned width, unsigned height) +{ + // Paint a fake window decoration + CairoContext context(width, height); + + cairo_save(context.cr_); + cairo_push_group(context.cr_); + + // Round window decoration top border + const double aspect = 1.0; + const double corner_radius = height / 10.0; + const double radius = corner_radius / aspect; + const double degrees = M_PI / 180.0; + + cairo_new_sub_path(context.cr_); + + cairo_arc(context.cr_, radius, radius, radius, 180 * degrees, 270 * degrees); + cairo_arc(context.cr_, width - radius, radius, radius, -90 * degrees, 0 * degrees); + cairo_line_to(context.cr_, width, height); + cairo_line_to(context.cr_, 0, height); + + cairo_close_path(context.cr_); + cairo_clip(context.cr_); + + // Draw window decoration based on gtk style + auto& style = panel::Style::Instance(); + gtk_render_background(style.GetStyleContext(), context.cr_, 0, 0, width, height); + gtk_render_frame(style.GetStyleContext(), context.cr_, 0, 0, width, height); + + cairo_pop_group_to_source(context.cr_); + + cairo_paint_with_alpha(context.cr_, 1.0); + cairo_restore(context.cr_); + + if (highlighted) + { + // Draw windows title + const float xText = SCALE_ITEMS_PADDING * 2 + SCALE_CLOSE_ICON_SIZE; + RenderText(context, xText, 0.0, width - xText - SCALE_ITEMS_PADDING, height); + } + + mask |= PAINT_WINDOW_BLEND_MASK; + int maxWidth, maxHeight; + + for (GLTexture *icon : context.texture_) + DrawTexture(icon, attrib, transform, mask, x, y, maxWidth , maxHeight); +} + +void UnityWindow::LoadCloseIcon(panel::WindowState state, GLTexture::List& texture) +{ + if (!texture.empty()) + return; + + auto& style = panel::Style::Instance(); + auto const& files = style.GetWindowButtonFileNames(panel::WindowButtonType::CLOSE, state); + + CompString pName("unityshell"); + for (std::string const& file : files) + { + CompString fileName(file.c_str()); + CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE); + texture = GLTexture::readImageToTexture(fileName, pName, size); + if (!texture.empty()) + break; + } + + if (texture.empty()) + { + std::string suffix; + if (state == panel::WindowState::PRELIGHT) + suffix = "_prelight"; + else if (state == panel::WindowState::PRESSED) + suffix = "_pressed"; + + CompString fileName((PKGDATADIR"/close_dash" + suffix + ".png").c_str()); + CompSize size(SCALE_CLOSE_ICON_SIZE, SCALE_CLOSE_ICON_SIZE); + texture = GLTexture::readImageToTexture(fileName, pName, size); + } +} + +void UnityWindow::SetupScaleHeaderStyle() +{ + LoadCloseIcon(panel::WindowState::NORMAL, close_normal_tex_); + LoadCloseIcon(panel::WindowState::PRELIGHT, close_prelight_tex_); + LoadCloseIcon(panel::WindowState::PRESSED, close_pressed_tex_); +} + +void UnityWindow::scalePaintDecoration(GLWindowPaintAttrib const& attrib, + GLMatrix const& transform, + CompRegion const& region, + unsigned int mask) +{ + ScaleWindow *scale_win = ScaleWindow::get(window); + if (!scale_win) + return; + + scale_win->scalePaintDecoration(attrib, transform, region, mask); + + if (!scale_win->hasSlot()) // animation not finished + return; + + UnityScreen* us = UnityScreen::get(screen); + const bool highlighted = (us->scale_highlighted_window_ == window->id()); + + ScalePosition const& pos = scale_win->getCurrentPosition(); + auto const& border_rect = window->borderRect(); + auto const& deco_ext = window->border(); + + const unsigned decoration_height = deco_ext.top; + unsigned width = (border_rect.width() + deco_ext.left + deco_ext.right) * pos.scale; + unsigned height = decoration_height * pos.scale; + int x = pos.x() + border_rect.x(); + int y = pos.y() + border_rect.y() + decoration_height - height - 1; + + // If window is highlighted, we draw the decoration at full size + if (highlighted) + height = decoration_height; + + DrawWindowDecoration(attrib, transform, mask, highlighted, x, y, width, height); + + if (highlighted) + { + x += SCALE_ITEMS_PADDING; + y += (height - SCALE_CLOSE_ICON_SIZE) / 2.0f; + int max_height = 0; + int max_width = 0; + mask |= PAINT_WINDOW_BLEND_MASK; + + switch(close_icon_state_) + { + case panel::WindowState::NORMAL: + default: + for (GLTexture *icon : close_normal_tex_) + DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height); + break; + + case panel::WindowState::PRELIGHT: + for (GLTexture *icon : close_prelight_tex_) + DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height); + break; + + case panel::WindowState::PRESSED: + for (GLTexture *icon : close_pressed_tex_) + DrawTexture(icon, attrib, transform, mask, x, y, max_width , max_height); + break; + } + + close_button_geo_.Set(x, y, max_height, max_width); + } + else if (!close_button_geo_.IsNull()) + { + close_button_geo_.Set(0, 0, 0, 0); + } +} + +void UnityWindow::scaleSelectWindow() +{ + ScaleWindow::get(window)->scaleSelectWindow(); + + UnityScreen* us = UnityScreen::get(screen); + + if (us->scale_highlighted_window_ != window->id()) + us->scale_highlighted_window_ = window->id(); +} + +void UnityWindow::OnInitiateSpreed() +{ + auto const windows = screen->windows(); + if (std::find(windows.begin(), windows.end(), window) == windows.end()) + return; + + close_icon_state_ = panel::WindowState::NORMAL; + SetupScaleHeaderStyle(); + + WindowManager *wm = WindowManager::Default(); + Window xid = window->id(); + + if (wm->IsWindowDecorated(xid)) + wm->Decorate(xid); +} + +void UnityWindow::OnTerminateSpreed() +{ + auto const windows = screen->windows(); + if (std::find(windows.begin(), windows.end(), window) == windows.end()) + return; + + WindowManager *wm = WindowManager::Default(); + Window xid = window->id(); + + if (wm->IsWindowDecorated(xid) && wm->IsWindowMaximized(xid)) + wm->Undecorate(xid);  }  UnityWindow::~UnityWindow() @@ -3437,13 +3710,10 @@ UnityWindow::~UnityWindow()  ShowdesktopHandler::animating_windows.remove (static_cast <ShowdesktopHandlerWindowInterface *> (this)); - if (mShowdesktopHandler) - delete mShowdesktopHandler; -  if (window->state () & CompWindowStateFullscreenMask)  UnityScreen::get (screen)->fullscreen_windows_.remove(window); - PluginAdapter::Default ()->OnWindowClosed (window); + PluginAdapter::Default ()->OnWindowClosed(window);  }  /* vtable init */ @@ -3474,7 +3744,6 @@ bool UnityPluginVTable::init()  return true;  } -  namespace  { diff --git a/plugins/unityshell/src/unityshell.h b/plugins/unityshell/src/unityshell.h index afc9b8da2..a1de06832 100644 --- a/plugins/unityshell/src/unityshell.h +++ b/plugins/unityshell/src/unityshell.h @@ -29,6 +29,7 @@  #include <sigc++/sigc++.h>  #include <boost/shared_ptr.hpp> +#include <scale/scale.h>  #include <core/core.h>  #include <core/pluginclasshandler.h>  #include <composite/composite.h> @@ -54,9 +55,6 @@  #include "UnityshellPrivate.h"  #include "UnityShowdesktopHandler.h"  #include "ThumbnailGenerator.h" -#ifndef USE_MODERN_COMPIZ_GL -#include "ScreenEffectFramebufferObject.h" -#endif  #include "compizminimizedwindowhandler.h"  #include "BGHash.h" @@ -65,6 +63,8 @@  #include "HudController.h"  #include "ThumbnailGenerator.h" +#include "WindowMinimizeSpeedController.h" +  namespace unity  { @@ -94,11 +94,7 @@ public:  void nuxEpilogue();  /* nux draw wrapper */ -#ifdef USE_MODERN_COMPIZ_GL  void paintDisplay(); -#else - void paintDisplay(const CompRegion& region, const GLMatrix& transform, unsigned int mask); -#endif  void paintPanelShadow(const GLMatrix& matrix);  void setPanelShadowMatrix(const GLMatrix& matrix); @@ -189,6 +185,8 @@ public:  void SetUpAndShowSwitcher(switcher::ShowMode show_mode = switcher::ShowMode::CURRENT_VIEWPORT); + void OnMinimizeDurationChanged(); +  switcher::Controller::Ptr switcher_controller();  launcher::Controller::Ptr launcher_controller(); @@ -243,6 +241,10 @@ private:  void InitGesturesSupport(); + void DrawTopPanelBackground(); + bool TopPanelBackgroundTextureNeedsUpdate() const; + void UpdateTopPanelBackgroundTexture(); +  nux::animation::TickSource tick_source_;  nux::animation::AnimationController animation_controller_; @@ -306,12 +308,7 @@ private:  BGHash _bghash; -#ifdef USE_MODERN_COMPIZ_GL  ::GLFramebufferObject *oldFbo; -#else - ScreenEffectFramebufferObject::Ptr _fbo; - GLuint _active_fbo; -#endif  bool queryForShader (); @@ -332,14 +329,13 @@ private:  bool scale_just_activated_; -#ifndef USE_MODERN_COMPIZ_GL - ScreenEffectFramebufferObject::GLXGetProcAddressProc glXGetProcAddressP; -#endif -  UBusManager ubus_manager_;  glib::SourceManager sources_;  unity::ThumbnailGenerator thumb_generator; -  + + Window scale_highlighted_window_; + + WindowMinimizeSpeedController* minimize_speed_controller;  friend class UnityWindow;  }; @@ -348,6 +344,7 @@ class UnityWindow :  public GLWindowInterface,  public ShowdesktopHandlerWindowInterface,  public compiz::WindowInputRemoverLockAcquireInterface, + public WrapableHandler<ScaleWindowInterface, 4>,  public BaseSwitchWindow,  public PluginClassHandler <UnityWindow, CompWindow>  { @@ -377,11 +374,7 @@ public:  /* basic window draw function */  bool glDraw(const GLMatrix& matrix, -#ifndef USE_MODERN_COMPIZ_GL - GLFragment::Attrib& attrib, -#else  const GLWindowPaintAttrib& attrib, -#endif  const CompRegion& region,  unsigned intmask); @@ -406,17 +399,25 @@ public:  void leaveShowDesktop ();  bool HandleAnimations (unsigned int ms); - void handleEvent (XEvent *event); + bool handleEvent(XEvent *event);  typedef compiz::CompizMinimizedWindowHandler<UnityScreen, UnityWindow>  UnityMinimizedHandler;  std::unique_ptr <UnityMinimizedHandler> mMinimizeHandler; - - ShowdesktopHandler *mShowdesktopHandler; + std::unique_ptr <ShowdesktopHandler> mShowdesktopHandler;  //! Emited when CompWindowNotifyBeforeDestroy is received  sigc::signal<void> being_destroyed; + + void scaleSelectWindow(); + void scalePaintDecoration(const GLWindowPaintAttrib &, + const GLMatrix &, + const CompRegion &, + unsigned int); +  private: + struct CairoContext; +  void DoEnableFocus ();  void DoDisableFocus (); @@ -436,6 +437,9 @@ private:  void DoShow ();  void DoNotifyShown (); + void OnInitiateSpreed(); + void OnTerminateSpreed(); +  void DoAddDamage ();  ShowdesktopHandlerWindowInterface::PostPaintAction DoHandleAnimations (unsigned int ms); @@ -447,7 +451,30 @@ private:  compiz::WindowInputRemoverLock::Ptr GetInputRemover (); + void DrawWindowDecoration(GLWindowPaintAttrib const& attrib, + GLMatrix const& transform, + unsigned int mask, + bool highlighted, + int x, int y, unsigned width, unsigned height); + void DrawTexture(GLTexture *icon, + const GLWindowPaintAttrib& attrib, + const GLMatrix& transform, + unsigned int mask, + float x, float y, + int &maxWidth, int &maxHeight); + void RenderText(CairoContext const& context, + float x, float y, + float maxWidth, float maxHeight); + + void SetupScaleHeaderStyle(); + void LoadCloseIcon(panel::WindowState state, GLTexture::List& texture); + + static GLTexture::List close_normal_tex_; + static GLTexture::List close_prelight_tex_; + static GLTexture::List close_pressed_tex_;  compiz::WindowInputRemoverLock::Weak input_remover_; + panel::WindowState close_icon_state_; + nux::Geometry close_button_geo_;  glib::Source::UniquePtr focus_desktop_timeout_;  }; diff --git a/plugins/unityshell/unityshell.xml.in b/plugins/unityshell/unityshell.xml.in index 1f0dab978..98571394a 100644 --- a/plugins/unityshell/unityshell.xml.in +++ b/plugins/unityshell/unityshell.xml.in @@ -394,26 +394,6 @@  <default>75</default>  </option> - <option name="devices_option" type="int"> - <_short>Show Devices</_short> - <_long>Show devices in the launcher</_long> - <min>0</min> - <max>2</max> - <default>1</default> - <desc> - <value>0</value> - <_name>Never</_name> - </desc> - <desc> - <value>1</value> - <_name>Only Mounted</_name> - </desc> - <desc> - <value>2</value> - <_name>Always</_name> - </desc> - </option> -  <option name="shortcut_overlay" type="bool">  <_short>Enable Shortcut Hints Overlay</_short>  <_long>Enable Shortcut Hints Overlay</_long> diff --git a/po/POTFILES.in b/po/POTFILES.in index 9b8c17cd5..494439443 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -11,11 +11,12 @@ hud/HudView.cpp  launcher/BFBLauncherIcon.cpp  launcher/BamfLauncherIcon.cpp  launcher/DesktopLauncherIcon.cpp -launcher/DeviceLauncherIcon.cpp +launcher/DeviceNotificationDisplayImp.cpp  launcher/LauncherController.cpp  launcher/SoftwareCenterLauncherIcon.cpp  launcher/SpacerLauncherIcon.cpp  launcher/TrashLauncherIcon.cpp +launcher/VolumeLauncherIcon.cpp  panel/PanelMenuView.cpp  plugins/networkarearegion/networkarearegion.xml.in  plugins/unity-mt-grab-handles/unitymtgrabhandles.xml.in diff --git a/services/CMakeLists.txt b/services/CMakeLists.txt index 865aa4125..2da54dce0 100644 --- a/services/CMakeLists.txt +++ b/services/CMakeLists.txt @@ -2,7 +2,8 @@  # Panel Service  #  find_package(PkgConfig) -pkg_check_modules(SERVICE_DEPS REQUIRED gtk+-3.0>=3.3 gobject-2.0 gio-2.0 gthread-2.0 indicator3-0.4>=0.4.90 x11) + +pkg_check_modules(SERVICE_DEPS REQUIRED gtk+-3.0>=3.3 gobject-2.0 gio-2.0 gthread-2.0 indicator3-0.4>=0.4.90 x11 atk-bridge-2.0)  execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable indicatordir OUTPUT_VARIABLE _indicatordir OUTPUT_STRIP_TRAILING_WHITESPACE)  execute_process (COMMAND ${PKG_CONFIG_EXECUTABLE} indicator3-0.4 --variable iconsdir OUTPUT_VARIABLE _iconsdir OUTPUT_STRIP_TRAILING_WHITESPACE) diff --git a/services/panel-a11y.c b/services/panel-a11y.c index 6f870555b..a22b39b87 100644 --- a/services/panel-a11y.c +++ b/services/panel-a11y.c @@ -17,119 +17,16 @@  */  #include <gio/gio.h> +#include <atk-bridge.h>  #include "panel-a11y.h"  #include "panel-util-accessible.h"  static gboolean a11y_initialized = FALSE; -#define INIT_METHOD "gnome_accessibility_module_init" -#define DESKTOP_SCHEMA "org.gnome.desktop.interface" -#define ACCESSIBILITY_ENABLED_KEY "toolkit-accessibility" -#define AT_SPI_SCHEMA "org.a11y.atspi" -#define ATK_BRIDGE_LOCATION_KEY "atk-bridge-location" - - -/* This method is required because g_setting_new abort if the schema - is not present. */ -static gboolean -has_gsettings_schema (const gchar *schema) -{ - const char * const *list_schemas = NULL; - gboolean found = FALSE; - int i = 0; - - list_schemas = g_settings_list_schemas (); - for (i = 0; list_schemas [i]; i++) - { - if (!g_strcmp0 (list_schemas[i], schema)) - { - found = TRUE; - break; - } - } - - return found; -} - -static gboolean -should_enable_a11y (void) -{ - GSettings *desktop_settings = NULL; - gboolean value = FALSE; - - if (!has_gsettings_schema (DESKTOP_SCHEMA)) - return FALSE; - - desktop_settings = g_settings_new (DESKTOP_SCHEMA); - value = g_settings_get_boolean (desktop_settings, ACCESSIBILITY_ENABLED_KEY); - - g_object_unref (desktop_settings); - - return value; -} - -static gchar* -get_atk_bridge_path (void) -{ - GSettings *atspi_settings = NULL; - GVariant *variant = NULL; - char *value = NULL; - - if (!has_gsettings_schema (AT_SPI_SCHEMA)) - return NULL; - - atspi_settings = g_settings_new(AT_SPI_SCHEMA); - variant = g_settings_get_value (atspi_settings, ATK_BRIDGE_LOCATION_KEY); - value = g_variant_dup_bytestring (variant, NULL); - - g_variant_unref (variant); - g_object_unref (atspi_settings); - - return value; -} - -static gboolean -a11y_invoke_module (const char *module_path) -{ - GModule *handle; - void (*invoke_fn) (void); - - if (!module_path) - { - g_warning ("Accessibility: invalid module path (NULL)"); - - return FALSE; - } - - if (!(handle = g_module_open (module_path, (GModuleFlags)0))) - { - g_warning ("Accessibility: failed to load module '%s': '%s'", - module_path, g_module_error ()); - - return FALSE; - } - - if (!g_module_symbol (handle, INIT_METHOD, (gpointer *)&invoke_fn)) - { - g_warning ("Accessibility: error library '%s' does not include " - "method '%s' required for accessibility support", - module_path, INIT_METHOD); - g_module_close (handle); - - return FALSE; - } - - invoke_fn (); - - return TRUE; -} -  void  panel_a11y_init (void)  { - gchar *bridge_path = NULL; -  if (a11y_initialized)  return; @@ -137,20 +34,9 @@ panel_a11y_init (void)  g_unsetenv ("NO_AT_BRIDGE");  g_unsetenv ("NO_GAIL"); - if (!should_enable_a11y ()) - return; -  /* Load PanelUtilAccessible class */  g_type_class_unref (g_type_class_ref (PANEL_TYPE_UTIL_ACCESSIBLE)); - - bridge_path = get_atk_bridge_path (); - if (a11y_invoke_module (bridge_path)) - { - g_debug ("Unity accessibility started, using bridge on %s", bridge_path); - } - - g_free (bridge_path); - atk_get_root (); + atk_bridge_adaptor_init(NULL, NULL);  a11y_initialized = TRUE;  } diff --git a/shortcuts/StandaloneShortcuts.cpp b/shortcuts/StandaloneShortcuts.cpp index a9ae5edce..6285967ba 100644 --- a/shortcuts/StandaloneShortcuts.cpp +++ b/shortcuts/StandaloneShortcuts.cpp @@ -205,12 +205,11 @@ void ThreadWidgetInit(nux::NThread* thread, void* InitData)  "core",  "close_window_key"))); - - // I don't know std::shared_ptr<shortcut::AbstractHint>(if it is really hardcoded, but I can't find where this option is stored.  hints.push_back(std::shared_ptr<shortcut::AbstractHint>(new shortcut::MockHint(_("Windows"), "", "",  _("Opens the window accessibility menu."), - shortcut::HARDCODED_OPTION, - "Alt+Space"))); + shortcut::COMPIZ_KEY_OPTION, + "core", + "window_menu_key")));  hints.push_back(std::shared_ptr<shortcut::AbstractHint>(new shortcut::MockHint(_("Windows"), "", "",  _("Places the window in corresponding position."), diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 453e3082c..e8542d1fc 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -213,6 +213,7 @@ if (GTEST_SRC_DIR AND  test_icon_loader.cpp  test_im_text_entry.cpp  test_launcher_controller.cpp + test_launcher_drag_window.cpp  test_keyboard_util.cpp  test_panel_style.cpp  test_previews_application.cpp @@ -227,6 +228,10 @@ if (GTEST_SRC_DIR AND  test_switcher_model.cpp  test_texture_cache.cpp  test_thumbnail_generator.cpp + test_launcher_minimize_speed.cpp + test_volume_imp.cpp + test_volume_launcher_icon.cpp + gmockmount.c  gmockvolume.c  ${CMAKE_SOURCE_DIR}/dash/AbstractPlacesGroup.cpp  ${CMAKE_SOURCE_DIR}/dash/DashViewPrivate.cpp @@ -258,14 +263,15 @@ if (GTEST_SRC_DIR AND  ${CMAKE_SOURCE_DIR}/launcher/DNDCollectionWindow.cpp  ${CMAKE_SOURCE_DIR}/launcher/Decaymulator.cpp  ${CMAKE_SOURCE_DIR}/launcher/DesktopLauncherIcon.cpp - ${CMAKE_SOURCE_DIR}/launcher/DeviceLauncherIcon.cpp  ${CMAKE_SOURCE_DIR}/launcher/DeviceLauncherSection.cpp - ${CMAKE_SOURCE_DIR}/launcher/DevicesSettings.cpp + ${CMAKE_SOURCE_DIR}/launcher/DeviceNotificationDisplayImp.cpp + ${CMAKE_SOURCE_DIR}/launcher/DevicesSettingsImp.cpp  ${CMAKE_SOURCE_DIR}/launcher/DndData.cpp  ${CMAKE_SOURCE_DIR}/launcher/EdgeBarrierController.cpp  ${CMAKE_SOURCE_DIR}/launcher/FavoriteStore.cpp  ${CMAKE_SOURCE_DIR}/launcher/FavoriteStoreGSettings.cpp  ${CMAKE_SOURCE_DIR}/launcher/FavoriteStorePrivate.cpp + ${CMAKE_SOURCE_DIR}/launcher/FileManagerOpenerImp.cpp  ${CMAKE_SOURCE_DIR}/launcher/HudLauncherIcon.cpp  ${CMAKE_SOURCE_DIR}/launcher/Launcher.cpp  ${CMAKE_SOURCE_DIR}/launcher/LauncherController.cpp @@ -296,6 +302,8 @@ if (GTEST_SRC_DIR AND  ${CMAKE_SOURCE_DIR}/launcher/SwitcherView.cpp  ${CMAKE_SOURCE_DIR}/launcher/Tooltip.cpp  ${CMAKE_SOURCE_DIR}/launcher/TrashLauncherIcon.cpp + ${CMAKE_SOURCE_DIR}/launcher/VolumeImp.cpp + ${CMAKE_SOURCE_DIR}/launcher/VolumeLauncherIcon.cpp  ${CMAKE_SOURCE_DIR}/launcher/VolumeMonitorWrapper.cpp  ${CMAKE_SOURCE_DIR}/unity-shared/Animator.cpp  ${CMAKE_SOURCE_DIR}/unity-shared/BackgroundEffectHelper.cpp @@ -332,6 +340,7 @@ if (GTEST_SRC_DIR AND  ${CMAKE_SOURCE_DIR}/unity-shared/UserThumbnailProvider.cpp  ${CMAKE_SOURCE_DIR}/unity-shared/WindowManager.cpp  ${CMAKE_SOURCE_DIR}/unity-shared/ubus-server.cpp + ${CMAKE_SOURCE_DIR}/plugins/unityshell/src/WindowMinimizeSpeedController.cpp  )  target_link_libraries(test-gtest gtest gmock ${LIBS})  add_test(UnityGTest test-gtest) diff --git a/tests/autopilot/unity/emulators/workspace.py b/tests/autopilot/unity/emulators/workspace.py index 773b968c1..7b02f10a8 100644 --- a/tests/autopilot/unity/emulators/workspace.py +++ b/tests/autopilot/unity/emulators/workspace.py @@ -10,7 +10,6 @@  from __future__ import absolute_import -from autopilot.globals import global_context  from autopilot.keybindings import KeybindingsHelper  from autopilot.utilities import (  get_compiz_option, diff --git a/tests/autopilot/unity/tests/test_hud.py b/tests/autopilot/unity/tests/test_hud.py index 987a2b580..42261b459 100644 --- a/tests/autopilot/unity/tests/test_hud.py +++ b/tests/autopilot/unity/tests/test_hud.py @@ -247,11 +247,11 @@ class HudBehaviorTests(HudTestsBase):  self.mouse.click()  self.assertThat(self.hud.visible, Eventually(Equals(False))) -  +  def test_hud_closes_click_after_text_removed(self):  """Clicking outside of the hud after a search text has been entered and  then removed from the searchbox will make it close.""" -  +  self.hud.ensure_visible()  self.keyboard.type("Test")  self.keyboard.press_and_release("Escape") @@ -259,7 +259,7 @@ class HudBehaviorTests(HudTestsBase):  (x,y,w,h) = self.hud.view.geometry  self.mouse.move(w/2, h+50)  self.mouse.click() -  +  self.assertThat(self.hud.visible, Eventually(Equals(False)))  def test_alt_f4_close_hud(self): @@ -276,49 +276,49 @@ class HudBehaviorTests(HudTestsBase):  self.hud.ensure_visible()  self.keyboard.press_and_release("Alt+F4")  self.assertThat(self.hud.visible, Eventually(Equals(False))) -  +  def test_app_activate_on_enter(self):  """Hud must close after activating a search item with Enter.""" - self.hud.ensure_visible()  -  + self.hud.ensure_visible() +  self.keyboard.type("Device > System Settings")  self.assertThat(self.hud.search_string, Eventually(Equals("Device > System Settings"))) -  +  self.keyboard.press_and_release("Enter") -  +  app_found = self.bamf.wait_until_application_is_running("gnome-control-center.desktop", 5)  self.assertTrue(app_found)  self.addCleanup(self.close_all_app, "System Settings") -  +  self.assertThat(self.hud.visible, Eventually(Equals(False))) -  +  def test_hud_closes_on_escape(self):  """Hud must close on escape after searchbox is cleared"""  self.hud.ensure_visible() -  +  self.keyboard.type("ThisText")  self.keyboard.press_and_release("Escape")  self.keyboard.press_and_release("Escape") -  +  self.assertThat(self.hud.visible, Eventually(Equals(False))) -  +  def test_hud_closes_on_escape_shrunk(self):  """Hud must close when escape key is pressed"""  self.hud.ensure_visible()  self.keyboard.press_and_release("Escape") -  +  self.assertThat(self.hud.visible, Eventually(Equals(False)))  def test_alt_arrow_keys_not_eaten(self):  """Tests that Alt+ArrowKey events are correctly passed to the  active window when Unity is not responding to them.""" -  +  self.start_app_window("Terminal") -  +  #There's no easy way to read text from terminal, writing input  #to a text file and then reading from there works.  self.keyboard.type('echo "') -  +  #Terminal is receiving input with Alt+Arrowkeys  self.keyboard.press("Alt")  self.keyboard.press_and_release("Up") @@ -326,13 +326,13 @@ class HudBehaviorTests(HudTestsBase):  self.keyboard.press_and_release("Right")  self.keyboard.press_and_release("Left")  self.keyboard.release("Alt") -  +  self.keyboard.type('" > /tmp/ap_test_alt_keys')  self.addCleanup(remove, '/tmp/ap_test_alt_keys')  self.keyboard.press_and_release("Enter") -  +  file_contents = open('/tmp/ap_test_alt_keys', 'r').read().strip() -  +  self.assertThat(file_contents, Equals('ABCD'))  def test_hud_closes_on_item_activated(self): @@ -357,6 +357,41 @@ class HudBehaviorTests(HudTestsBase):  self.assertThat(self.hud.visible, Eventually(Equals(False))) + def test_mouse_changes_selected_hud_button(self): + """This tests moves the mouse from the top of the screen to the bottom, this must + change the selected button from 1 to 5. + """ + + self.hud.ensure_visible() + + self.keyboard.type("a") + (x,y,w,h) = self.hud.view.geometry + + self.mouse.move(w/2, 0) + self.assertThat(self.hud.view.selected_button, Eventually(Equals(1))) + + self.mouse.move(w/2, h) + self.assertThat(self.hud.view.selected_button, Eventually(Equals(5))) + + def test_keyboard_steals_focus_from_mouse(self): + """This tests moves the mouse from the top of the screen to the bottom, + then it presses the keyboard up 5 times, this must change the selected button from 5 to 1. + """ + + self.hud.ensure_visible() + + self.keyboard.type("a") + (x,y,w,h) = self.hud.view.geometry + + self.mouse.move(w/2, 0) + self.mouse.move(w/2, h) + self.assertThat(self.hud.view.selected_button, Eventually(Equals(5))) + + for i in range(5): + self.keyboard.press_and_release('Up') + + self.assertThat(self.hud.view.selected_button, Eventually(Equals(1))) +  class HudLauncherInteractionsTests(HudTestsBase): diff --git a/tests/autopilot/unity/tests/xim/__init__.py b/tests/autopilot/unity/tests/xim/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/autopilot/unity/tests/xim/__init__.py diff --git a/tests/autopilot/unity/tests/xim/test_gcin.py b/tests/autopilot/unity/tests/xim/test_gcin.py new file mode 100644 index 000000000..f7fe3f8a7 --- /dev/null +++ b/tests/autopilot/unity/tests/xim/test_gcin.py @@ -0,0 +1,72 @@ +# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- +# Copyright 2012 Canonical +# Author: Brandon Schaefer +# +# 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. + +from __future__ import absolute_import + +from autopilot.matchers import Eventually +from os import environ +import subprocess +from testtools.matchers import Equals + +from unity.tests import UnityTestCase + + +class GcinTestCase(UnityTestCase): + """Tests the Input Method gcin.""" + + def setUp(self): + super(GcinTestCase, self).setUp() + + # Check that gcin is set as the active IM through im-switch + if environ.get('XMODIFIERS', '') != "@im=gcin": + self.skip("Please make sure XMODIFIERS is set to @im=gcin. Set it using 'im-switch'.") + + running_process = subprocess.check_output('ps -e', shell=True) + if 'gcin' not in running_process: + self.skip("gcin is not an active process, please start 'gcin' before running these tests.") + + if 'ibus' in running_process: + self.skip("IBus is currently running, please close 'ibus-daemon' before running these tests.") + + +class GcinTestHangul(GcinTestCase): + """Tests the Dash and Hud with gcin in hangul mode.""" + + scenarios = [ + ('hangul', {'input': 'han geul ', 'result': u'\ud55c\uae00'}), + ('morning letters', {'input': 'a chimgeul ', 'result': u'\uc544\uce68\uae00'}), + ('national script', {'input': 'gug mun ', 'result': u'\uad6d\ubb38'}), + ] + + def setUp(self): + super(GcinTestHangul, self).setUp() + + def enter_hangul_mode(self): + """Ctrl+Space turns gcin on, Ctrl+Alt+/ turns hangul on.""" + self.keyboard.press_and_release("Ctrl+Space") + self.keyboard.press_and_release("Ctrl+Alt+/") + + def test_dash_input(self): + """Entering an input string through gcin will result in a Korean string result in the dash.""" + + self.dash.ensure_visible() + self.addCleanup(self.dash.ensure_hidden) + self.enter_hangul_mode() + + self.keyboard.type(self.input) + self.assertThat(self.dash.search_string, Eventually(Equals(self.result))) + + def test_hud_input(self): + """Entering an input string through gcin will result in a Korean string result in the hud.""" + + self.hud.ensure_visible() + self.addCleanup(self.hud.ensure_hidden) + self.enter_hangul_mode() + + self.keyboard.type(self.input) + self.assertThat(self.hud.search_string, Eventually(Equals(self.result))) diff --git a/tests/gmockmount.c b/tests/gmockmount.c new file mode 100644 index 000000000..fd4c3d7a1 --- /dev/null +++ b/tests/gmockmount.c @@ -0,0 +1,167 @@ +// -*- 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> + * + */ + +#include <glib.h> + +#include "gmockmount.h" + +static void g_mock_mount_iface_init (GMountIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GMockMount, g_mock_mount, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_MOUNT, +	g_mock_mount_iface_init)) + +static void +g_mock_mount_finalize (GObject *object) +{ + G_OBJECT_CLASS (g_mock_mount_parent_class)->finalize (object); +} + +static void +g_mock_mount_dispose (GObject *object) +{ + G_OBJECT_CLASS (g_mock_mount_parent_class)->dispose (object); +} + + +static void +g_mock_mount_class_init (GMockMountClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = g_mock_mount_finalize; + gobject_class->dispose = g_mock_mount_dispose; +} + +static void +g_mock_mount_init (GMockMount *mock_mount) +{} + +GMockMount * +g_mock_mount_new () +{ + GMockMount *mount; + + mount = g_object_new (G_TYPE_MOCK_MOUNT, NULL); + + return mount; +} + +static GFile * +g_mock_mount_get_root (GMount *mount) +{ + return g_file_new_for_path (ROOT_FILE_PATH); +} + +static GIcon * +g_mock_mount_get_icon (GMount *mount) +{ + return NULL; +} + +static char * +g_mock_mount_get_uuid (GMount *mount) +{ + return g_strdup (""); +} + +static char * +g_mock_mount_get_name (GMount *mount) +{ + return g_strdup (""); +} + +static GDrive * +g_mock_mount_get_drive (GMount *mount) +{ + return NULL; +} + +static GVolume * +g_mock_mount_get_volume (GMount *mount) +{ + return NULL; +} + +static gboolean +g_mock_mount_can_unmount (GMount *mount) +{ + return TRUE; +} + +static gboolean +g_mock_mount_can_eject (GMount *mount) +{ + return FALSE; +} + +static void +g_mock_mount_unmount (GMount *mount, + GMountUnmountFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ +} + +static gboolean +g_mock_mount_unmount_finish (GMount *mount, + GAsyncResult *result, + GError **error) +{ + return TRUE; +} + +static void +g_mock_mount_eject (GMount *mount, + GMountUnmountFlags flags, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ +} + +static gboolean +g_mock_mount_eject_finish (GMount *mount, + GAsyncResult *result, + GError **error) +{ + return TRUE; +} + + +static void +g_mock_mount_iface_init (GMountIface *iface) +{ + iface->get_root = g_mock_mount_get_root; + iface->get_name = g_mock_mount_get_name; + iface->get_icon = g_mock_mount_get_icon; + iface->get_uuid = g_mock_mount_get_uuid; + iface->get_drive = g_mock_mount_get_drive; + iface->get_volume = g_mock_mount_get_volume; + iface->can_unmount = g_mock_mount_can_unmount; + iface->can_eject = g_mock_mount_can_eject; + iface->unmount = g_mock_mount_unmount; + iface->unmount_finish = g_mock_mount_unmount_finish; + iface->eject = g_mock_mount_eject; + iface->eject_finish = g_mock_mount_eject_finish; +} diff --git a/tests/gmockmount.h b/tests/gmockmount.h new file mode 100644 index 000000000..b5eb6b5af --- /dev/null +++ b/tests/gmockmount.h @@ -0,0 +1,55 @@ +// -*- 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 UNITYSHELL_G_MOCK_MOUNT_H +#define UNITYSHELL_G_MOCK_MOUNT_H + +#include <gio/gio.h> + +G_BEGIN_DECLS + +#define ROOT_FILE_PATH "/some/directory/testfile" +#define ROOT_FILE_URI "file://" ROOT_FILE_PATH + +#define G_TYPE_MOCK_MOUNT (g_mock_mount_get_type ()) +#define G_MOCK_MOUNT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MOCK_MOUNT, GMockMount)) +#define G_MOCK_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MOCK_MOUNT, GMockMountClass)) +#define G_IS_MOCK_MOUNT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MOCK_MOUNT)) +#define G_IS_MOCK_MOUNT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MOCK_MOUNT)) + +typedef struct _GMockMount GMockMount; +typedef struct _GMockMountClass GMockMountClass; + +struct _GMockMount { + GObject parent; +}; + +struct _GMockMountClass { + GObjectClass parent_class; +}; + +GType g_mock_mount_get_type (void) G_GNUC_CONST; +GMockMount * g_mock_mount_new (); + +G_END_DECLS + +#endif // UNITYSHELL_G_MOCK_MOUNT_H diff --git a/tests/gmockvolume.c b/tests/gmockvolume.c index 5ee2d23a1..be667f2b8 100644 --- a/tests/gmockvolume.c +++ b/tests/gmockvolume.c @@ -22,13 +22,13 @@  #include <glib.h> +#include "gmockmount.h"  #include "gmockvolume.h" -static void g_mock_volume_volume_iface_init (GVolumeIface *iface); +static void g_mock_volume_iface_init (GVolumeIface *iface);  G_DEFINE_TYPE_WITH_CODE (GMockVolume, g_mock_volume, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, -	g_mock_volume_volume_iface_init)) + G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME, g_mock_volume_iface_init))  static void  g_mock_volume_finalize (GObject *object) @@ -37,44 +37,119 @@ g_mock_volume_finalize (GObject *object)  }  static void +g_mock_volume_dispose (GObject *object) +{ + GMockVolume *self = G_MOCK_VOLUME (object); + + self->can_eject = FALSE; + + if (self->name) + { + g_free(self->name); + self->name = NULL; + } + + if (self->icon) + { + g_object_unref(self->icon); + self->icon = NULL; + } + + if (self->uuid) + { + g_free(self->uuid); + self->uuid = NULL; + } + + if (self->mount) + { + g_object_unref(self->mount); + self->mount = NULL; + } + + G_OBJECT_CLASS (g_mock_volume_parent_class)->dispose (object); +} + + +static void  g_mock_volume_class_init (GMockVolumeClass *klass)  {  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);  gobject_class->finalize = g_mock_volume_finalize; + gobject_class->dispose = g_mock_volume_dispose;  }  static void  g_mock_volume_init (GMockVolume *mock_volume)  { + mock_volume->name = g_strdup(""); + mock_volume->icon = g_icon_new_for_string("", NULL); + mock_volume->uuid = g_strdup(""); + mock_volume->mount = NULL;  }  GMockVolume *  g_mock_volume_new ()  {  GMockVolume *volume; -  +  volume = g_object_new (G_TYPE_MOCK_VOLUME, NULL);  return volume;  } +void +g_mock_volume_set_name (GMockVolume *volume, const char* name) +{ + if (volume->name) + g_free(volume->name); + + volume->name = g_strdup (name); +} +  static char *  g_mock_volume_get_name (GVolume *volume)  { - return g_strdup (""); + GMockVolume *self = G_MOCK_VOLUME (volume); + return g_strdup (self->name); +} + +void +g_mock_volume_set_icon (GMockVolume *volume, GIcon *icon) +{ + if (volume->icon) + g_object_unref(volume->icon); + + volume->icon = icon;  } +  static GIcon *  g_mock_volume_get_icon (GVolume *volume)  { - return g_icon_new_for_string("", NULL); + GMockVolume *self = G_MOCK_VOLUME (volume); + + if (self->icon) + return g_object_ref (self->icon); + else + return NULL; +} + +void +g_mock_volume_set_uuid (GMockVolume *volume, const char* uuid) +{ + if (volume->uuid) + g_free(volume->uuid); + + volume->uuid = g_strdup (uuid);  }  static char *  g_mock_volume_get_uuid (GVolume *volume)  { - return NULL; + GMockVolume *self = G_MOCK_VOLUME (volume); + return g_strdup (self->uuid);  }  static GDrive * @@ -83,10 +158,23 @@ g_mock_volume_get_drive (GVolume *volume)  return NULL;  } +void +g_mock_volume_set_mount (GMockVolume *volume, GMount *mount) +{ + if (volume->mount) + g_object_unref(volume->mount); + + volume->mount = mount; +} +  static GMount *  g_mock_volume_get_mount (GVolume *volume)  { - return NULL; + GMockVolume *self = G_MOCK_VOLUME (volume); + if (self->mount) + return g_object_ref (self->mount); + else + return NULL;  }  static gboolean @@ -98,7 +186,14 @@ g_mock_volume_can_mount (GVolume *volume)  static gboolean  g_mock_volume_can_eject (GVolume *volume)  { - return FALSE; + GMockVolume *self = G_MOCK_VOLUME (volume); + return self->can_eject; +} + +void +g_mock_volume_set_can_eject (GMockVolume* mock_volume, gboolean can_eject) +{ + mock_volume->can_eject = can_eject;  }  static gboolean @@ -115,6 +210,11 @@ g_mock_volume_mount (GVolume *volume,  GAsyncReadyCallback callback,  gpointer user_data)  { + g_mock_volume_set_mount(G_MOCK_VOLUME(volume), G_MOUNT(g_mock_mount_new())); + + callback(NULL, + G_ASYNC_RESULT (g_simple_async_result_new (NULL, NULL, NULL, NULL)), + user_data);  }  static gboolean @@ -132,7 +232,10 @@ g_mock_volume_eject (GVolume *volume,  GAsyncReadyCallback callback,  gpointer user_data)  { -} +  + callback(NULL, + G_ASYNC_RESULT (g_simple_async_result_new (NULL, NULL, NULL, NULL)), + user_data);}  static gboolean  g_mock_volume_eject_finish (GVolume *volume, @@ -146,6 +249,11 @@ static gchar *  g_mock_volume_get_identifier (GVolume *volume,  const gchar *kind)  { + GMockVolume *self = G_MOCK_VOLUME (volume); + + if (!g_strcmp0 (kind, G_VOLUME_IDENTIFIER_KIND_UUID)) + return g_strdup (self->uuid); +  return NULL;  } @@ -156,7 +264,7 @@ g_mock_volume_enumerate_identifiers (GVolume *volume)  }  static void -g_mock_volume_volume_iface_init (GVolumeIface *iface) +g_mock_volume_iface_init (GVolumeIface *iface)  {  iface->get_name = g_mock_volume_get_name;  iface->get_icon = g_mock_volume_get_icon; @@ -173,4 +281,3 @@ g_mock_volume_volume_iface_init (GVolumeIface *iface)  iface->get_identifier = g_mock_volume_get_identifier;  iface->enumerate_identifiers = g_mock_volume_enumerate_identifiers;  } - diff --git a/tests/gmockvolume.h b/tests/gmockvolume.h index 0c9ee118a..bec8e0962 100644 --- a/tests/gmockvolume.h +++ b/tests/gmockvolume.h @@ -38,14 +38,26 @@ typedef struct _GMockVolumeClass GMockVolumeClass;  struct _GMockVolume {  GObject parent; + + gboolean can_eject; + char *name; + GIcon *icon; + char *uuid; + GMount *mount;  };  struct _GMockVolumeClass {  GObjectClass parent_class;  }; -GType g_mock_volume_get_type (void) G_GNUC_CONST; -GMockVolume * g_mock_volume_new (); +GType g_mock_volume_get_type (void) G_GNUC_CONST; +GMockVolume * g_mock_volume_new (); + +void g_mock_volume_set_can_eject (GMockVolume* volume, gboolean can_eject); +void g_mock_volume_set_name (GMockVolume *volume, const char *name); +void g_mock_volume_set_icon (GMockVolume *volume, GIcon *icon); +void g_mock_volume_set_uuid (GMockVolume *volume, const char *uuid); +void g_mock_volume_set_mount (GMockVolume *volume, GMount *mount);  G_END_DECLS diff --git a/tests/test_device_launcher_section.cpp b/tests/test_device_launcher_section.cpp index b00bbfc72..055a1156e 100644 --- a/tests/test_device_launcher_section.cpp +++ b/tests/test_device_launcher_section.cpp @@ -24,6 +24,7 @@  using namespace testing;  #include "DeviceLauncherSection.h" +#include "DevicesSettings.h"  #include "AbstractVolumeMonitorWrapper.h"  using namespace unity;  using namespace unity::launcher; @@ -74,12 +75,20 @@ public:  glib::Object<GVolume> volume2;  }; +class MockDevicesSettings : public DevicesSettings +{ + 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) - , section_(monitor_) + , devices_settings_(new MockDevicesSettings) + , section_(monitor_, devices_settings_)  {}  void SetUp() @@ -89,6 +98,7 @@ public:  }  MockVolumeMonitorWrapper::Ptr monitor_; + DevicesSettings::Ptr devices_settings_;  DeviceLauncherSection section_;  }; diff --git a/tests/test_edge_barrier_controller.cpp b/tests/test_edge_barrier_controller.cpp index f00ac2bf1..3ea731e30 100644 --- a/tests/test_edge_barrier_controller.cpp +++ b/tests/test_edge_barrier_controller.cpp @@ -156,9 +156,9 @@ TEST_F(TestEdgeBarrierController, ProcessHandledEventOnReleasedBarrier)  bc.Subscribe(&handling_subscriber, monitor);  MockPointerBarrier owner(monitor, true); - auto breaking_barrier_event = MakeBarrierEvent(0, true); + auto breaking_barrier_event = MakeBarrierEvent(5, true); - EXPECT_CALL(owner, ReleaseBarrier(_)).Times(0); + EXPECT_CALL(owner, ReleaseBarrier(5)).Times(1);  bc.ProcessBarrierEvent(&owner, breaking_barrier_event);  } @@ -239,7 +239,7 @@ TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForNotHand  bc.ProcessBarrierEvent(&owner, MakeBarrierEvent(6, false));  } -TEST_F(TestEdgeBarrierController, BreakingEdgeDontReleasesBarrierForHandledEvents) +TEST_F(TestEdgeBarrierController, BreakingEdgeTemporaryReleasesBarrierForHandledEvents)  {  MockPointerBarrier owner;  int monitor = 0; @@ -250,7 +250,7 @@ TEST_F(TestEdgeBarrierController, BreakingEdgeDontReleasesBarrierForHandledEvent  ASSERT_TRUE(owner.released());  subscribers_[monitor].handles_ = true; - EXPECT_CALL(owner, ReleaseBarrier(_)).Times(0); + EXPECT_CALL(owner, ReleaseBarrier(6)).Times(1);  bc.ProcessBarrierEvent(&owner, MakeBarrierEvent(6, true));  } diff --git a/tests/test_glib_source.cpp b/tests/test_glib_source.cpp index a654fa8fa..54718c91e 100644 --- a/tests/test_glib_source.cpp +++ b/tests/test_glib_source.cpp @@ -247,11 +247,14 @@ TEST(TestGLibTimeout, RemovePtrOnCallback)  unsigned int local_callback_call_count = 0;  Source::UniquePtr timeout(new Timeout(10, [&] { + unsigned int id = timeout->Id();  local_callback_called = true; - ++local_callback_call_count; - timeout.reset(); + local_callback_call_count++; - // this function would be called more than once if we had not removed the source. + timeout.reset(); + // resetting the ptr should destroy the source. + EXPECT_TRUE(g_main_context_find_source_by_id(NULL, id) == nullptr); + // this function would be called more than once (local_callback_call_count > 1) if we had not removed the source.  return true;  })); @@ -262,6 +265,31 @@ TEST(TestGLibTimeout, RemovePtrOnCallback)  EXPECT_EQ(local_callback_call_count, 1);  } +TEST(TestGLibTimeout, AutoRemoveSourceOnCallback) +{ + bool local_callback_called = false; + unsigned int local_callback_call_count = 0; + + Source::UniquePtr timeout(new Timeout(10, [&] { + local_callback_called = true; + ++local_callback_call_count; + + // return false to remove source. + return false; + })); + unsigned int id = timeout->Id(); + + Utils::WaitForTimeoutMSec(100); + + timeout.reset(); + EXPECT_EQ(local_callback_called, true); + EXPECT_EQ(local_callback_call_count, 1); + + // source should be removed by now. + EXPECT_TRUE(g_main_context_find_source_by_id(NULL, id) == nullptr); +} + +  // GLib TimeoutSeconds tests  TEST(TestGLibTimeoutSeconds, Construction) diff --git a/tests/test_home_lens.cpp b/tests/test_home_lens.cpp index 9e5105850..a1cfc57e7 100644 --- a/tests/test_home_lens.cpp +++ b/tests/test_home_lens.cpp @@ -18,6 +18,7 @@  #include "test_utils.h"  using namespace std; +using namespace unity;  using namespace unity::dash;  namespace @@ -61,6 +62,7 @@ public:  : Lens(id, "", "", name, "lens-icon.png",  description, search_hint, true, "",  ModelType::LOCAL) + , num_results_(-1)  {  search_in_global(true);  connected.SetGetterFunction(sigc::mem_fun(this, &StaticTestLens::force_connected)); @@ -101,28 +103,37 @@ public:  row_buf[1] = g_variant_new_string("");  row_buf[3] = g_variant_new_string(""); - row_buf[4] = g_variant_new_string("");  row_buf[5] = g_variant_new_string("");  row_buf[6] = g_variant_new_string("");  row_buf[7] = NULL;  unsigned int i; - for (i = 0; i < search_string.size(); i++) + unsigned int results_count = search_string.size(); + if (num_results_ >= 0) results_count = static_cast<unsigned>(num_results_); + for (i = 0; i < results_count; i++)  {  ostringstream uri; - uri << "uri+" << search_string.at(i) << "+" << id(); + char res_id(i >= search_string.size() ? '-' : search_string.at(i)); + uri << "uri+" << res_id << "+" << id();  row_buf[0] = g_variant_new_string(uri.str().c_str());  row_buf[2] = g_variant_new_uint32(i % 3); + glib::String name(g_strdup_printf("%s - %d", + results_base_name_.empty() ? + search_string.c_str() : results_base_name_.c_str(), + i)); + row_buf[4] = g_variant_new_string(name);  dee_model_append_row(model, row_buf);  }  g_free(row_buf); + + global_search_finished.emit(Hints());  }  void GlobalSearch(string const& search_string)  { - /* Dispatch search async, because that's */ + /* Dispatch search async, because that's what it'd normally do */  LensSearchClosure* closure = g_new0(LensSearchClosure, 1);  closure->lens = this;  closure->search_string = g_strdup(search_string.c_str()); @@ -144,6 +155,19 @@ public:  } + void SetResultsBaseName(string const& name) + { + results_base_name_ = name; + } + + void SetNumResults(int count) + { + num_results_ = count; + } + +private: + string results_base_name_; + int num_results_;  };  static gboolean dispatch_global_search(gpointer userdata) @@ -213,6 +237,25 @@ private:  Lens::Ptr lens_2_;  }; +class ThreeStaticTestLenses : public StaticTestLenses +{ +public: + ThreeStaticTestLenses() + : lens_1_(new StaticTestLens("first.lens", "First Lens", "The very first lens", "First search hint")) + , lens_2_(new StaticTestLens("second.lens", "Second Lens", "The second lens", "Second search hint")) + , lens_3_(new StaticTestLens("applications.lens", "Applications", "The applications lens", "Search applications")) + { + list_.push_back(lens_1_); + list_.push_back(lens_2_); + list_.push_back(lens_3_); + } + +private: + Lens::Ptr lens_1_; + Lens::Ptr lens_2_; + Lens::Ptr lens_3_; +}; +  TEST(TestHomeLens, TestConstruction)  {  HomeLens home_lens_("name", "description", "searchhint"); @@ -354,7 +397,13 @@ TEST(TestHomeLens, TestOneSearch)  home_lens_.Search("ape"); - Utils::WaitForTimeoutMSec(); + bool finished = false; + home_lens_.search_finished.connect([&finished] (Lens::Hints const& hints) + { + finished = true; + }); + + Utils::WaitUntil(finished);  /* Validate counts */  EXPECT_EQ(dee_model_get_n_rows(results), 6); // 3 hits from each lens @@ -388,4 +437,210 @@ TEST(TestHomeLens, TestOneSearch)  EXPECT_EQ(cat_shared, dee_model_get_uint32(results, iter, CAT_COLUMN));  } +TEST(TestHomeLens, TestOrderingAfterSearch) +{ + HomeLens home_lens_("name", "description", "searchhint", + HomeLens::MergeMode::OWNER_LENS); + ThreeStaticTestLenses lenses_; + DeeModel* results = home_lens_.results()->model(); + DeeModel* cats = home_lens_.categories()->model(); + DeeModel* filters = home_lens_.filters()->model(); + DeeModelIter* iter; + unsigned int lens1_cat = 0; + // the lens is added as third, so must have cat == 2 + unsigned int apps_lens_cat = 2; + const unsigned int URI_COLUMN = 0; + const unsigned int CAT_COLUMN = 2; + + home_lens_.AddLenses(lenses_); + + bool order_changed = false; + home_lens_.categories_reordered.connect([&order_changed] () + { + order_changed = true; + }); + + home_lens_.Search("ape"); + + bool finished = false; + home_lens_.search_finished.connect([&finished] (Lens::Hints const& hints) + { + finished = true; + }); + Utils::WaitUntil(finished); + + /* Validate counts */ + EXPECT_EQ(dee_model_get_n_rows(results), 9); // 3 hits from each lens + EXPECT_EQ(dee_model_get_n_rows(cats), 3); // 3 cats since we are merging categories by lens + EXPECT_EQ(dee_model_get_n_rows(filters), 0); // We ignore filters deliberately currently + + /* Validate the category order */ + auto order = home_lens_.GetCategoriesOrder(); + + /* The home lens includes applications lens which contains exact match, must + * be first category */ + EXPECT_EQ(order.at(0), apps_lens_cat); + + /* Plus the categories reordered should have been emitted */ + EXPECT_EQ(order_changed, true); + + /* The model will not be sorted acording to the categories though. */ + iter = dee_model_get_iter_at_row(results, 0); + EXPECT_EQ(string("uri+a+first.lens"), string(dee_model_get_string(results, iter, URI_COLUMN))); + EXPECT_EQ( dee_model_get_uint32(results, iter, CAT_COLUMN), lens1_cat); +} + +TEST(TestHomeLens, TestOrderingWithExactAppsMatch) +{ + HomeLens home_lens_("name", "description", "searchhint", + HomeLens::MergeMode::OWNER_LENS); + ThreeStaticTestLenses lenses_; + // the lens is added as third, so must have cat == 2 + unsigned int apps_lens_cat = 2; + + home_lens_.AddLenses(lenses_); + Lens::Ptr apps_lens = lenses_.GetLens("applications.lens"); + + auto static_lens = dynamic_pointer_cast<StaticTestLens>(apps_lens); + static_lens->SetNumResults(1); + + bool order_changed = false; + home_lens_.categories_reordered.connect([&order_changed] () + { + order_changed = true; + }); + + home_lens_.Search("ape"); + + bool finished = false; + home_lens_.search_finished.connect([&finished] (Lens::Hints const& hints) + { + finished = true; + }); + Utils::WaitUntil(finished); + + /* Validate counts */ + EXPECT_EQ(home_lens_.results()->count(), 7); // 3+3+1 hits + EXPECT_EQ(home_lens_.categories()->count(), 3); // 3 cats since we are merging categories by lens + EXPECT_EQ(home_lens_.filters()->count(), 0); // We ignore filters deliberately currently + + /* Validate the category order */ + auto order = home_lens_.GetCategoriesOrder(); + + /* The home lens includes applications lens and it contains exact match, + * so must be the first category, even though there are fewer results than + * in the other categories */ + EXPECT_EQ(order.at(0), apps_lens_cat); + + /* Plus the categories reordered should have been emitted */ + EXPECT_EQ(order_changed, true); +} + +TEST(TestHomeLens, TestOrderingWithoutExactAppsMatch) +{ + HomeLens home_lens_("name", "description", "searchhint", + HomeLens::MergeMode::OWNER_LENS); + ThreeStaticTestLenses lenses_; + // the lens is added as third, so must have cat == 2 + unsigned int apps_lens_cat = 2; + + home_lens_.AddLenses(lenses_); + Lens::Ptr apps_lens = lenses_.GetLens("applications.lens"); + + auto static_lens = dynamic_pointer_cast<StaticTestLens>(apps_lens); + static_lens->SetResultsBaseName("noapes"); + static_lens->SetNumResults(1); + + bool order_changed = false; + home_lens_.categories_reordered.connect([&order_changed] () + { + order_changed = true; + }); + + home_lens_.Search("ape"); + + bool finished = false; + home_lens_.search_finished.connect([&finished] (Lens::Hints const& hints) + { + finished = true; + }); + Utils::WaitUntil(finished); + + /* Validate counts */ + EXPECT_EQ(home_lens_.results()->count(), 7); // 3+3+1 hits + EXPECT_EQ(home_lens_.categories()->count(), 3); // 3 cats since we are merging categories by lens + EXPECT_EQ(home_lens_.filters()->count(), 0); // We ignore filters deliberately currently + + /* Validate the category order */ + auto order = home_lens_.GetCategoriesOrder(); + + /* The home lens includes applications lens but it doesn't contain exact + * match, so can't be the first category */ + EXPECT_NE(order.at(0), apps_lens_cat); + + /* Plus the categories reordered should have been emitted */ + EXPECT_EQ(order_changed, true); +} + +TEST(TestHomeLens, TestOrderingByNumResults) +{ + HomeLens home_lens_("name", "description", "searchhint", + HomeLens::MergeMode::OWNER_LENS); + ThreeStaticTestLenses lenses_; + unsigned int lens1_cat = 0; + unsigned int lens2_cat = 1; + // the lens is added as third, so must have cat == 2 + unsigned int apps_lens_cat = 2; + + home_lens_.AddLenses(lenses_); + + Lens::Ptr lens = lenses_.GetLensAtIndex(2); + auto static_lens = dynamic_pointer_cast<StaticTestLens>(lens); + static_lens->SetResultsBaseName("noapes"); // no exact match in apps lens + static_lens->SetNumResults(2); + + static_lens = dynamic_pointer_cast<StaticTestLens>(lenses_.GetLensAtIndex(0)); + static_lens->SetNumResults(1); + static_lens = dynamic_pointer_cast<StaticTestLens>(lenses_.GetLensAtIndex(1)); + static_lens->SetNumResults(3); + + bool order_changed = false; + home_lens_.categories_reordered.connect([&order_changed] () + { + order_changed = true; + }); + + home_lens_.Search("ape"); + + bool finished = false; + home_lens_.search_finished.connect([&finished] (Lens::Hints const& hints) + { + finished = true; + }); + Utils::WaitUntil(finished); + + /* + * lens1 -> 1 result + * lens2 -> 3 results + * lens3 -> 2 results (apps.lens) + */ + + /* Validate counts */ + EXPECT_EQ(home_lens_.results()->count(), 6); // 1+3+2 hits + EXPECT_EQ(home_lens_.categories()->count(), 3); // 3 cats since we are merging categories by lens + EXPECT_EQ(home_lens_.filters()->count(), 0); // We ignore filters deliberately currently + + /* Validate the category order */ + auto order = home_lens_.GetCategoriesOrder(); + + /* The home lens includes applications lens but it doesn't contain exact + * match, so can't be the first category */ + EXPECT_EQ(order.at(0), lens2_cat); + EXPECT_EQ(order.at(1), apps_lens_cat); + EXPECT_EQ(order.at(2), lens1_cat); + + /* Plus the categories reordered should have been emitted */ + EXPECT_EQ(order_changed, true); +} +  } diff --git a/tests/test_launcher.cpp b/tests/test_launcher.cpp index b6f6e22ad..ae8f7bb4e 100644 --- a/tests/test_launcher.cpp +++ b/tests/test_launcher.cpp @@ -15,10 +15,12 @@  * along with this program. If not, see <http://www.gnu.org/licenses/>.  *  * Authored by: Andrea Azzarone <andrea.azzarone@canonical.com> + * Marco Trevisan <marco.trevisan@canonical.com>  */  #include <list>  #include <gmock/gmock.h> +#include "test_uscreen_mock.h"  using namespace testing;  #include <Nux/Nux.h> @@ -29,6 +31,7 @@ using namespace testing;  #include "launcher/Launcher.h"  #include "unity-shared/PanelStyle.h"  #include "unity-shared/UnitySettings.h" +#include "unity-shared/IconRenderer.h"  #include "test_utils.h"  using namespace unity; @@ -54,29 +57,76 @@ public:  class TestLauncher : public Test  {  public: + class MockLauncher : public launcher::Launcher + { + public: + MockLauncher(nux::BaseWindow* parent, nux::ObjectPtr<DNDCollectionWindow> const& collection_window) + : Launcher(parent, collection_window) + {} + + AbstractLauncherIcon::Ptr MouseIconIntersection(int x, int y) + { + for (auto const& icon : *_model) + { + auto const& center = icon->GetCenter(monitor()); + + if (y > center.y - GetIconSize()/2.0f && y < center.y + GetIconSize()/2.0f) + return icon; + } + + return AbstractLauncherIcon::Ptr(); + } + + float IconBackgroundIntensity(AbstractLauncherIcon::Ptr icon, timespec const& current) const + { + return Launcher::IconBackgroundIntensity(icon, current); + } + + void StartIconDrag(AbstractLauncherIcon::Ptr icon) + { + Launcher::StartIconDrag(icon); + } + + void ShowDragWindow() + { + Launcher::ShowDragWindow(); + } + + void UpdateDragWindowPosition(int x, int y) + { + Launcher::UpdateDragWindowPosition(x, y); + } + + void HideDragWindow() + { + Launcher::HideDragWindow(); + } + + void ResetMouseDragState() + { + Launcher::ResetMouseDragState(); + } + }; +  TestLauncher()  : parent_window_(new nux::BaseWindow("TestLauncherWindow"))  , dnd_collection_window_(new DNDCollectionWindow)  , model_(new LauncherModel)  , options_(new Options) - , launcher_(new Launcher(parent_window_, dnd_collection_window_)) + , launcher_(new MockLauncher(parent_window_, dnd_collection_window_))  {  launcher_->options = options_;  launcher_->SetModel(model_);  } - float IconBackgroundIntensity(AbstractLauncherIcon::Ptr icon, timespec const& current) const - { - return launcher_->IconBackgroundIntensity(icon, current); - } - + MockUScreen uscreen;  nux::BaseWindow* parent_window_;  nux::ObjectPtr<DNDCollectionWindow> dnd_collection_window_;  Settings settings;  panel::Style panel_style;  LauncherModel::Ptr model_;  Options::Ptr options_; - nux::ObjectPtr<Launcher> launcher_; + nux::ObjectPtr<MockLauncher> launcher_;  };  TEST_F(TestLauncher, TestQuirksDuringDnd) @@ -149,10 +199,66 @@ TEST_F(TestLauncher, TestIconBackgroundIntensity)  timespec current;  clock_gettime(CLOCK_MONOTONIC, ¤t); - EXPECT_THAT(IconBackgroundIntensity(first, current), Gt(0.0f)); - EXPECT_THAT(IconBackgroundIntensity(second, current), Gt(0.0f)); - EXPECT_EQ(IconBackgroundIntensity(third, current), 0.0f); + EXPECT_THAT(launcher_->IconBackgroundIntensity(first, current), Gt(0.0f)); + EXPECT_THAT(launcher_->IconBackgroundIntensity(second, current), Gt(0.0f)); + EXPECT_EQ(launcher_->IconBackgroundIntensity(third, current), 0.0f);  } +TEST_F(TestLauncher, DragLauncherIconCancelRestoresIconOrder) +{ + MockMockLauncherIcon::Ptr icon1(new MockMockLauncherIcon); + MockMockLauncherIcon::Ptr icon2(new MockMockLauncherIcon); + MockMockLauncherIcon::Ptr icon3(new MockMockLauncherIcon); + + 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()); + + // Start dragging icon2 + launcher_->StartIconDrag(icon2); + launcher_->ShowDragWindow(); + + // Moving icon2 at the end + auto const& center3 = icon3->GetCenter(launcher_->monitor()); + launcher_->UpdateDragWindowPosition(center3.x, center3.y); + + auto it = model_->begin(); + ASSERT_EQ(*it, icon1); it++; + ASSERT_EQ(*it, icon3); it++; + ASSERT_EQ(*it, icon2); + + // Moving icon2 at the begin + auto const& center1 = icon1->GetCenter(launcher_->monitor()); + launcher_->UpdateDragWindowPosition(center1.x, center1.y); + + it = model_->begin(); + ASSERT_EQ(*it, icon2); it++; + ASSERT_EQ(*it, icon1); it++; + ASSERT_EQ(*it, icon3); + + bool model_saved = false; + model_->saved.connect([&model_saved] { model_saved = true; }); + + // Emitting the drag cancel request + launcher_->GetDraggedIcon()->drag_cancel_request.emit(); + + // The icon order should be reset + it = model_->begin(); + ASSERT_EQ(*it, icon1); it++; + ASSERT_EQ(*it, icon2); it++; + ASSERT_EQ(*it, icon3); + + EXPECT_FALSE(model_saved); + + launcher_->HideDragWindow();  } +  } +} + diff --git a/tests/test_launcher_controller.cpp b/tests/test_launcher_controller.cpp index 16fbbf5ee..a4d5fc660 100644 --- a/tests/test_launcher_controller.cpp +++ b/tests/test_launcher_controller.cpp @@ -52,7 +52,6 @@ private:  class MockBamfLauncherIcon : public BamfLauncherIcon  {  public: - //typedef nux::ObjectPtr<MockMockLauncherIcon> Ptr;  MockBamfLauncherIcon(BamfApplication* app)  : BamfLauncherIcon(app) {} @@ -71,7 +70,6 @@ public:  virtual void SetUp()  { - lc.options = std::make_shared<Options>();  lc.multiple_launchers = true;  } @@ -98,6 +96,8 @@ TEST_F(TestLauncherController, Construction)  {  EXPECT_NE(lc.options(), nullptr);  EXPECT_TRUE(lc.multiple_launchers()); + ASSERT_EQ(lc.launchers().size(), 1); + EXPECT_EQ(lc.launcher().monitor(), 0);  }  TEST_F(TestLauncherController, MultimonitorMultipleLaunchers) @@ -230,4 +230,65 @@ TEST_F(TestLauncherController, OnlyUnstickIconOnFavoriteRemoval)  favorite_store.favorite_removed.emit(USC_DESKTOP);  } +TEST_F(TestLauncherController, EnabledStrutsByDefault) +{ + EXPECT_EQ(lc.launcher().options()->hide_mode, LAUNCHER_HIDE_NEVER); + EXPECT_TRUE(lc.launcher().GetParent()->InputWindowStrutsEnabled()); +} + +TEST_F(TestLauncherController, EnabledStrutsOnNeverHide) +{ + lc.multiple_launchers = true; + uscreen.SetupFakeMultiMonitor(); + lc.options()->hide_mode = LAUNCHER_HIDE_NEVER; + + for (int i = 0; i < max_num_monitors; ++i) + ASSERT_TRUE(lc.launchers()[i]->GetParent()->InputWindowStrutsEnabled()); +} + +TEST_F(TestLauncherController, DisabledStrutsOnAutoHide) +{ + lc.multiple_launchers = true; + uscreen.SetupFakeMultiMonitor(); + lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; + + for (int i = 0; i < max_num_monitors; ++i) + ASSERT_FALSE(lc.launchers()[i]->GetParent()->InputWindowStrutsEnabled()); +} + +TEST_F(TestLauncherController, EnabledStrutsAddingNewLaunchersOnAutoHide) +{ + // This makes the controller to add multiple launchers + lc.multiple_launchers = true; + lc.options()->hide_mode = LAUNCHER_HIDE_NEVER; + uscreen.SetupFakeMultiMonitor(); + + // This makes the controller to remove unneeded launchers + lc.multiple_launchers = false; + + // This makes the controller to add again new launchers + lc.multiple_launchers = true; + + for (int i = 0; i < max_num_monitors; ++i) + ASSERT_TRUE(lc.launchers()[i]->GetParent()->InputWindowStrutsEnabled()); +} + +TEST_F(TestLauncherController, DisabledStrutsAddingNewLaunchersOnNeverHide) +{ + // This makes the controller to add multiple launchers + lc.multiple_launchers = true; + lc.options()->hide_mode = LAUNCHER_HIDE_AUTOHIDE; + uscreen.SetupFakeMultiMonitor(); + + // This makes the controller to remove unneeded launchers + lc.multiple_launchers = false; + + // This makes the controller to add again new launchers + lc.multiple_launchers = true; + + for (int i = 0; i < max_num_monitors; ++i) + ASSERT_FALSE(lc.launchers()[i]->GetParent()->InputWindowStrutsEnabled());  } + +} + diff --git a/tests/test_launcher_drag_window.cpp b/tests/test_launcher_drag_window.cpp new file mode 100644 index 000000000..2a5186d18 --- /dev/null +++ b/tests/test_launcher_drag_window.cpp @@ -0,0 +1,91 @@ +/* + * 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 "LauncherDragWindow.h" +#include "WindowManager.h" + +using namespace unity::launcher; +using namespace testing; + +namespace +{ +const int ICON_WIDTH = 10; +const int ICON_HEIGHT = 15; +} + +namespace unity +{ +namespace launcher +{ +struct TestLauncherDragWindow : public testing::Test +{ + TestLauncherDragWindow() + : drag_window(nux::ObjectPtr<nux::IOpenGLBaseTexture>(new nux::IOpenGLBaseTexture(nux::RTTEXTURE, ICON_WIDTH, ICON_HEIGHT, 24, 1, nux::BITFMT_B8G8R8A8))) + {} + + LauncherDragWindow drag_window; +}; +} + +TEST_F(TestLauncherDragWindow, Construction) +{ + EXPECT_EQ(drag_window.GetBaseWidth(), ICON_WIDTH); + EXPECT_EQ(drag_window.GetBaseHeight(), ICON_HEIGHT); + EXPECT_FALSE(drag_window.Animating()); + EXPECT_FALSE(drag_window.Cancelled()); +} + +TEST_F(TestLauncherDragWindow, EscapeRequestsCancellation) +{ + nux::Event cancel; + cancel.type = nux::NUX_KEYDOWN; + cancel.x11_keysym = NUX_VK_ESCAPE; + bool got_signal; + + drag_window.drag_cancel_request.connect([&got_signal] { got_signal = true; }); + drag_window.GrabKeyboard(); + nux::GetWindowCompositor().ProcessEvent(cancel); + + EXPECT_TRUE(got_signal); + EXPECT_TRUE(drag_window.Cancelled()); +} + +TEST_F(TestLauncherDragWindow, CancelsOnWindowMapped) +{ + bool got_signal; + drag_window.drag_cancel_request.connect([&got_signal] { got_signal = true; }); + WindowManager::Default()->window_mapped.emit(0); + + EXPECT_TRUE(got_signal); + EXPECT_TRUE(drag_window.Cancelled()); +} + +TEST_F(TestLauncherDragWindow, CancelsOnWindowUnmapped) +{ + bool got_signal; + drag_window.drag_cancel_request.connect([&got_signal] { got_signal = true; }); + WindowManager::Default()->window_unmapped.emit(0); + + EXPECT_TRUE(got_signal); + EXPECT_TRUE(drag_window.Cancelled()); +} + +} diff --git a/tests/test_launcher_minimize_speed.cpp b/tests/test_launcher_minimize_speed.cpp new file mode 100644 index 000000000..a24e3b90d --- /dev/null +++ b/tests/test_launcher_minimize_speed.cpp @@ -0,0 +1,123 @@ +/* + * 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: Ugo Riboni <ugo.riboni@canonical.com> + * + */ + +#include <config.h> + +#include <gio/gio.h> +#include <gtest/gtest.h> + +#include "plugins/unityshell/src/WindowMinimizeSpeedController.h" + +using namespace unity; +using namespace testing; + +namespace +{ + +const gchar* SCHEMA_DIRECTORY = BUILDDIR"/settings"; + +class TestLauncherMinimizeSpeed : public Test +{ +public: + glib::Object<GSettings> mSettings; + WindowMinimizeSpeedController* mController; +  + /* override */ void SetUp() + { + g_setenv("GSETTINGS_SCHEMA_DIR", SCHEMA_DIRECTORY, true); + g_setenv("GSETTINGS_BACKEND", "memory", TRUE); + mSettings = g_settings_new("com.canonical.Unity"); + mController = new WindowMinimizeSpeedController(); + } + + /* override */ void TearDown() + { + g_setenv("GSETTINGS_SCHEMA_DIR", "", true); + g_unsetenv("GSETTINGS_BACKEND"); + delete mController; + } +}; + +TEST_F(TestLauncherMinimizeSpeed, TestSlowest) +{ + g_settings_set_int(mSettings, "minimize-count", 0); + g_settings_set_int(mSettings, "minimize-speed-threshold", 10); + g_settings_set_int(mSettings, "minimize-fast-duration", 200); + g_settings_set_int(mSettings, "minimize-slow-duration", 1200); +  + EXPECT_TRUE(mController->getDuration() == 1200); +} + +TEST_F(TestLauncherMinimizeSpeed, TestFastest) +{ + g_settings_set_int(mSettings, "minimize-count", 10); + g_settings_set_int(mSettings, "minimize-speed-threshold", 10); + g_settings_set_int(mSettings, "minimize-fast-duration", 200); + g_settings_set_int(mSettings, "minimize-slow-duration", 1200); +  + EXPECT_TRUE(mController->getDuration() == 200); +} + +TEST_F(TestLauncherMinimizeSpeed, TestHalfway) +{ + g_settings_set_int(mSettings, "minimize-count", 5); + g_settings_set_int(mSettings, "minimize-speed-threshold", 10); + g_settings_set_int(mSettings, "minimize-fast-duration", 200); + g_settings_set_int(mSettings, "minimize-slow-duration", 1200); +  + EXPECT_TRUE(mController->getDuration() == 700); +} + +TEST_F(TestLauncherMinimizeSpeed, TestOvershoot) +{ + g_settings_set_int(mSettings, "minimize-count", 20); + g_settings_set_int(mSettings, "minimize-speed-threshold", 10); + g_settings_set_int(mSettings, "minimize-fast-duration", 200); + g_settings_set_int(mSettings, "minimize-slow-duration", 1200); +  + EXPECT_TRUE(mController->getDuration() == 200); +} + +TEST_F(TestLauncherMinimizeSpeed, TestSignal) +{ + + bool signal_emitted = false; + mController->DurationChanged.connect([&] () { + signal_emitted = true; + }); +  + g_settings_set_int(mSettings, "minimize-count", 5); + EXPECT_TRUE(signal_emitted); +} + +TEST_F(TestLauncherMinimizeSpeed, TestInvalidFastSlow) +{ + g_settings_set_int(mSettings, "minimize-fast-duration", 2000); + g_settings_set_int(mSettings, "minimize-slow-duration", 100); + + bool signal_emitted = false; + mController->DurationChanged.connect([&] () { + signal_emitted = true; + }); +  + g_settings_set_int(mSettings, "minimize-count", 5); + EXPECT_FALSE(signal_emitted); +} +} \ No newline at end of file diff --git a/tests/test_launcher_model.cpp b/tests/test_launcher_model.cpp index 476c37fb7..a56c929cf 100644 --- a/tests/test_launcher_model.cpp +++ b/tests/test_launcher_model.cpp @@ -1,5 +1,5 @@  /* - * Copyright 2011 Canonical Ltd. + * Copyright 2011-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 @@ -15,7 +15,7 @@  * <http://www.gnu.org/licenses/>  *  * Authored by: Jason Smith <jason.smith@canonical.com> - * + * Marco Trevisan <marco.trevisan@canonical.com>  */  #include <gtest/gtest.h> @@ -32,185 +32,308 @@ using namespace unity::launcher;  namespace  { -class EventListener +class TestLauncherModel : public testing::Test  { - public: - EventListener() - { - icon_added = false; - icon_removed = false; - } - - void OnIconAdded (AbstractLauncherIcon::Ptr icon) - { - icon_added = true; - } - - void OnIconRemoved (AbstractLauncherIcon::Ptr icon) - { - icon_removed = true; - } - - bool icon_added; - bool icon_removed;  +public: + TestLauncherModel() + : icon1(new MockLauncherIcon()) + , icon2(new MockLauncherIcon()) + , icon3(new MockLauncherIcon()) + , icon4(new MockLauncherIcon()) + {} + + AbstractLauncherIcon::Ptr icon1; + AbstractLauncherIcon::Ptr icon2; + AbstractLauncherIcon::Ptr icon3; + AbstractLauncherIcon::Ptr icon4; + + LauncherModel model;  }; -//bool seen_result; -TEST(TestLauncherModel, TestConstructor) +TEST_F(TestLauncherModel, Constructor)  { - LauncherModel::Ptr model(new LauncherModel()); - EXPECT_EQ(model->Size(), 0); + EXPECT_EQ(model.Size(), 0);  } -TEST(TestLauncherModel, TestAdd) +TEST_F(TestLauncherModel, Add)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - LauncherModel::Ptr model(new LauncherModel()); + model.AddIcon(icon1); + EXPECT_EQ(model.Size(), 1); - EXPECT_EQ(model->Size(), 0); - model->AddIcon(first); - EXPECT_EQ(model->Size(), 1); + model.AddIcon(icon1); + EXPECT_EQ(model.Size(), 1); + + model.AddIcon(AbstractLauncherIcon::Ptr()); + EXPECT_EQ(model.Size(), 1);  } -TEST(TestLauncherModel, TestRemove) +TEST_F(TestLauncherModel, Remove)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - LauncherModel::Ptr model(new LauncherModel()); + model.AddIcon(icon1); + EXPECT_EQ(model.Size(), 1); - EXPECT_EQ(model->Size(), 0); - model->AddIcon(first); - EXPECT_EQ(model->Size(), 1); - model->RemoveIcon(first); - EXPECT_EQ(model->Size(), 0); + model.RemoveIcon(icon1); + EXPECT_EQ(model.Size(), 0);  } -TEST(TestLauncherModel, TestAddSignal) +TEST_F(TestLauncherModel, AddSignal)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - LauncherModel::Ptr model(new LauncherModel()); + bool icon_added = false; + model.icon_added.connect([&icon_added] (AbstractLauncherIcon::Ptr) { icon_added = true; }); - EventListener *listener = new EventListener(); + model.AddIcon(icon1); + EXPECT_TRUE(icon_added); - model->icon_added.connect(sigc::mem_fun(listener, &EventListener::OnIconAdded)); - model->AddIcon(first); - EXPECT_EQ(listener->icon_added, true); + icon_added = false; + model.AddIcon(icon1); + EXPECT_FALSE(icon_added); - delete listener; + icon_added = false; + model.AddIcon(AbstractLauncherIcon::Ptr()); + EXPECT_FALSE(icon_added);  } -TEST(TestLauncherModel, TestRemoveSignal) +TEST_F(TestLauncherModel, RemoveSignal)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - LauncherModel::Ptr model(new LauncherModel()); - - EventListener *listener = new EventListener(); - - model->icon_removed.connect(sigc::mem_fun(listener, &EventListener::OnIconRemoved)); - model->AddIcon(first); - EXPECT_EQ(listener->icon_removed, false); - model->RemoveIcon(first); - EXPECT_EQ(listener->icon_removed, true); + bool icon_removed = false; + model.icon_removed.connect([&icon_removed] (AbstractLauncherIcon::Ptr) { icon_removed = true; }); - delete listener; + model.AddIcon(icon1); + EXPECT_FALSE(icon_removed); + model.RemoveIcon(icon1); + EXPECT_TRUE(icon_removed);  } -TEST(TestLauncherModel, TestSort) +TEST_F(TestLauncherModel, Sort)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr second(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr third(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr fourth(new MockLauncherIcon()); + icon2->SetSortPriority(0); + model.AddIcon(icon2); - LauncherModel::Ptr model(new LauncherModel()); + icon1->SetSortPriority(-1); + model.AddIcon(icon1); - third->SetSortPriority(0); - model->AddIcon(third); + icon4->SetSortPriority(2); + model.AddIcon(icon4); - first->SetSortPriority(-1); - model->AddIcon(first); - - fourth->SetSortPriority(2); - model->AddIcon(fourth); - - second->SetSortPriority(0); - model->AddIcon(second); + icon3->SetSortPriority(0); + model.AddIcon(icon3);  LauncherModel::iterator it; - it = model->begin(); + it = model.begin(); - EXPECT_EQ(first, *it); + EXPECT_EQ(icon1, *it);  it++; - EXPECT_EQ(second, *it); + EXPECT_EQ(icon2, *it);  it++; - EXPECT_EQ(third, *it); + EXPECT_EQ(icon3, *it);  it++; - EXPECT_EQ(fourth, *it); + EXPECT_EQ(icon4, *it);  } -TEST(TestLauncherModel, TestReorderBefore) +TEST_F(TestLauncherModel, ModelKeepsPriorityDeltas)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr second(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr third(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr fourth(new MockLauncherIcon()); + icon2->SetSortPriority(0); + icon1->SetSortPriority(-1); + icon4->SetSortPriority(2); + icon3->SetSortPriority(0); + + model.AddIcon(icon2); + model.AddIcon(icon1); + model.AddIcon(icon4); + model.AddIcon(icon3); + + auto it = model.begin(); + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon4, *it); - LauncherModel::Ptr model(new LauncherModel()); + EXPECT_GT(icon2->SortPriority(), icon1->SortPriority()); + EXPECT_EQ(icon2->SortPriority(), icon3->SortPriority()); + EXPECT_LT(icon3->SortPriority(), icon4->SortPriority()); +} - first->SetSortPriority(0); - second->SetSortPriority(1); - third->SetSortPriority(2); - fourth->SetSortPriority(3); +TEST_F(TestLauncherModel, ReorderBefore) +{ + icon1->SetSortPriority(0); + icon2->SetSortPriority(1); + icon3->SetSortPriority(2); + icon4->SetSortPriority(3); - model->AddIcon(first); - model->AddIcon(second); - model->AddIcon(third); - model->AddIcon(fourth); + model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); - model->ReorderBefore(third, second, false); + model.ReorderBefore(icon3, icon2, false);  LauncherModel::iterator it; - it = model->begin(); + it = model.begin(); - EXPECT_EQ(first, *it); + EXPECT_EQ(icon1, *it);  it++; - EXPECT_EQ(third, *it); + EXPECT_EQ(icon3, *it);  it++; - EXPECT_EQ(second, *it); + EXPECT_EQ(icon2, *it);  it++; - EXPECT_EQ(fourth, *it); + EXPECT_EQ(icon4, *it);  } -TEST(TestLauncherModel, TestReorderSmart) +TEST_F(TestLauncherModel, ReorderAfter)  { - AbstractLauncherIcon::Ptr first(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr second(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr third(new MockLauncherIcon()); - AbstractLauncherIcon::Ptr fourth(new MockLauncherIcon()); + model.AddIcon(icon1); + model.AddIcon(icon3); + model.AddIcon(icon2); + model.AddIcon(icon4); + + model.ReorderAfter(icon3, icon2); + + LauncherModel::iterator it; + it = model.begin(); - LauncherModel::Ptr model(new LauncherModel()); + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon4, *it); +} - first->SetSortPriority(0); - second->SetSortPriority(1); - third->SetSortPriority(2); - fourth->SetSortPriority(3); +TEST_F(TestLauncherModel, ReorderSmart) +{ + icon1->SetSortPriority(0); + icon2->SetSortPriority(1); + icon3->SetSortPriority(2); + icon4->SetSortPriority(3); - model->AddIcon(first); - model->AddIcon(second); - model->AddIcon(third); - model->AddIcon(fourth); + model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); - model->ReorderSmart(third, second, false); + model.ReorderSmart(icon3, icon2, false);  LauncherModel::iterator it; - it = model->begin(); + it = model.begin(); + + EXPECT_EQ(icon1, *it); + it++; + EXPECT_EQ(icon3, *it); + it++; + EXPECT_EQ(icon2, *it); + it++; + EXPECT_EQ(icon4, *it); +} - EXPECT_EQ(first, *it); +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(third, *it); + EXPECT_EQ(icon3, *it);  it++; - EXPECT_EQ(second, *it); + EXPECT_EQ(icon4, *it);  it++; - EXPECT_EQ(fourth, *it); + EXPECT_EQ(icon5, *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); + it_main++; + EXPECT_EQ(it_main, model.main_end()); + + auto it_shelf = model.shelf_begin(); + EXPECT_EQ(icon5, *it_shelf); + it_shelf++; + EXPECT_EQ(it_shelf, model.shelf_end()); +} + +TEST_F(TestLauncherModel, GetClosestIcon) +{ + model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); + + bool before; + EXPECT_EQ(model.GetClosestIcon(icon1, before), icon2); + EXPECT_FALSE(before); + + EXPECT_EQ(model.GetClosestIcon(icon2, before), icon1); + EXPECT_TRUE(before); + + EXPECT_EQ(model.GetClosestIcon(icon3, before), icon2); + EXPECT_TRUE(before); + + EXPECT_EQ(model.GetClosestIcon(icon4, before), icon3); + EXPECT_TRUE(before); +} + +TEST_F(TestLauncherModel, GetClosestIconWithOneIcon) +{ + model.AddIcon(icon1); + + bool before; + EXPECT_EQ(model.GetClosestIcon(icon1, before), nullptr); + EXPECT_TRUE(before); +} + +TEST_F(TestLauncherModel, IconIndex) +{ + model.AddIcon(icon1); + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); + + EXPECT_EQ(model.IconIndex(icon1), 0); + EXPECT_EQ(model.IconIndex(icon2), 1); + EXPECT_EQ(model.IconIndex(icon3), 2); + EXPECT_EQ(model.IconIndex(icon4), 3); + + AbstractLauncherIcon::Ptr icon5(new MockLauncherIcon()); + EXPECT_EQ(model.IconIndex(icon5), -1); +} + +TEST_F(TestLauncherModel, IconHasSister) +{ + model.AddIcon(icon1); + EXPECT_FALSE(model.IconHasSister(icon1)); + + model.AddIcon(icon2); + model.AddIcon(icon3); + model.AddIcon(icon4); + + EXPECT_TRUE(model.IconHasSister(icon1)); + EXPECT_TRUE(model.IconHasSister(icon2)); + EXPECT_TRUE(model.IconHasSister(icon3)); + EXPECT_TRUE(model.IconHasSister(icon4)); + + EXPECT_FALSE(AbstractLauncherIcon::Ptr());  }  } diff --git a/tests/test_lens.cpp b/tests/test_lens.cpp index c0c2f678b..79decb031 100644 --- a/tests/test_lens.cpp +++ b/tests/test_lens.cpp @@ -273,6 +273,47 @@ TEST_F(TestLens, TestPreviewAction)  Utils::WaitUntil(action_executed);  } +TEST_F(TestLens, TestPreviewActionWithHints) +{ + std::string uri = PopulateAndGetFirstResultURI(); + bool previewed = false; + Preview::Ptr preview; + + auto preview_cb = [&previewed, &uri, &preview] + (std::string const& uri_, + Preview::Ptr const& preview_) + { + EXPECT_EQ(uri, uri_); + EXPECT_EQ(preview_->renderer_name, "preview-movie"); + + preview = preview_; + previewed = true; + }; + + lens_->preview_ready.connect(preview_cb); + lens_->Preview(uri); + + Utils::WaitUntil(previewed); + + bool action_executed = false; + auto activated_cb = [&action_executed] (std::string const& uri, + HandledType handled_type, + Lens::Hints const& hints) + { + EXPECT_EQ(handled_type, HandledType::SHOW_DASH); + action_executed = true; + }; + + lens_->activated.connect(activated_cb); + EXPECT_GT(preview->GetActions().size(), (unsigned)0); + auto action = preview->GetActions()[0]; + auto hints = Lens::Hints(); + hints["passing-test-hint"] = g_variant_new_boolean(TRUE); + preview->PerformAction(action->id, hints); + + Utils::WaitUntil(action_executed); +} +  TEST_F(TestLens, TestEmitClosedSignal)  {  std::string uri = PopulateAndGetFirstResultURI(); diff --git a/tests/test_volume_imp.cpp b/tests/test_volume_imp.cpp new file mode 100644 index 000000000..a6c8e444e --- /dev/null +++ b/tests/test_volume_imp.cpp @@ -0,0 +1,163 @@ +/* + * 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#include <memory> + +#include <gmock/gmock.h> +using namespace testing; + +#include "gmockmount.h" +#include "gmockvolume.h" +#include "launcher/VolumeImp.h" +#include "test_utils.h" +using namespace unity; + +namespace +{ + +class MockFileManagerOpener : public launcher::FileManagerOpener +{ +public: + typedef std::shared_ptr<MockFileManagerOpener> Ptr; + + MOCK_METHOD1(Open, void(std::string const& uri)); +}; + +class MockDeviceNotificationDisplay : public launcher::DeviceNotificationDisplay +{ +public: + typedef std::shared_ptr<MockDeviceNotificationDisplay> Ptr; + + MOCK_METHOD2(Display, void(std::string const& icon_name, std::string const& device_name)); +}; + +class TestVolumeImp : public Test +{ +public: + void SetUp() + { + gvolume_ = g_mock_volume_new(); + file_manager_opener_.reset(new MockFileManagerOpener); + device_notification_display_.reset(new MockDeviceNotificationDisplay); + volume_.reset(new launcher::VolumeImp(glib::Object<GVolume>(G_VOLUME(gvolume_.RawPtr()), glib::AddRef()), + file_manager_opener_, device_notification_display_)); + } + + glib::Object<GMockVolume> gvolume_; + MockFileManagerOpener::Ptr file_manager_opener_; + MockDeviceNotificationDisplay::Ptr device_notification_display_; + launcher::VolumeImp::Ptr volume_; +}; + +TEST_F(TestVolumeImp, TestCtor) +{ + EXPECT_FALSE(volume_->IsMounted()); +} + +TEST_F(TestVolumeImp, TestCanBeEjected) +{ + EXPECT_FALSE(volume_->CanBeEjected()); + + g_mock_volume_set_can_eject(gvolume_, TRUE); + EXPECT_TRUE(volume_->CanBeEjected()); +} + +TEST_F(TestVolumeImp, TestGetName) +{ + std::string const volume_name("Test Device"); + + // g_mock_volume_set_name is equivalent to + // EXPECT_CALL(gvolume_, g_volume_get_name) ... + g_mock_volume_set_name(gvolume_, volume_name.c_str()); + EXPECT_EQ(volume_->GetName(), volume_name); +} + +TEST_F(TestVolumeImp, TestGetIconName) +{ + std::string const icon_name("gnome-dev-cdrom"); + + g_mock_volume_set_icon(gvolume_, g_icon_new_for_string(icon_name.c_str(), NULL)); + EXPECT_EQ(volume_->GetIconName(), icon_name); +} + +TEST_F(TestVolumeImp, TestGetIdentifier) +{ + std::string const uuid("0123456789abc"); + + g_mock_volume_set_uuid(gvolume_, uuid.c_str()); + EXPECT_EQ(volume_->GetIdentifier(), uuid); +} + +TEST_F(TestVolumeImp, TestIsMounted) +{ + g_mock_volume_set_mount(gvolume_, nullptr); + ASSERT_FALSE(volume_->IsMounted()); + + g_mock_volume_set_mount(gvolume_, G_MOUNT(g_mock_mount_new())); + EXPECT_TRUE(volume_->IsMounted()); +} + +TEST_F(TestVolumeImp, TestEjectAndShowNotification) +{ + g_mock_volume_set_can_eject(gvolume_, TRUE); + + EXPECT_CALL(*device_notification_display_, Display(volume_->GetIconName(), volume_->GetName())) + .Times(1); + + volume_->EjectAndShowNotification(); +} + +TEST_F(TestVolumeImp, TestMountAndOpenInFileManager) +{ + EXPECT_CALL(*file_manager_opener_, Open(ROOT_FILE_URI)) + .Times(1); + + volume_->MountAndOpenInFileManager(); + EXPECT_TRUE(volume_->IsMounted()); + + EXPECT_CALL(*file_manager_opener_, Open(ROOT_FILE_URI)) + .Times(1); + + volume_->MountAndOpenInFileManager(); + EXPECT_TRUE(volume_->IsMounted()); +} + +TEST_F(TestVolumeImp, TestChangedSignal) +{ + bool callback_called = false; + volume_->changed.connect([&]() { + callback_called = true; + }); + + g_signal_emit_by_name(gvolume_, "changed", nullptr); + Utils::WaitUntil(callback_called); +} + +TEST_F(TestVolumeImp, TestRemovedSignal) +{ + bool callback_called = false; + volume_->removed.connect([&]() { + callback_called = true; + }); + + g_signal_emit_by_name(gvolume_, "removed", nullptr); + Utils::WaitUntil(callback_called); +} + +} diff --git a/tests/test_volume_launcher_icon.cpp b/tests/test_volume_launcher_icon.cpp new file mode 100644 index 000000000..287a9558c --- /dev/null +++ b/tests/test_volume_launcher_icon.cpp @@ -0,0 +1,498 @@ +/* + * 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: Andrea Azzarone <andrea.azzarone@canonical.com> + */ + +#include <gmock/gmock.h> +using namespace testing; + +#include "launcher/DevicesSettings.h" +#include "launcher/Volume.h" +#include "launcher/VolumeLauncherIcon.h" +#include "test_utils.h" +using namespace unity; +using namespace unity::launcher; + +namespace +{ + +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)); +}; + +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); + settings_.reset(new MockDevicesSettings); + + SetupVolumeDefaultBehavior(); + SetupSettingsDefaultBehavior(); + } + + void CreateIcon() + { + icon_ = new VolumeLauncherIcon(volume_, settings_); + } + + void SetupSettingsDefaultBehavior() + { + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(false)); + } + + void SetupVolumeDefaultBehavior() + { + EXPECT_CALL(*volume_, CanBeRemoved()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*volume_, CanBeStopped()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*volume_, GetName()) + .WillRepeatedly(Return("Test Name")); + + EXPECT_CALL(*volume_, GetIconName()) + .WillRepeatedly(Return("Test Icon Name")); + + EXPECT_CALL(*volume_, GetIdentifier()) + .WillRepeatedly(Return("Test Identifier")); + + EXPECT_CALL(*volume_, HasSiblings()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*volume_, CanBeEjected()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(true)); + } + + glib::Object<DbusmenuMenuitem> GetMenuItemAtIndex(int index) + { + auto menuitems = icon_->GetMenus(); + auto menuitem = menuitems.begin(); + std::advance(menuitem, index); + + return *menuitem; + }  + + MockVolume::Ptr volume_; + MockDevicesSettings::Ptr settings_; + VolumeLauncherIcon::Ptr icon_; +}; + +TEST_F(TestVolumeLauncherIcon, TestIconType) +{ + CreateIcon(); + EXPECT_EQ(icon_->GetIconType(), AbstractLauncherIcon::IconType::DEVICE); +} + +TEST_F(TestVolumeLauncherIcon, TestQuirks) +{ + CreateIcon(); + + EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::RUNNING)); +} + +TEST_F(TestVolumeLauncherIcon, TestTooltipText) +{ + CreateIcon(); + + ASSERT_EQ(icon_->tooltip_text, "Test Name"); +} + +TEST_F(TestVolumeLauncherIcon, TestIconName) +{ + CreateIcon(); + + ASSERT_EQ(icon_->icon_name, "Test Icon Name"); +} + +TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedVolume) +{ + CreateIcon(); + + EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + +TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyMountedBlacklistedVolume) +{ + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + ASSERT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + + +TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyUnmountedVolume) +{ + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(false)); + + CreateIcon(); + + EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + + +TEST_F(TestVolumeLauncherIcon, TestVisibility_InitiallyUnmountedBlacklistedVolume) +{ + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + +TEST_F(TestVolumeLauncherIcon, TestSettingsChangedSignal) +{ + CreateIcon(); + + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(true)); + settings_->changed.emit(); + + EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + +TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount) +{ + CreateIcon(); + + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*settings_, TryToBlacklist(_)) + .Times(0); + + volume_->changed.emit(); + Utils::WaitForTimeout(1); + + EXPECT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + +TEST_F(TestVolumeLauncherIcon, TestVisibilityAfterUnmount_BlacklistedVolume) +{ + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(false)); + + EXPECT_CALL(*settings_, TryToUnblacklist(_)) + .Times(0); + + volume_->changed.emit(); + Utils::WaitForTimeout(1); + + EXPECT_FALSE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + +TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_VolumeWithoutIdentifier) +{ + EXPECT_CALL(*volume_, GetIdentifier()) + .WillRepeatedly(Return("")); + + CreateIcon(); + + for (auto menuitem : icon_->GetMenus()) + ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher"); +} + +TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Success) +{ + CreateIcon(); + + auto menuitem = GetMenuItemAtIndex(0); + + ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher"); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); + + EXPECT_CALL(*settings_, TryToBlacklist(_)) + .Times(1); + + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(true)); + + 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)); +} + +TEST_F(TestVolumeLauncherIcon, TestUnlockFromLauncherMenuItem_Failure) +{ + CreateIcon(); + + auto menuitem = GetMenuItemAtIndex(0); + + ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unlock from Launcher"); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); + + EXPECT_CALL(*settings_, TryToBlacklist(_)) + .Times(1); + + dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); + Utils::WaitForTimeout(1); + + ASSERT_TRUE(icon_->GetQuirk(AbstractLauncherIcon::Quirk::VISIBLE)); +} + +TEST_F(TestVolumeLauncherIcon, TestOpenMenuItem) +{ + CreateIcon(); + + auto menuitem = GetMenuItemAtIndex(1); + + ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Open"); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_ENABLED)); + + EXPECT_CALL(*volume_, MountAndOpenInFileManager()) + .Times(1); + + dbusmenu_menuitem_handle_event(menuitem, DBUSMENU_MENUITEM_EVENT_ACTIVATED, nullptr, 0); + Utils::WaitForTimeout(1); +} + +TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem_NotEjectableVolume) +{ + CreateIcon(); + + for (auto menuitem : icon_->GetMenus()) + ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Eject"); +} + +TEST_F(TestVolumeLauncherIcon, TestEjectMenuItem) +{ + EXPECT_CALL(*volume_, CanBeEjected()) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + auto menuitem = GetMenuItemAtIndex(2); + + EXPECT_CALL(*volume_, EjectAndShowNotification()) + .Times(1); + + ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Eject"); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); + 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) +{ + CreateIcon(); + + for (auto menuitem : icon_->GetMenus()) + ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Safely remove"); +} + +TEST_F(TestVolumeLauncherIcon, TestSafelyRemoveMenuItem) +{ + EXPECT_CALL(*volume_, CanBeStopped()) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + auto menuitem = GetMenuItemAtIndex(2); + + EXPECT_CALL(*volume_, StopDrive()) + .Times(1); + + ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Safely remove"); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); + 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) +{ + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(false)); + + CreateIcon(); + + for (auto menuitem : icon_->GetMenus()) + ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); +} + + +TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem_EjectableVolume) +{ + EXPECT_CALL(*volume_, CanBeEjected()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + for (auto menuitem : icon_->GetMenus()) + ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); +} + +TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem_StoppableVolume) +{ + EXPECT_CALL(*volume_, CanBeStopped()) + .WillRepeatedly(Return(true)); + + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + for (auto menuitem : icon_->GetMenus()) + ASSERT_STRNE(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); +} + +TEST_F(TestVolumeLauncherIcon, TestUnmountMenuItem) +{ + EXPECT_CALL(*volume_, IsMounted()) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + auto menuitem = GetMenuItemAtIndex(2); + + EXPECT_CALL(*volume_, Unmount()) + .Times(1); + + ASSERT_STREQ(dbusmenu_menuitem_property_get(menuitem, DBUSMENU_MENUITEM_PROP_LABEL), "Unmount"); + EXPECT_TRUE(dbusmenu_menuitem_property_get_bool(menuitem, DBUSMENU_MENUITEM_PROP_VISIBLE)); + 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) +{ + CreateIcon(); + + EXPECT_CALL(*volume_, CanBeEjected()) + .WillRepeatedly(Return(true)); + ASSERT_TRUE(icon_->CanEject()); + + EXPECT_CALL(*volume_, CanBeEjected()) + .WillRepeatedly(Return(false)); + ASSERT_FALSE(icon_->CanEject()); + +} + +TEST_F(TestVolumeLauncherIcon, TestEject) +{ + EXPECT_CALL(*volume_, CanBeEjected()) + .WillRepeatedly(Return(true)); + + CreateIcon(); + + EXPECT_CALL(*volume_, EjectAndShowNotification()) + .Times(1); + + icon_->EjectAndShowNotification(); +} + +TEST_F(TestVolumeLauncherIcon, OnRemoved) +{ + CreateIcon(); + + EXPECT_CALL(*settings_, TryToBlacklist(_)) + .Times(0); + EXPECT_CALL(*settings_, TryToUnblacklist(_)) + .Times(0); + + volume_->removed.emit(); +} + +TEST_F(TestVolumeLauncherIcon, OnRemoved_RemovabledVolume) +{ + EXPECT_CALL(*volume_, CanBeRemoved()) + .WillRepeatedly(Return(true)); + CreateIcon(); + + EXPECT_CALL(*settings_, TryToBlacklist(_)) + .Times(0); + EXPECT_CALL(*settings_, TryToUnblacklist(_)) + .Times(0); + + volume_->removed.emit(); +} + +TEST_F(TestVolumeLauncherIcon, OnRemoved_RemovableAndBlacklistedVolume) +{ + EXPECT_CALL(*volume_, CanBeRemoved()) + .WillRepeatedly(Return(true)); + EXPECT_CALL(*settings_, IsABlacklistedDevice(_)) + .WillRepeatedly(Return(true)); + CreateIcon(); + + EXPECT_CALL(*settings_, TryToBlacklist(_)) + .Times(0); + EXPECT_CALL(*settings_, TryToUnblacklist(_)) + .Times(1); + + volume_->removed.emit(); +} + +} diff --git a/tools/convert-files/compiz-profile-active-unity.convert b/tools/convert-files/compiz-profile-active-unity.convert index 15e14d36a..5c1dfc51a 100644 --- a/tools/convert-files/compiz-profile-active-unity.convert +++ b/tools/convert-files/compiz-profile-active-unity.convert @@ -23,7 +23,7 @@ reveal-pressure = /apps/compiz-1/plugins/unityshell/screen0/options/reveal_press  decay-rate = /apps/compiz-1/plugins/unityshell/screen0/options/decay_rate  stop-velocity = /apps/compiz-1/plugins/unityshell/screen0/options/stop_velocity  autohide-animation = /apps/compiz-1/plugins/unityshell/screen0/options/autohide_animation -alt-tab-bias_viewport = /apps/compiz-1/plugins/unityshell/screen0/options/alt_tab_bias_viewport +alt-tab-bias-viewport = /apps/compiz-1/plugins/unityshell/screen0/options/alt_tab_bias_viewport  edge-responsiveness = /apps/compiz-1/plugins/unityshell/screen0/options/edge_responsiveness  automaximize-value = /apps/compiz-1/plugins/unityshell/screen0/options/automaximize_value  background-color = /apps/compiz-1/plugins/unityshell/screen0/options/background_color @@ -36,11 +36,11 @@ menus-discovery-duration = /apps/compiz-1/plugins/unityshell/screen0/options/men  alt-tab-prev-window = /apps/compiz-1/plugins/unityshell/screen0/options/alt_tab_prev_window  devices-option = /apps/compiz-1/plugins/unityshell/screen0/options/devices_option  reveal-trigger = /apps/compiz-1/plugins/unityshell/screen0/options/reveal_trigger -panel-first_menu = /apps/compiz-1/plugins/unityshell/screen0/options/panel_first_menu +panel-first-menu = /apps/compiz-1/plugins/unityshell/screen0/options/panel_first_menu  icon-size = /apps/compiz-1/plugins/unityshell/screen0/options/icon_size  backlight-mode = /apps/compiz-1/plugins/unityshell/screen0/options/backlight_mode  menus-fadein = /apps/compiz-1/plugins/unityshell/screen0/options/menus_fadein -alt-tab-detail_start = /apps/compiz-1/plugins/unityshell/screen0/options/alt_tab_detail_start +alt-tab-detail-start = /apps/compiz-1/plugins/unityshell/screen0/options/alt_tab_detail_start  show-desktop-icon = /apps/compiz-1/plugins/unityshell/screen0/options/show_desktop_icon  alt-tab-next-window = /apps/compiz-1/plugins/unityshell/screen0/options/alt_tab_next_window  launcher-opacity = /apps/compiz-1/plugins/unityshell/screen0/options/launcher_opacity @@ -53,7 +53,7 @@ menus-fadeout = /apps/compiz-1/plugins/unityshell/screen0/options/menus_fadeout  active-plugins = /apps/compiz-1/plugins/core/screen0/options/active_plugins  vsize = /apps/compiz-1/plugins/core/screen0/options/vsize  hsize = /apps/compiz-1/plugins/core/screen0/options/vsize -close_window_button = /apps/compiz-1/plugins/core/screen0/options/close_window_button +close-window-button = /apps/compiz-1/plugins/core/screen0/options/close_window_button  close-window-key = /apps/compiz-1/plugins/core/screen0/options/close_window_key  lower-window-button = /apps/compiz-1/plugins/core/screen0/options/lower_window_button  lower-window-key = /apps/compiz-1/plugins/core/screen0/options/lower_window_key @@ -66,7 +66,7 @@ raise-window-button = /apps/compiz-1/plugins/core/screen0/options/raise_window_b  raise-window-key = /apps/compiz-1/plugins/core/screen0/options/raise_window_key  show-desktop-edge = /apps/compiz-1/plugins/core/screen0/options/show_desktop_edge  show-desktop-key = /apps/compiz-1/plugins/core/screen0/options/show_desktop_key -toggle-window_maximized-button = /apps/compiz-1/plugins/core/screen0/options/toggle_window_maximized_button +toggle-window-maximized-button = /apps/compiz-1/plugins/core/screen0/options/toggle_window_maximized_button  toggle-window-maximized-horizontally-key = /apps/compiz-1/plugins/core/screen0/options/toggle_window_maximized_horizontally_key  toggle-window-maximized-key = /apps/compiz-1/plugins/core/screen0/options/toggle_window_maximized_key  toggle-window-maximized-vertically-key = /apps/compiz-1/plugins/core/screen0/options/toggle_window_maximized_vertically_key @@ -154,8 +154,7 @@ right-edge-action = /apps/compiz-1/plugins/grid/screen0/options/right_edge_actio  right-edge-threshold = /apps/compiz-1/plugins/grid/screen0/options/right_edge_threshold  snapback-windows = /apps/compiz-1/plugins/grid/screen0/options/snapback_windows  snapoff-maximized = /apps/compiz-1/plugins/grid/screen0/options/snapoff_maximized -top-edge_action = /apps/compiz-1/plugins/grid/screen0/options/top_edge_action -top-edge_threshold = /apps/compiz-1/plugins/grid/screen0/options/top_edge_threshold +top-edge-action = /apps/compiz-1/plugins/grid/screen0/options/top_edge_action +top-edge-threshold = /apps/compiz-1/plugins/grid/screen0/options/top_edge_threshold  top-left-corner-action = /apps/compiz-1/plugins/grid/screen0/options/top_left_corner_action  top-right-corner-action = /apps/compiz-1/plugins/grid/screen0/options/top_right_corner_action - diff --git a/tools/convert-files/compiz-profile-unity.convert b/tools/convert-files/compiz-profile-unity.convert index c97669d29..36fcc9ee2 100644 --- a/tools/convert-files/compiz-profile-unity.convert +++ b/tools/convert-files/compiz-profile-unity.convert @@ -23,7 +23,7 @@ reveal-pressure = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0  decay-rate = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/decay_rate  stop-velocity = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/stop_velocity  autohide-animation = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/autohide_animation -alt-tab-bias_viewport = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/alt_tab_bias_viewport +alt-tab-bias-viewport = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/alt_tab_bias_viewport  edge-responsiveness = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/edge_responsiveness  automaximize-value = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/automaximize_value  background-color = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/background_color @@ -36,11 +36,11 @@ menus-discovery-duration = /apps/compizconfig-1/profiles/unity/plugins/unityshel  alt-tab-prev-window = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/alt_tab_prev_window  devices-option = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/devices_option  reveal-trigger = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/reveal_trigger -panel-first_menu = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/panel_first_menu +panel-first-menu = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/panel_first_menu  icon-size = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/icon_size  backlight-mode = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/backlight_mode  menus-fadein = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/menus_fadein -alt-tab-detail_start = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/alt_tab_detail_start +alt-tab-detail-start = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/alt_tab_detail_start  show-desktop-icon = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/show_desktop_icon  alt-tab-next-window = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/alt_tab_next_window  launcher-opacity = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/options/launcher_opacity @@ -53,7 +53,7 @@ menus-fadeout = /apps/compizconfig-1/profiles/unity/plugins/unityshell/screen0/o  active-plugins = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/active_plugins  vsize = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/vsize  hsize = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/vsize -close_window_button = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/close_window_button +close-window-button = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/close_window_button  close-window-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/close_window_key  lower-window-button = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/lower_window_button  lower-window-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/lower_window_key @@ -66,7 +66,7 @@ raise-window-button = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/o  raise-window-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/raise_window_key  show-desktop-edge = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/show_desktop_edge  show-desktop-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/show_desktop_key -toggle-window_maximized-button = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/toggle_window_maximized_button +toggle-window-maximized-button = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/toggle_window_maximized_button  toggle-window-maximized-horizontally-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/toggle_window_maximized_horizontally_key  toggle-window-maximized-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/toggle_window_maximized_key  toggle-window-maximized-vertically-key = /apps/compizconfig-1/profiles/unity/plugins/core/screen0/options/toggle_window_maximized_vertically_key @@ -154,8 +154,7 @@ right-edge-action = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/opt  right-edge-threshold = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/right_edge_threshold  snapback-windows = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/snapback_windows  snapoff-maximized = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/snapoff_maximized -top-edge_action = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/top_edge_action -top-edge_threshold = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/top_edge_threshold +top-edge-action = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/top_edge_action +top-edge-threshold = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/top_edge_threshold  top-left-corner-action = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/top_left_corner_action  top-right-corner-action = /apps/compizconfig-1/profiles/unity/plugins/grid/screen0/options/top_right_corner_action - diff --git a/tools/unity.cmake b/tools/unity.cmake index af7871855..69bf4c1e5 100755 --- a/tools/unity.cmake +++ b/tools/unity.cmake @@ -18,7 +18,6 @@  # this program; if not, write to the Free Software Foundation, Inc.,  # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -import gconf  import glib  import glob  from optparse import OptionParser @@ -64,47 +63,6 @@ def set_unity_env ():  print "WARNING: no DISPLAY variable set, setting it to :0"  os.environ['DISPLAY'] = ':0' -def reset_unity_compiz_profile (): - '''reset the compiz/unity profile to a vanilla one''' -  - client = gconf.client_get_default() -  - if not client: -	print "WARNING: no gconf client found. No reset will be done" -	return -  - # get current compiz profile to know if we need to switch or not - # as compiz is setting that as a default key schema each time you - # change the profile, the key isn't straightforward to get and set - # as compiz set a new schema instead of a value.. - try: - current_profile_schema = client.get_schema("/apps/compizconfig-1/current_profile") - except (glib.GError, AttributeError), e: - print "WARNING: environment is incorrect: %s\nDid you just try to reset in a tty?" % e - return -  - # default value to not force reset if current_profile is unset - if not current_profile_schema: - print "WARNING: no current gconf profile set, assuming unity" - current_profile_str = 'unity' - current_profile_gconfvalue = None - else: - current_profile_gconfvalue = current_profile_schema.get_default_value() - current_profile_str = current_profile_gconfvalue.get_string() - - if current_profile_str == 'unity': - print "WARNING: Unity currently default profile, so switching to metacity while resetting the values" - subprocess.Popen(["metacity", "--replace"]) #TODO: check if compiz is indeed running - # wait for compiz to stop - time.sleep(2) - if current_profile_gconfvalue: - current_profile_gconfvalue.set_string('fooo') - current_profile_schema.set_default_value(current_profile_gconfvalue) - client.set_schema("/apps/compizconfig-1/current_profile", current_profile_schema) - # the python binding doesn't recursive-unset right - subprocess.Popen(["gconftool-2", "--recursive-unset", "/apps/compiz-1"]).communicate() - subprocess.Popen(["gconftool-2", "--recursive-unset", "/apps/compizconfig-1/profiles/unity"]).communicate() -  def reset_launcher_icons ():  '''Reset the default launcher icon and restart it.'''  subprocess.Popen(["gsettings", "reset" ,"com.canonical.Unity.Launcher" , "favorites"])  @@ -203,8 +161,6 @@ if __name__ == '__main__':  help="Store log under filename.")  parser.add_option("--replace", action="store_true",  help="Run unity /!\ This is for compatibility with other desktop interfaces and acts the same as running unity without --replace") - parser.add_option("--reset", action="store_true", - help="Reset the unity profile in compiz and restart it.")   parser.add_option("--reset-icons", action="store_true",  help="Reset the default launcher icon.")   parser.add_option("-v", "--verbose", action="store_true", @@ -216,9 +172,6 @@ if __name__ == '__main__':  if options.distro: 	sys.exit(reset_to_distro()) - if options.reset: - reset_unity_compiz_profile () -   if options.reset_icons:  reset_launcher_icons () diff --git a/unity-shared/BGHash.cpp b/unity-shared/BGHash.cpp index fc299bc98..4f66a873a 100644 --- a/unity-shared/BGHash.cpp +++ b/unity-shared/BGHash.cpp @@ -46,17 +46,17 @@ void BGHash::OverrideColor(nux::Color const& color)  {  override_color_ = color; - if (override_color_.alpha) - { - TransitionToNewColor(override_color_); - return; - } -  RefreshColor();  }  void BGHash::RefreshColor()  { + if (override_color_.alpha > 0.0f) + { + TransitionToNewColor(override_color_); + return; + } +  Atom real_type;  gint result;  gint real_format; diff --git a/unity-shared/CMakeLists.txt b/unity-shared/CMakeLists.txt index d1b06f000..c2c5b9562 100644 --- a/unity-shared/CMakeLists.txt +++ b/unity-shared/CMakeLists.txt @@ -18,8 +18,6 @@ add_definitions (${CFLAGS})  set (LIBS ${CACHED_UNITY_DEPS_LIBRARIES} ${UNITY_STANDALONE_LADD}) -link_libraries (${LIBS}) -  set (LIB_PATHS ${CACHED_UNITY_DEPS_LIBRARY_DIRS})  link_directories (${CMAKE_BINARY_DIR}/UnityCore ${LIB_PATHS}) @@ -70,6 +68,7 @@ set (UNITY_SHARED_SOURCES  )  add_library (unity-shared STATIC ${UNITY_SHARED_SOURCES}) +target_link_libraries (unity-shared ${LIBS})  add_dependencies (unity-shared unity-core-${UNITY_API_VERSION})  # @@ -81,6 +80,8 @@ set (UNITY_SHARED_COMPIZ_SOURCES  PluginAdapterCompiz.cpp  )  add_library (unity-shared-compiz STATIC ${UNITY_SHARED_COMPIZ_SOURCES}) +target_link_libraries (unity-shared-compiz ${LIBS}) +  add_dependencies (unity-shared-compiz unity-shared)  # standalone @@ -88,6 +89,7 @@ set (UNITY_SHARED_STANDALONE_SOURCES  PluginAdapterStandalone.cpp  )  add_library (unity-shared-standalone STATIC ${UNITY_SHARED_STANDALONE_SOURCES}) +target_link_libraries (unity-shared-standalone ${LIBS})  add_dependencies (unity-shared-standalone unity-shared) diff --git a/unity-shared/CoverArt.cpp b/unity-shared/CoverArt.cpp index 000be4dd2..ac8044e5a 100644 --- a/unity-shared/CoverArt.cpp +++ b/unity-shared/CoverArt.cpp @@ -286,6 +286,7 @@ void CoverArt::TextureLoaded(std::string const& texid, unsigned size, glib::Obje  return;  }  texture_screenshot_.Adopt(nux::CreateTexture2DFromPixbuf(pixbuf, true)); + QueueDraw();  }  void CoverArt::Draw(nux::GraphicsEngine& gfx_engine, bool force_draw) diff --git a/unity-shared/PanelStyle.cpp b/unity-shared/PanelStyle.cpp index 142095396..2d64af4f0 100644 --- a/unity-shared/PanelStyle.cpp +++ b/unity-shared/PanelStyle.cpp @@ -182,9 +182,17 @@ nux::NBitmapData* Style::GetBackground(int width, int height, float opacity)  return context.GetBitmap();  } -nux::BaseTexture* Style::GetWindowButton(WindowButtonType type, WindowState state) +/*! + Return a vector with the possible file names sorted by priority + + @param type The type of the button. + @param state The button state. + + @return A vector of strings with the possible file names sorted by priority. +*/ +std::vector<std::string> Style::GetWindowButtonFileNames(WindowButtonType type, WindowState state)  { - nux::BaseTexture* texture = NULL; + std::vector<std::string> files;  std::string names[] = { "close", "minimize", "unmaximize", "maximize" };  std::string states[] = { "", "_focused_prelight", "_focused_pressed", "_unfocused",  "_unfocused", "_unfocused_prelight", "_unfocused_pressed"}; @@ -200,38 +208,40 @@ nux::BaseTexture* Style::GetWindowButton(WindowButtonType type, WindowState stat  glib::String filename(g_build_filename(home_dir, ".themes", _theme_name.c_str(), subpath.str().c_str(), NULL));  if (g_file_test(filename.Value(), G_FILE_TEST_EXISTS)) - { - glib::Error error; - - // Found a file, try loading the pixbuf - glib::Object<GdkPixbuf> pixbuf(gdk_pixbuf_new_from_file(filename.Value(), &error)); - if (error) - LOG_WARNING(logger) << "Unable to load window button " << filename.Value() << ": " << error.Message(); - else - texture = nux::CreateTexture2DFromPixbuf(pixbuf, true); - } + files.push_back (filename.Value());  } - // texture is NULL if the pixbuf is not loaded - if (!texture) - { - const char* var = g_getenv("GTK_DATA_PREFIX"); - if (!var) - var = "/usr"; + const char* var = g_getenv("GTK_DATA_PREFIX"); + if (!var) + var = "/usr"; - glib::String filename(g_build_filename(var, "share", "themes", _theme_name.c_str(), subpath.str().c_str(), NULL)); + glib::String filename(g_build_filename(var, "share", "themes", _theme_name.c_str(), subpath.str().c_str(), NULL)); + if (g_file_test(filename.Value(), G_FILE_TEST_EXISTS)) + files.push_back (filename.Value()); - if (g_file_test(filename.Value(), G_FILE_TEST_EXISTS)) - { - glib::Error error; + return files; +} + +nux::BaseTexture* Style::GetWindowButton(WindowButtonType type, WindowState state) +{ + nux::BaseTexture* texture = NULL; - // Found a file, try loading the pixbuf - glib::Object<GdkPixbuf> pixbuf(gdk_pixbuf_new_from_file(filename.Value(), &error)); + std::vector<std::string> files = GetWindowButtonFileNames (type, state); + for (unsigned int i=0; i < files.size(); i++) + { + glib::Error error; + // Try loading the pixbuf + glib::Object<GdkPixbuf> pixbuf(gdk_pixbuf_new_from_file(files[i].c_str (), &error));  if (error) - LOG_WARNING(logger) << "Unable to load window button " << filename.Value() << ": " << error.Message(); + { + LOG_WARNING(logger) << "Unable to load window button " << files[i] << ": " << error.Message(); + }  else + {  texture = nux::CreateTexture2DFromPixbuf(pixbuf, true); - } + if (texture) + break; + }  }  if (!texture) diff --git a/unity-shared/PanelStyle.h b/unity-shared/PanelStyle.h index b5640c18e..b73c05f74 100644 --- a/unity-shared/PanelStyle.h +++ b/unity-shared/PanelStyle.h @@ -71,6 +71,7 @@ public:  GtkStyleContext* GetStyleContext();  nux::NBitmapData* GetBackground(int width, int height, float opacity);  nux::BaseTexture* GetWindowButton(WindowButtonType type, WindowState state); + std::vector<std::string> GetWindowButtonFileNames(WindowButtonType type, WindowState state);  nux::BaseTexture* GetFallbackWindowButton(WindowButtonType type, WindowState state);  glib::Object<GdkPixbuf> GetHomeButton();  std::string GetFontDescription(PanelItem item); diff --git a/unity-shared/PluginAdapter.h b/unity-shared/PluginAdapter.h index 8c9882bd1..a6fda2bcf 100644 --- a/unity-shared/PluginAdapter.h +++ b/unity-shared/PluginAdapter.h @@ -154,6 +154,7 @@ public:  nux::Geometry GetWindowSavedGeometry(guint32 xid) const;  nux::Geometry GetScreenGeometry() const;  nux::Geometry GetWorkAreaGeometry(guint32 xid = 0) const; + std::string GetWindowName(guint32 xid) const;  void CheckWindowIntersections(nux::Geometry const& region, bool &active, bool &any); @@ -177,6 +178,9 @@ private:  bool CheckWindowIntersection(nux::Geometry const& region, CompWindow* window) const;  void SetMwmWindowHints(Window xid, MotifWmHints* new_hints); + std::string GetTextProperty(guint32 xid, Atom atom) const; + std::string GetUtf8Property(guint32 xid, Atom atom) const; +  CompScreen* m_Screen;  MultiActionList m_ExpoActionList;  MultiActionList m_ScaleActionList; diff --git a/unity-shared/PluginAdapterCompiz.cpp b/unity-shared/PluginAdapterCompiz.cpp index 1c67e9659..fe1c978cc 100644 --- a/unity-shared/PluginAdapterCompiz.cpp +++ b/unity-shared/PluginAdapterCompiz.cpp @@ -1335,3 +1335,70 @@ PluginAdapter::AddProperties(GVariantBuilder* builder)  .add("viewport_switch_running", IsViewPortSwitchStarted())  .add("showdesktop_active", _in_show_desktop);  } + +std::string +PluginAdapter::GetWindowName(guint32 xid) const +{ + std::string name; + Atom visibleNameAtom; + + visibleNameAtom = XInternAtom(m_Screen->dpy(), "_NET_WM_VISIBLE_NAME", 0); + name = GetUtf8Property(xid, visibleNameAtom); + if (name.empty()) + { + Atom wmNameAtom = XInternAtom(m_Screen->dpy(), "_NET_WM_NAME", 0); + name = GetUtf8Property(xid, wmNameAtom); + } + + if (name.empty()) + name = GetTextProperty(xid, XA_WM_NAME); + + return name; +} + +std::string +PluginAdapter::GetUtf8Property(guint32 xid, Atom atom) const +{ + Atom type; + int result, format; + unsigned long nItems, bytesAfter; + char *val; + std::string retval; + Atom utf8StringAtom; + + utf8StringAtom = XInternAtom(m_Screen->dpy(), "UTF8_STRING", 0); + result = XGetWindowProperty(m_Screen->dpy(), xid, atom, 0L, 65536, False, + utf8StringAtom, &type, &format, &nItems, + &bytesAfter, reinterpret_cast<unsigned char **>(&val)); + + if (result != Success) + return retval; + + if (type == utf8StringAtom && format == 8 && val && nItems > 0) + { + retval = std::string(val, nItems); + } + if (val) + XFree(val); + + return retval; +} + +std::string +PluginAdapter::GetTextProperty(guint32 id, Atom atom) const +{ + XTextProperty text; + std::string retval; + + text.nitems = 0; + if (XGetTextProperty(m_Screen->dpy(), id, &text, atom)) + { + if (text.value) + { + retval = std::string(reinterpret_cast<char*>(text.value), text.nitems); + XFree (text.value); + } + } + + return retval; +} diff --git a/unity-shared/PluginAdapterStandalone.cpp b/unity-shared/PluginAdapterStandalone.cpp index 9ba893021..9e31f2a1b 100644 --- a/unity-shared/PluginAdapterStandalone.cpp +++ b/unity-shared/PluginAdapterStandalone.cpp @@ -458,3 +458,9 @@ PluginAdapter::AddProperties(GVariantBuilder* builder)  .add("viewport_switch_running", IsViewPortSwitchStarted())  .add("showdesktop_active", _in_show_desktop);  } + +std::string +PluginAdapter::GetWindowName(guint32 xid) const +{ + return ""; +} diff --git a/unity-shared/PreviewStyle.cpp b/unity-shared/PreviewStyle.cpp index 9d44dc539..ef8df611f 100644 --- a/unity-shared/PreviewStyle.cpp +++ b/unity-shared/PreviewStyle.cpp @@ -39,6 +39,9 @@ namespace  {  Style* style_instance = nullptr; +const int preview_width = 770; +const int preview_height = 380; +  nux::logging::Logger logger("unity.dash.previews.style");  typedef nux::ObjectPtr<nux::BaseTexture> BaseTexturePtr; @@ -159,9 +162,14 @@ int Style::GetNavigatorIconSize() const  return 24;   } -float Style::GetPreviewAspectRatio() const +int Style::GetPreviewWidth() const  { - return static_cast<float>(796)/390; + return preview_width; +} + +int Style::GetPreviewHeight() const +{ + return preview_height;  }  int Style::GetDetailsTopMargin() const @@ -186,7 +194,7 @@ int Style::GetDetailsLeftMargin() const  int Style::GetPanelSplitWidth() const  { - return 16; + return 10;  }  int Style::GetAppIconAreaWidth() const diff --git a/unity-shared/PreviewStyle.h b/unity-shared/PreviewStyle.h index 3bfff9fd6..ac197f29d 100644 --- a/unity-shared/PreviewStyle.h +++ b/unity-shared/PreviewStyle.h @@ -60,7 +60,8 @@ public:  int GetNavigatorWidth() const;  int GetNavigatorIconSize() const; - float GetPreviewAspectRatio() const; + int GetPreviewWidth() const; + int GetPreviewHeight() const;  int GetDetailsTopMargin() const;  int GetDetailsBottomMargin() const; diff --git a/unity-shared/WindowManager.cpp b/unity-shared/WindowManager.cpp index 5f5cd6519..df983217c 100644 --- a/unity-shared/WindowManager.cpp +++ b/unity-shared/WindowManager.cpp @@ -240,6 +240,11 @@ class WindowManagerDummy : public WindowManager  void AddProperties(GVariantBuilder* builder)  {  } + + std::string GetWindowName(guint32 xid) const + { + return "unknown"; + }  };  WindowManager* diff --git a/unity-shared/WindowManager.h b/unity-shared/WindowManager.h index 84ae82485..204c9439c 100644 --- a/unity-shared/WindowManager.h +++ b/unity-shared/WindowManager.h @@ -111,6 +111,8 @@ public:  virtual bool saveInputFocus() = 0;  virtual bool restoreInputFocus() = 0; + virtual std::string GetWindowName(guint32 xid) const = 0; +  // Signals  sigc::signal<void, guint32> window_mapped;  sigc::signal<void, guint32> window_unmapped; | 
