Merge lp:~kamstrup/unity/home-lenses into lp:unity

Proposed by Mikkel Kamstrup Erlandsen
Status: Merged
Approved by: Michal Hruby
Approved revision: no longer in the source branch.
Merged at revision: 1868
Proposed branch: lp:~kamstrup/unity/home-lenses
Merge into: lp:unity
Diff against target: 3591 lines (+1781/-1033)
35 files modified
HACKING (+7/-0)
UnityCore/CMakeLists.txt (+2/-0)
UnityCore/Categories.cpp (+8/-0)
UnityCore/Categories.h (+1/-0)
UnityCore/Filters.cpp (+8/-0)
UnityCore/Filters.h (+1/-0)
UnityCore/GLibDBusProxy.cpp (+11/-0)
UnityCore/GLibDBusProxy.h (+1/-0)
UnityCore/GLibWrapper.cpp (+5/-0)
UnityCore/GLibWrapper.h (+1/-0)
UnityCore/HomeLens.cpp (+957/-0)
UnityCore/HomeLens.h (+79/-0)
UnityCore/Lens.cpp (+128/-55)
UnityCore/Lens.h (+17/-6)
UnityCore/Model-inl.h (+47/-9)
UnityCore/Model.h (+11/-0)
UnityCore/Results.cpp (+8/-0)
UnityCore/Results.h (+1/-0)
com.canonical.Unity.gschema.xml (+10/-3)
plugins/unityshell/src/DashView.cpp (+41/-9)
plugins/unityshell/src/DashView.h (+4/-2)
plugins/unityshell/src/HomeView.cpp (+0/-252)
plugins/unityshell/src/HomeView.h (+0/-93)
plugins/unityshell/src/LensBar.cpp (+13/-3)
plugins/unityshell/src/LensView.cpp (+11/-2)
plugins/unityshell/src/PlacesHomeView.cpp (+0/-378)
plugins/unityshell/src/PlacesHomeView.h (+0/-72)
plugins/unityshell/src/ResultViewGrid.cpp (+13/-2)
po/POTFILES.in (+0/-1)
po/unity.pot (+0/-120)
standalone-clients/CMakeLists.txt (+0/-4)
standalone-clients/standalone_dash.cpp (+0/-16)
tests/CMakeLists.txt (+7/-6)
tests/test_home_lens.cpp (+362/-0)
tests/test_utils.h (+27/-0)
To merge this branch: bzr merge lp:~kamstrup/unity/home-lenses
Reviewer Review Type Date Requested Status
Michal Hruby (community) Approve
Andrea Azzarone (community) Needs Information
Review via email: mp+89669@code.launchpad.net

Description of the change

Replace the Unity home screen tiles with a "merged lens view" where lenses can contribute any categories they see fit.

settings: introduced a new gsettings key in the com.canonical.Unity.Dash namespace that controls the order of the results on the home screen. We should consider in a later branch having the sorting logic in FilesystemLenses also use this key (still falling back to alphabetical sorting when nothing is set). It empowers users and OEMs to override the default view on the homescreen by putting custom stuff first.

libunity-core: Added a HomeLens class that implements Lens and Lenses interfaces. The typical use case is to add a FilesystemLenses to the HomeLens which will make it automatically merge all lenses found on the fs. In testing we implement custom Lenses instances and add those to the HomeLens in stead.

unityshell: Removed the tiled homescreen and use a bog standard LensView to render the HomeLens in stead. Fixed a bunch of places where we assumed that Lenses' categories were always only appended to. This is no longer true with the HomeLens. Also important: Fix the SetViewType() calling semantics to the lenses when showing/hiding the dash and when switching between the lenses.

Testing it out:
For this branch to work you need also: lp:~kamstrup/unity-lens-applications/home-lenses, lp:~kamstrup/unity-lens-files/home-lenses, and lp:~mhr3/unity-lens-music/home-lenses. Add to that latest bamf. Without these you will not see the correct results.

To validate the results: Make sure that bringing up the dash resets the search and shows you a default view with exactly 3 categories in this order: Recent Apps, Recent Files, and Downloads. Then do a search and validate that you have exactly 3 categories in this order Applications, Files & Folders, and Music.

Finally; launch an app that is not a favorite, then bring up the bring up the dash home screen and assert that the running app is *not* in the recent apps category (but showing in the launcher). Then close the app and bring up the dash again. The app should now show under Recent Apps.

Design review:
Home screen without search: https://bugs.launchpad.net/unity/+bug/885738/+attachment/2690636/+files/unity-home-lens-design-review-1.png
Home screen with search: https://bugs.launchpad.net/unity/+bug/885738/+attachment/2690639/+files/unity-home-lens-design-review-2.png

To post a comment you must log in.
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Meh. Sorry for the i18n noise. No idea how that got here. Will try to remove.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Sigh. bzr insists that I don't have any diff with trunk wrt the translations. Fun and games.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Found it. Amended in r1875.

Revision history for this message
Michal Hruby (mhr3) wrote :

Just a note: all the lens branches are now merged in respective trunks.

Revision history for this message
Andrea Azzarone (azzar1) wrote :

Is PlacesHomeView.cpp/h still needed?

Revision history for this message
Andrea Azzarone (azzar1) :
review: Needs Information
Revision history for this message
Michal Hruby (mhr3) wrote :

1519 ~Lens();

I believe this should be virtual now.

1538 template<class RowAdaptor>
1539 Model<RowAdaptor>::Model()

The missing model.SetGetterFunction(...) could be a very nasty surprise in the future.

One of weird things I noticed is that when using keynav in the home view (specifically the down array) sometimes the "See xx more results" doesn't get highlighted and can't be expanded by pressing enter.

review: Needs Fixing
Revision history for this message
Andrea Azzarone (azzar1) wrote :

> One of weird things I noticed is that when using keynav in the home view
> (specifically the down array) sometimes the "See xx more results" doesn't get
> highlighted and can't be expanded by pressing enter.

Fixed here https://code.launchpad.net/~andyrock/unity/key-nav

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

> Is PlacesHomeView.cpp/h still needed

Strictly, no. I didn't delete it because technically the home-lens concept is on a trial run. If design ditches it we'll have to revert to the old view and I didn't want to dig it out of the rev history. Although, I probably should delete it, to have a better looking diffstat :-)

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

> 1519 ~Lens();
>
> I believe this should be virtual now.

Right. I wonder why the compiler didn't complain. It usually does in these cases. Fixed.

> 1538 template<class RowAdaptor>
> 1539 Model<RowAdaptor>::Model()
>
> The missing model.SetGetterFunction(...) could be a very nasty surprise in the
> future.

Ah, good catch. I created a new Init() method that gets called from the constructors so they can share the setup code. (my favorite hate-pattern. why oh why can't we chain constructors in C++?!?!?!11). Anyway; fixed.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

WTF. The diffs seems to be showing that I removed po/unity.pot which I swear I didn't! Let me look into it.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Ok, the diff should be clean in r1881. Please re-review

Revision history for this message
Michal Hruby (mhr3) wrote :

One thing that's still missing is resetting the search entry when dash is hidden (or not-resetting the search sent to lenses as https://bugs.launchpad.net/ayatana-design/+bug/914759 requires).

Either would be fine, but right now the search entry isn't reset, while the search is...

review: Needs Fixing
Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Implemented search state retention as requested by design. Please re-review.

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

John acked the design review on IRC

Revision history for this message
Michal Hruby (mhr3) wrote :

Sorry, but Esc now no longer clears the search entry... I don't think that's desired.

review: Needs Fixing
Revision history for this message
Andrea Azzarone (azzar1) wrote :

> Sorry, but Esc now no longer clears the search entry... I don't think that's
> desired.

1866 - else
1867 - search_bar_->search_string = "";

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

> > Sorry, but Esc now no longer clears the search entry... I don't think that's
> > desired.
>
> 1866 - else
> 1867 - search_bar_->search_string = "";

I am not sure if you mean to back Michal up, or refute his claim Andrea? :-)

In any case, I think I have the right behavior here... When clicking Esc I see:

a) if there is text in the entry the text is cleared, and the dash shows results for the empty search
b) if there is no search, the dash is hidden

As a Corollary, c) hitting Esc twice clears and hides the dash.

Revision history for this message
Michal Hruby (mhr3) wrote :

Unfortunately, that's not the behaviour I see (particularly a) doesn't work). Afaict the code Andrea pasted is what handled the "esc and search entry not empty" case, and you removed it...

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

Hmmm, I can see you are correct looking at the code... I wonder how it could work on my box. I must have been running some almost, but not entirely, identical to what I pushed. Nonetheless; pushed a fix.

Revision history for this message
Michal Hruby (mhr3) wrote :

Yep, it's fine now. Approved from me, but I suppose more people want to go through this...

review: Approve
Revision history for this message
Neil J. Patel (njpatel) wrote :

237 (and other places): Instead of handling the gchar* yourself, use glib::String from UnityCore/GLibWrapper.h, it autofree's it when it's popped from the stack, so you don't need to worry about g_free in multiple places.

330: Evil, evil kamstrup.

Apart from that, excellent work :)

Revision history for this message
Mikkel Kamstrup Erlandsen (kamstrup) wrote :

> 237 (and other places): Instead of handling the gchar* yourself, use
> glib::String from UnityCore/GLibWrapper.h, it autofree's it when it's popped
> from the stack, so you don't need to worry about g_free in multiple places.

Ok, now using glib::String. It required that I add automatic std::string conversion to glib::String in order not to complicate the code - I was wondering if there was a specific reason it wasn't in glib::String already. Seems like a strange omission?

Revision history for this message
Michal Hruby (mhr3) wrote :

Neil ACKed this as well, so approving...

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'HACKING'
--- HACKING 2012-01-10 16:35:26 +0000
+++ HACKING 2012-01-26 08:37:26 +0000
@@ -39,5 +39,12 @@
3939
40Testing is handled through GTest, googles test system, we currently have three test targets; test-gtest, test-gtest-xless, test-gtest-dbus. tests that require Nux should be in test-gtest as they require X, tests that can run headless should be in test-gtest-xless and tests that require dbus should be run with test-gtest-dbus, which tests run with our test service.40Testing is handled through GTest, googles test system, we currently have three test targets; test-gtest, test-gtest-xless, test-gtest-dbus. tests that require Nux should be in test-gtest as they require X, tests that can run headless should be in test-gtest-xless and tests that require dbus should be run with test-gtest-dbus, which tests run with our test service.
4141
42To enable debug logging you must set the environment variable UNITY_LOG_SEVERITY to the log domain you
43want to watch and the log level for that domain. For example:
44
45 UNITY_LOG_SEVERITY="unity.dash.lens=DEBUG" standalone-clients/dash
46
47
48
42for more information see the README file49for more information see the README file
4350
4451
=== modified file 'UnityCore/CMakeLists.txt'
--- UnityCore/CMakeLists.txt 2012-01-17 16:02:39 +0000
+++ UnityCore/CMakeLists.txt 2012-01-26 08:37:26 +0000
@@ -21,6 +21,7 @@
21 GLibSignal-inl.h21 GLibSignal-inl.h
22 GLibWrapper.h22 GLibWrapper.h
23 GLibWrapper-inl.h23 GLibWrapper-inl.h
24 HomeLens.h
24 IndicatorEntry.h25 IndicatorEntry.h
25 Indicator.h26 Indicator.h
26 Indicators.h27 Indicators.h
@@ -53,6 +54,7 @@
53 GLibDBusProxy.cpp54 GLibDBusProxy.cpp
54 GLibSignal.cpp55 GLibSignal.cpp
55 GLibWrapper.cpp56 GLibWrapper.cpp
57 HomeLens.cpp
56 Indicator.cpp58 Indicator.cpp
57 IndicatorEntry.cpp59 IndicatorEntry.cpp
58 Indicators.cpp60 Indicators.cpp
5961
=== modified file 'UnityCore/Categories.cpp'
--- UnityCore/Categories.cpp 2011-07-25 10:54:25 +0000
+++ UnityCore/Categories.cpp 2012-01-26 08:37:26 +0000
@@ -31,6 +31,14 @@
31 row_removed.connect(sigc::mem_fun(this, &Categories::OnRowRemoved));31 row_removed.connect(sigc::mem_fun(this, &Categories::OnRowRemoved));
32}32}
3333
34Categories::Categories(ModelType model_type)
35 : Model<Category>::Model(model_type)
36{
37 row_added.connect(sigc::mem_fun(this, &Categories::OnRowAdded));
38 row_changed.connect(sigc::mem_fun(this, &Categories::OnRowChanged));
39 row_removed.connect(sigc::mem_fun(this, &Categories::OnRowRemoved));
40}
41
34void Categories::OnRowAdded(Category& category)42void Categories::OnRowAdded(Category& category)
35{43{
36 category_added.emit(category);44 category_added.emit(category);
3745
=== modified file 'UnityCore/Categories.h'
--- UnityCore/Categories.h 2011-07-28 13:34:38 +0000
+++ UnityCore/Categories.h 2012-01-26 08:37:26 +0000
@@ -36,6 +36,7 @@
36 typedef std::shared_ptr<Categories> Ptr;36 typedef std::shared_ptr<Categories> Ptr;
3737
38 Categories();38 Categories();
39 Categories(ModelType model_type);
3940
40 sigc::signal<void, Category const&> category_added;41 sigc::signal<void, Category const&> category_added;
41 sigc::signal<void, Category const&> category_changed;42 sigc::signal<void, Category const&> category_changed;
4243
=== modified file 'UnityCore/Filters.cpp'
--- UnityCore/Filters.cpp 2011-08-05 09:55:33 +0000
+++ UnityCore/Filters.cpp 2012-01-26 08:37:26 +0000
@@ -56,6 +56,14 @@
56 row_removed.connect(sigc::mem_fun(this, &Filters::OnRowRemoved));56 row_removed.connect(sigc::mem_fun(this, &Filters::OnRowRemoved));
57}57}
5858
59Filters::Filters(ModelType model_type)
60 : Model<FilterAdaptor>::Model(model_type)
61{
62 row_added.connect(sigc::mem_fun(this, &Filters::OnRowAdded));
63 row_changed.connect(sigc::mem_fun(this, &Filters::OnRowChanged));
64 row_removed.connect(sigc::mem_fun(this, &Filters::OnRowRemoved));
65}
66
59Filters::~Filters()67Filters::~Filters()
60{}68{}
6169
6270
=== modified file 'UnityCore/Filters.h'
--- UnityCore/Filters.h 2011-08-05 09:55:33 +0000
+++ UnityCore/Filters.h 2012-01-26 08:37:26 +0000
@@ -51,6 +51,7 @@
51 typedef std::map<DeeModelIter*, Filter::Ptr> FilterMap;51 typedef std::map<DeeModelIter*, Filter::Ptr> FilterMap;
5252
53 Filters();53 Filters();
54 Filters(ModelType model_type);
54 ~Filters();55 ~Filters();
5556
56 Filter::Ptr FilterAtIndex(std::size_t index);57 Filter::Ptr FilterAtIndex(std::size_t index);
5758
=== modified file 'UnityCore/GLibDBusProxy.cpp'
--- UnityCore/GLibDBusProxy.cpp 2012-01-16 15:19:47 +0000
+++ UnityCore/GLibDBusProxy.cpp 2012-01-26 08:37:26 +0000
@@ -72,6 +72,7 @@
72 int timeout_msec);72 int timeout_msec);
7373
74 void Connect(string const& signal_name, ReplyCallback callback);74 void Connect(string const& signal_name, ReplyCallback callback);
75 bool IsConnected();
7576
76 static void OnNameAppeared(GDBusConnection* connection, const char* name,77 static void OnNameAppeared(GDBusConnection* connection, const char* name,
77 const char* name_owner, gpointer impl);78 const char* name_owner, gpointer impl);
@@ -192,6 +193,11 @@
192 this);193 this);
193}194}
194195
196bool DBusProxy::Impl::IsConnected()
197{
198 return connected_;
199}
200
195void DBusProxy::Impl::OnProxyConnectCallback(GObject* source,201void DBusProxy::Impl::OnProxyConnectCallback(GObject* source,
196 GAsyncResult* res,202 GAsyncResult* res,
197 gpointer impl)203 gpointer impl)
@@ -320,5 +326,10 @@
320 pimpl->Connect(signal_name, callback);326 pimpl->Connect(signal_name, callback);
321}327}
322328
329bool DBusProxy::IsConnected()
330{
331 return pimpl->IsConnected();
332}
333
323}334}
324}335}
325336
=== modified file 'UnityCore/GLibDBusProxy.h'
--- UnityCore/GLibDBusProxy.h 2011-12-06 10:39:36 +0000
+++ UnityCore/GLibDBusProxy.h 2012-01-26 08:37:26 +0000
@@ -54,6 +54,7 @@
54 int timeout_msec = -1);54 int timeout_msec = -1);
5555
56 void Connect(std::string const& signal_name, ReplyCallback callback);56 void Connect(std::string const& signal_name, ReplyCallback callback);
57 bool IsConnected();
5758
58 sigc::signal<void> connected;59 sigc::signal<void> connected;
59 sigc::signal<void> disconnected;60 sigc::signal<void> disconnected;
6061
=== modified file 'UnityCore/GLibWrapper.cpp'
--- UnityCore/GLibWrapper.cpp 2011-12-06 10:39:36 +0000
+++ UnityCore/GLibWrapper.cpp 2012-01-26 08:37:26 +0000
@@ -104,6 +104,11 @@
104 return string_;104 return string_;
105}105}
106106
107String::operator std::string()
108{
109 return Str();
110}
111
107String::operator bool() const112String::operator bool() const
108{113{
109 return bool(string_);114 return bool(string_);
110115
=== modified file 'UnityCore/GLibWrapper.h'
--- UnityCore/GLibWrapper.h 2011-12-14 18:22:23 +0000
+++ UnityCore/GLibWrapper.h 2012-01-26 08:37:26 +0000
@@ -115,6 +115,7 @@
115115
116 operator bool() const;116 operator bool() const;
117 operator char*();117 operator char*();
118 operator std::string();
118 gchar* Value();119 gchar* Value();
119 std::string Str() const;120 std::string Str() const;
120121
121122
=== added file 'UnityCore/HomeLens.cpp'
--- UnityCore/HomeLens.cpp 1970-01-01 00:00:00 +0000
+++ UnityCore/HomeLens.cpp 2012-01-26 08:37:26 +0000
@@ -0,0 +1,957 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3 * Copyright (C) 2012 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com>
18 */
19
20#include <glib.h>
21#include <string>
22#include <stdexcept>
23#include <map>
24
25#include "GLibSignal.h"
26#include "HomeLens.h"
27#include "Lens.h"
28#include "Model.h"
29
30#include "config.h"
31
32namespace unity
33{
34namespace dash
35{
36
37namespace
38{
39
40nux::logging::Logger logger("unity.dash.homelens");
41
42}
43
44/*
45 * Helper class that maps category offsets between the merged lens and
46 * source lenses. We also use it to merge categories from different lenses
47 * with the same display name into the same category.
48 *
49 * NOTE: The model pointers passed in are expected to be pointers to the
50 * result source models - and not the category source models!
51 */
52class HomeLens::CategoryRegistry
53{
54public:
55 CategoryRegistry(HomeLens* owner)
56 : is_dirty_(false)
57 , owner_(owner) {}
58
59 int FindCategoryOffset(DeeModel* model, unsigned int source_cat_offset)
60 {
61 glib::String c_id(g_strdup_printf("%u+%p", source_cat_offset, model));
62 std::map<std::string,unsigned int>::iterator i = reg_by_id_.find(c_id);
63
64 if (i != reg_by_id_.end())
65 return i->second;
66
67 return -1;
68 }
69
70 int FindCategoryOffset(const gchar* display_name)
71 {
72 std::map<std::string,unsigned int>::iterator i =
73 reg_by_display_name_.find(display_name);
74 if (i != reg_by_display_name_.end())
75 return i->second;
76
77 return -1;
78 }
79
80 /* Register a new category */
81 void RegisterCategoryOffset(DeeModel* model,
82 unsigned int source_cat_offset,
83 const gchar* display_name,
84 unsigned int target_cat_offset)
85 {
86 glib::String c_id(g_strdup_printf("%u+%p", source_cat_offset, model));
87
88 std::map<std::string,unsigned int>::iterator i = reg_by_id_.find(c_id);
89 if (i != reg_by_id_.end())
90 {
91 LOG_ERROR(logger) << "Category '" << c_id << "' already registered!";
92 return;
93 }
94
95 if (display_name != NULL)
96 {
97 i = reg_by_display_name_.find(display_name);
98 if (i != reg_by_display_name_.end())
99 {
100 LOG_ERROR(logger) << "Category '" << display_name << "' already registered!";
101 return;
102 }
103 }
104
105 /* Any existing categories with offsets >= target_cat_offset must be
106 * pushed up. Update both maps by id and display name */
107 std::map<std::string,unsigned int>::iterator end = reg_by_id_.end();
108 for (i = reg_by_id_.begin(); i != end; i++)
109 {
110 if (i->second >= target_cat_offset)
111 {
112 i->second = i->second + 1;
113 is_dirty_ = true;
114 }
115 }
116
117 for (i = reg_by_display_name_.begin(), end = reg_by_display_name_.end(); i != end; i++)
118 {
119 if (i->second >= target_cat_offset)
120 {
121 i->second = i->second + 1;
122 is_dirty_ = true;
123 }
124 }
125
126 reg_by_id_[c_id] = target_cat_offset;
127
128 /* Callers pass a NULL display_name when they already have a category
129 * with the right display registered */
130 if (display_name != NULL)
131 {
132 reg_by_display_name_[display_name] = target_cat_offset;
133 LOG_DEBUG(logger) << "Registered category '" << display_name
134 << "' with source offset " << source_cat_offset
135 << " and target offset " << target_cat_offset
136 << ". Id " << c_id;
137 }
138 else
139 {
140 LOG_DEBUG(logger) << "Registered category with source offset "
141 << source_cat_offset << " and target offset "
142 << target_cat_offset << ". Id " << c_id;
143 }
144 }
145
146 /* Associate a source results model and category offset with an existing
147 * target category offset */
148 void AssociateCategoryOffset(DeeModel* model,
149 unsigned int source_cat_offset,
150 unsigned int target_cat_offset)
151 {
152 glib::String c_id(g_strdup_printf("%u+%p", source_cat_offset, model));
153
154 std::map<std::string,unsigned int>::iterator i = reg_by_id_.find(c_id);
155 if (i != reg_by_id_.end())
156 {
157 LOG_ERROR(logger) << "Category '" << c_id << "' already registered!";
158 return;
159 }
160
161 reg_by_id_[c_id] = target_cat_offset;
162 }
163
164 /**
165 * Returns true and resets the dirty state if the registry was dirty.
166 * When you've checked a dirty registry you must either clear the
167 * merged results model or recalibrate all category offset in it
168 * (and Unity probably wont support the latter?).
169 */
170 bool CheckDirty()
171 {
172 return is_dirty_ ? (is_dirty_ = false, true) : false;
173 }
174
175private:
176 std::map<std::string,unsigned int> reg_by_id_;
177 std::map<std::string,unsigned int> reg_by_display_name_;
178 bool is_dirty_;
179 HomeLens* owner_;
180};
181
182/*
183 * Helper class that merges a set of DeeModels into one super model
184 */
185class HomeLens::ModelMerger : public sigc::trackable
186{
187public:
188 ModelMerger(glib::Object<DeeModel> target);
189 virtual ~ModelMerger();
190
191 void AddSource(glib::Object<DeeModel> source);
192
193protected:
194 virtual void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter);
195 virtual void OnSourceRowRemoved(DeeModel* model, DeeModelIter* iter);
196 virtual void OnSourceRowChanged(DeeModel* model, DeeModelIter* iter);
197 void EnsureRowBuf(DeeModel *source);
198
199 /* The merge tag lives on the source models, pointing to the mapped
200 * row in the target model */
201 DeeModelTag* FindSourceToTargetTag(DeeModel *model);
202
203protected:
204 glib::SignalManager sig_manager_;
205 GVariant** row_buf_;
206 unsigned int n_cols_;
207 glib::Object<DeeModel> target_;
208 std::map<DeeModel*,DeeModelTag*> source_to_target_tags_;
209};
210
211/*
212 * Specialized ModelMerger that takes care merging results models.
213 * We need special handling here because rows in each lens' results model
214 * specifies an offset into the lens' categories model where the display
215 * name of the category is defined.
216 *
217 * This class converts the offset of the source lens' categories into
218 * offsets into the merged category model.
219 *
220 * Each row added to the target is tagged with a pointer to the Lens instance
221 * from which it came
222 */
223class HomeLens::ResultsMerger : public ModelMerger
224{
225public:
226 ResultsMerger(glib::Object<DeeModel> target,
227 HomeLens::CategoryRegistry* cat_registry);
228
229protected:
230 void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter);
231 void OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter);
232 void OnSourceRowChanged(DeeModel *model, DeeModelIter *iter);
233 void CheckCategoryRegistryDirty();
234
235private:
236 HomeLens::CategoryRegistry* cat_registry_;
237};
238
239/*
240 * Specialized ModelMerger that takes care merging category models.
241 * We need special handling here because rows in each lens' results model
242 * specifies an offset into the lens' categories model where the display
243 * name of the category is defined.
244 *
245 * This class records a map of the offsets from the original source category
246 * models to the offsets in the combined categories model.
247 */
248class HomeLens::CategoryMerger : public ModelMerger
249{
250public:
251 CategoryMerger(glib::Object<DeeModel> target,
252 HomeLens::CategoryRegistry* cat_registry);
253
254 void OnSourceRowAdded(DeeModel *model, DeeModelIter *iter);
255 void OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter);
256
257private:
258 HomeLens::CategoryRegistry* cat_registry_;
259 DeeModelTag* priority_tag_;
260};
261
262/*
263 * Pimpl for HomeLens
264 */
265class HomeLens::Impl : public sigc::trackable
266{
267public:
268 Impl(HomeLens* owner);
269 ~Impl();
270
271 void OnLensAdded(Lens::Ptr& lens);
272 gsize FindLensPriority (Lens::Ptr& lens);
273 void EnsureCategoryAnnotation(Lens::Ptr& lens, DeeModel* results, DeeModel* categories);
274 Lens::Ptr FindLensForUri(std::string const& uri);
275
276 HomeLens* owner_;
277 Lenses::LensList lenses_;
278 HomeLens::CategoryRegistry cat_registry_;
279 HomeLens::ResultsMerger results_merger_;
280 HomeLens::CategoryMerger categories_merger_;
281 HomeLens::ModelMerger filters_merger_;
282 int running_searches_;
283 glib::Object<GSettings> settings_;
284};
285
286/*
287 * IMPLEMENTATION
288 */
289
290HomeLens::ModelMerger::ModelMerger(glib::Object<DeeModel> target)
291 : row_buf_(NULL)
292 , n_cols_(0)
293 , target_(target)
294{}
295
296HomeLens::ResultsMerger::ResultsMerger(glib::Object<DeeModel> target,
297 CategoryRegistry *cat_registry)
298 : HomeLens::ModelMerger::ModelMerger(target)
299 , cat_registry_(cat_registry)
300{}
301
302HomeLens::CategoryMerger::CategoryMerger(glib::Object<DeeModel> target,
303 CategoryRegistry *cat_registry)
304 : HomeLens::ModelMerger::ModelMerger(target)
305 , cat_registry_(cat_registry)
306 , priority_tag_(dee_model_register_tag(target, NULL))
307{}
308
309HomeLens::ModelMerger::~ModelMerger()
310{
311 if (row_buf_)
312 delete row_buf_;
313}
314
315void HomeLens::ModelMerger::AddSource(glib::Object<DeeModel> source)
316{
317 typedef glib::Signal<void, DeeModel*, DeeModelIter*> RowSignalType;
318
319 if (!source)
320 {
321 LOG_ERROR(logger) << "Trying to add NULL source to ModelMerger";
322 return;
323 }
324
325 DeeModelTag* merger_tag = dee_model_register_tag(source, NULL);
326 source_to_target_tags_[source.RawPtr()] = merger_tag;
327
328 sig_manager_.Add(new RowSignalType(source.RawPtr(),
329 "row-added",
330 sigc::mem_fun(this, &HomeLens::ModelMerger::OnSourceRowAdded)));
331
332 sig_manager_.Add(new RowSignalType(source.RawPtr(),
333 "row-removed",
334 sigc::mem_fun(this, &HomeLens::ModelMerger::OnSourceRowRemoved)));
335
336 sig_manager_.Add(new RowSignalType(source.RawPtr(),
337 "row-changed",
338 sigc::mem_fun(this, &HomeLens::ModelMerger::OnSourceRowChanged)));
339}
340
341void HomeLens::ModelMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter)
342{
343 // Default impl. does nothing.
344 // Note that the filters_merger_ relies on this behavior. Supporting
345 // filters on the home screen is possible, but *quite* tricky.
346 // So...
347 // Discard ALL the rows!
348}
349
350void HomeLens::ResultsMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter)
351{
352 DeeModelIter* target_iter;
353 DeeModelTag* target_tag;
354 int target_cat_offset, source_cat_offset;
355 const unsigned int CATEGORY_COLUMN = 2;
356
357 EnsureRowBuf(model);
358 CheckCategoryRegistryDirty();
359
360 dee_model_get_row (model, iter, row_buf_);
361 target_tag = FindSourceToTargetTag(model);
362
363 /* Update the row with the corrected category offset */
364 source_cat_offset = dee_model_get_uint32(model, iter, CATEGORY_COLUMN);
365 target_cat_offset = cat_registry_->FindCategoryOffset(model, source_cat_offset);
366
367 if (target_cat_offset >= 0)
368 {
369 g_variant_unref (row_buf_[CATEGORY_COLUMN]);
370 row_buf_[CATEGORY_COLUMN] = g_variant_new_uint32(target_cat_offset);
371
372 /* Sink the ref on the new row member. By Dee API contract they must all
373 * be strong refs, not floating */
374 g_variant_ref_sink(row_buf_[CATEGORY_COLUMN]);
375
376 target_iter = dee_model_append_row (target_, row_buf_);
377 dee_model_set_tag(model, iter, target_tag, target_iter);
378
379 LOG_DEBUG(logger) << "Found " << dee_model_get_string(model, iter, 0)
380 << " (source cat " << source_cat_offset << ", target cat "
381 << target_cat_offset << ")";
382 }
383 else
384 {
385 LOG_ERROR(logger) << "No category registered for model "
386 << model << ", source offset " << source_cat_offset
387 << ": " << dee_model_get_string(model, iter, 0);
388 }
389
390 for (unsigned int i = 0; i < n_cols_; i++) g_variant_unref(row_buf_[i]);
391}
392
393void HomeLens::CategoryMerger::OnSourceRowAdded(DeeModel *model, DeeModelIter *iter)
394{
395 DeeModel* results_model;
396 DeeModelIter* target_iter;
397 DeeModelIter* target_end;
398 DeeModelTag* target_tag;
399 int target_cat_offset, source_cat_offset;
400 const gchar* display_name;
401 const unsigned int DISPLAY_NAME_COLUMN = 0;
402 gsize lens_priority, prio;
403
404 EnsureRowBuf(model);
405
406 results_model = static_cast<DeeModel*>(g_object_get_data(
407 G_OBJECT(model), "unity-homelens-results-model"));
408 if (results_model == NULL)
409 {
410 LOG_DEBUG(logger) << "Category model " << model
411 << " does not have a results model yet";
412 return;
413 }
414
415 dee_model_get_row (model, iter, row_buf_);
416 target_tag = FindSourceToTargetTag(model);
417 source_cat_offset = dee_model_get_position(model, iter);
418
419 /* If we already have a category registered with the same display name
420 * then we just use that. Otherwise register a new category for it */
421 display_name = dee_model_get_string(model, iter, DISPLAY_NAME_COLUMN);
422 target_cat_offset = cat_registry_->FindCategoryOffset(display_name);
423 if (target_cat_offset >= 0)
424 {
425 cat_registry_->AssociateCategoryOffset(results_model, source_cat_offset,
426 target_cat_offset);
427 goto cleanup;
428 }
429
430 /*
431 * Below we can assume that we have a genuinely new category.
432 *
433 * Our goal is to insert the category at a position suitable for its
434 * priority. We insert it as the last item in the set of items which
435 * have equal priority.
436 *
437 * We allow our selves to do linear inserts as we wont expect a lot
438 * of categories.
439 */
440
441 lens_priority = GPOINTER_TO_SIZE(g_object_get_data(
442 G_OBJECT(model), "unity-homelens-priority"));
443
444 /* Seek correct position in the merged category model */
445 target_iter = dee_model_get_first_iter(target_);
446 target_end = dee_model_get_last_iter(target_);
447 while (target_iter != target_end)
448 {
449 prio = GPOINTER_TO_SIZE(dee_model_get_tag(target_, target_iter, priority_tag_));
450 if (lens_priority > prio)
451 break;
452 target_iter = dee_model_next(target_, target_iter);
453 }
454
455 /* Add the row to the merged categories model and store required metadata */
456 target_iter = dee_model_insert_row_before(target_, target_iter, row_buf_);
457 dee_model_set_tag(model, iter, target_tag, target_iter);
458 dee_model_set_tag(target_, target_iter, priority_tag_, GSIZE_TO_POINTER(lens_priority));
459 target_cat_offset = dee_model_get_position(target_, target_iter);
460 cat_registry_->RegisterCategoryOffset(results_model, source_cat_offset,
461 display_name, target_cat_offset);
462
463 cleanup:
464 for (unsigned int i = 0; i < n_cols_; i++) g_variant_unref(row_buf_[i]);
465}
466
467void HomeLens::CategoryMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter)
468{
469 /* We don't support removals of categories.
470 * You can check out any time you like, but you can never leave
471 *
472 * The category registry code is spaghettified enough already.
473 * No more please.
474 */
475 LOG_DEBUG(logger) << "Removal of categories not supported.";
476}
477
478void HomeLens::ModelMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter)
479{
480 DeeModelIter* target_iter;
481 DeeModelTag* target_tag;
482
483 EnsureRowBuf(model);
484
485 target_tag = FindSourceToTargetTag(model);
486 target_iter = static_cast<DeeModelIter*>(dee_model_get_tag(model,
487 iter,
488 target_tag));
489
490 /* We might not have registered a target iter for the row.
491 * This fx. happens if we re-used a category based on display_name */
492 if (target_iter != NULL)
493 dee_model_remove(target_, target_iter);
494}
495
496void HomeLens::ResultsMerger::OnSourceRowRemoved(DeeModel *model, DeeModelIter *iter)
497{
498 CheckCategoryRegistryDirty();
499 ModelMerger::OnSourceRowRemoved(model, iter);
500}
501
502void HomeLens::ModelMerger::OnSourceRowChanged(DeeModel *model, DeeModelIter *iter)
503{
504 DeeModelIter* target_iter;
505 DeeModelTag* target_tag;
506
507 EnsureRowBuf(model);
508
509 dee_model_get_row (model, iter, row_buf_);
510 target_tag = FindSourceToTargetTag(model);
511 target_iter = static_cast<DeeModelIter*>(dee_model_get_tag(model,
512 iter,
513 target_tag));
514
515 dee_model_set_row (target_, target_iter, row_buf_);
516
517 for (unsigned int i = 0; i < n_cols_; i++) g_variant_unref(row_buf_[i]);
518}
519
520void HomeLens::ResultsMerger::OnSourceRowChanged(DeeModel *model, DeeModelIter *iter)
521{
522 // FIXME: We can support this, but we need to re-calculate the category offset
523 LOG_WARN(logger) << "In-line changing of results not supported in the home lens. Sorry.";
524}
525
526void HomeLens::ModelMerger::EnsureRowBuf(DeeModel *model)
527{
528 if (G_UNLIKELY (n_cols_ == 0))
529 {
530 /* We have two things to accomplish here.
531 * 1) Allocate the row_buf_, and
532 * 2) Make sure that the target model has the correct schema set.
533 *
534 * INVARIANT: n_cols_ == 0 iff row_buf_ == NULL.
535 */
536
537 n_cols_ = dee_model_get_n_columns(model);
538
539 if (n_cols_ == 0)
540 {
541 LOG_ERROR(logger) << "Source model has not provided a schema for the model merger!";
542 return;
543 }
544
545 /* Lazily adopt schema from source if we don't have one.
546 * If we do have a schema let's validate that they match the source */
547 if (dee_model_get_n_columns(target_) == 0)
548 {
549 dee_model_set_schema_full(target_,
550 dee_model_get_schema(model, NULL),
551 n_cols_);
552 }
553 else
554 {
555 unsigned int n_cols1;
556 const gchar* const *schema1 = dee_model_get_schema(target_, &n_cols1);
557 const gchar* const *schema2 = dee_model_get_schema(model, NULL);
558
559 /* At the very least we should have an equal number of rows */
560 if (n_cols_ != n_cols1)
561 {
562 LOG_ERROR(logger) << "Schema mismatch between source and target model. Expected "
563 << n_cols1 << " columns, but found "
564 << n_cols_ << ".";
565 n_cols_ = 0;
566 return;
567 }
568
569 /* Compare schemas */
570 for (unsigned int i = 0; i < n_cols_; i++)
571 {
572 if (g_strcmp0(schema1[i], schema2[i]) != 0)
573 {
574 LOG_ERROR(logger) << "Schema mismatch between source and target model. Expected column "
575 << i << " to be '" << schema1[i] << "', but found '"
576 << schema2[i] << "'.";
577 n_cols_ = 0;
578 return;
579 }
580 }
581 }
582
583 row_buf_ = g_new0 (GVariant*, n_cols_);
584 }
585}
586
587DeeModelTag* HomeLens::ModelMerger::FindSourceToTargetTag(DeeModel *model)
588{
589 return source_to_target_tags_[model];
590}
591
592void HomeLens::ResultsMerger::CheckCategoryRegistryDirty()
593{
594 DeeModel* source;
595 DeeModelTag* target_tag;
596 const unsigned int CATEGORY_COLUMN = 2;
597 std::map<DeeModel*,DeeModelTag*>::iterator i, end;
598
599 if (G_LIKELY(!cat_registry_->CheckDirty()))
600 return;
601
602 LOG_DEBUG(logger) << "Category registry marked dirty. Fixing category offsets.";
603
604 /*
605 * Iterate over all results in each source model and re-calculate the
606 * the category offset in the corresponding rows in the target model
607 */
608 for (i = source_to_target_tags_.begin(), end = source_to_target_tags_.end();
609 i != end; i++)
610 {
611 source = i->first;
612 target_tag = i->second;
613
614 DeeModelIter* source_iter = dee_model_get_first_iter(source);
615 DeeModelIter* source_end = dee_model_get_last_iter(source);
616
617 for (source_iter = dee_model_get_first_iter(source), source_end = dee_model_get_last_iter(source);
618 source_iter != source_end;
619 source_iter = dee_model_next(source, source_iter))
620 {
621 DeeModelIter* target_iter = static_cast<DeeModelIter*>(dee_model_get_tag(source, source_iter, target_tag));
622
623 /* No guarantee that rows in the source are mapped to the target */
624 if (target_iter == NULL)
625 continue;
626
627 unsigned int source_cat_offset = dee_model_get_uint32(source, source_iter, CATEGORY_COLUMN);
628 int cat_offset = cat_registry_->FindCategoryOffset(source, source_cat_offset);
629
630 if (G_LIKELY(cat_offset >= 0))
631 {
632 dee_model_set_value(target_, target_iter, CATEGORY_COLUMN,
633 g_variant_new_uint32(cat_offset));
634 }
635 else
636 {
637 LOG_ERROR(logger) << "No registered category id for category "
638 << source_cat_offset << " on result source model "
639 << source << ".";
640 /* We can't really recover from this :-( */
641 }
642 }
643 }
644}
645
646HomeLens::Impl::Impl(HomeLens *owner)
647 : owner_(owner)
648 , cat_registry_(owner)
649 , results_merger_(owner->results()->model(), &cat_registry_)
650 , categories_merger_(owner->categories()->model(), &cat_registry_)
651 , filters_merger_(owner->filters()->model())
652 , running_searches_(0)
653 , settings_(g_settings_new("com.canonical.Unity.Dash"))
654{
655 DeeModel* results = owner->results()->model();
656 if (dee_model_get_n_columns(results) == 0)
657 {
658 dee_model_set_schema(results, "s", "s", "u", "s", "s", "s", "s", NULL);
659 }
660
661 DeeModel* categories = owner->categories()->model();
662 if (dee_model_get_n_columns(categories) == 0)
663 {
664 dee_model_set_schema(categories, "s", "s", "s", "a{sv}", NULL);
665 }
666
667 DeeModel* filters = owner->filters()->model();
668 if (dee_model_get_n_columns(filters) == 0)
669 {
670 dee_model_set_schema(filters, "s", "s", "s", "s", "a{sv}", "b", "b", "b", NULL);
671 }
672}
673
674HomeLens::Impl::~Impl()
675{
676
677}
678
679/*void HomeLens::Impl::CheckCategories()
680{
681
682}*/
683
684gsize HomeLens::Impl::FindLensPriority (Lens::Ptr& lens)
685{
686 gchar** lenses = g_settings_get_strv(settings_, "home-lens-ordering");
687 gsize pos = 0, len = g_strv_length(lenses);
688
689 for (pos = 0; pos < len; pos++)
690 {
691 if (g_strcmp0(lenses[pos], lens->id().c_str()) == 0)
692 break;
693 }
694
695 g_strfreev(lenses);
696
697 return len - pos;
698}
699
700void HomeLens::Impl::EnsureCategoryAnnotation (Lens::Ptr& lens,
701 DeeModel* categories,
702 DeeModel* results)
703{
704 if (categories && results)
705 {
706 if (!(DEE_IS_MODEL(results) && DEE_IS_MODEL(categories)))
707 {
708 LOG_ERROR(logger) << "The "
709 << std::string(DEE_IS_MODEL(results) ? "categories" : "results")
710 << " model is not a valid DeeModel. ("
711 << lens->id() << ")";
712 return;
713 }
714
715 g_object_set_data(G_OBJECT(categories),
716 "unity-homelens-results-model",
717 results);
718
719 gsize lens_priority = FindLensPriority(lens);
720 g_object_set_data(G_OBJECT(categories),
721 "unity-homelens-priority",
722 GSIZE_TO_POINTER(lens_priority));
723
724 LOG_DEBUG(logger) << "Registering results model " << results
725 << " and lens priority " << lens_priority
726 << " on category model " << categories << ". ("
727 << lens->id() << ")";
728 }
729}
730
731Lens::Ptr HomeLens::Impl::FindLensForUri(std::string const& uri)
732{
733 /* We iterate over all lenses looking for the given uri in their
734 * global results. This might seem like a sucky approach, but it
735 * saves us from a ship load of book keeping */
736
737 for (auto lens : lenses_)
738 {
739 DeeModel* results = lens->global_results()->model();
740 DeeModelIter* iter = dee_model_get_first_iter(results);
741 DeeModelIter* end = dee_model_get_last_iter(results);
742 const int URI_COLUMN = 0;
743
744 while (iter != end)
745 {
746 if (g_strcmp0(uri.c_str(), dee_model_get_string(results, iter, URI_COLUMN)) == 0)
747 {
748 return lens;
749 }
750 iter = dee_model_next(results, iter);
751 }
752 }
753
754 return Lens::Ptr();
755}
756
757// FIXME: Coordinated sorting between the lens bar and home screen categories. Make void FilesystemLenses::Impl::DecrementAndCheckChildrenWaiting() use the gsettings key
758// FIXME: on no results https://bugs.launchpad.net/unity/+bug/711199
759
760void HomeLens::Impl::OnLensAdded (Lens::Ptr& lens)
761{
762 lenses_.push_back (lens);
763 owner_->lens_added.emit(lens);
764
765 /* When we dispatch a search we inc the search count and when we finish
766 * one we decrease it. When we reach 0 we'll emit search_finished. */
767 lens->global_search_finished.connect([&] (Hints const& hints) {
768 running_searches_--;
769
770 if (running_searches_ <= 0)
771 {
772 owner_->search_finished.emit(Hints());
773 LOG_DEBUG(logger) << "Search finished";
774 }
775 });
776
777 nux::ROProperty<glib::Object<DeeModel>>& results_prop = lens->global_results()->model;
778 nux::ROProperty<glib::Object<DeeModel>>& categories_prop = lens->categories()->model;
779 nux::ROProperty<glib::Object<DeeModel>>& filters_prop = lens->filters()->model;
780
781 /*
782 * Important: We must ensure that the categories model is annotated
783 * with the results model in the "unity-homelens-results-model"
784 * data slot. We need it later to compute the transfermed offsets
785 * of the categories in the merged category model.
786 */
787
788 /* Most lenses add models lazily, but we can't know that;
789 * so try to see if we can add them up front */
790 if (results_prop().RawPtr())
791 {
792 EnsureCategoryAnnotation(lens, categories_prop(), results_prop());
793 results_merger_.AddSource(results_prop());
794 }
795
796 if (categories_prop().RawPtr())
797 {
798 EnsureCategoryAnnotation(lens, categories_prop(), results_prop());
799 categories_merger_.AddSource(categories_prop());
800 }
801
802 if (filters_prop().RawPtr())
803 filters_merger_.AddSource(filters_prop());
804
805 /*
806 * Pick it up when the lens set models lazily.
807 */
808 results_prop.changed.connect([&] (glib::Object<DeeModel> model)
809 {
810 EnsureCategoryAnnotation(lens, lens->categories()->model(), model);
811 results_merger_.AddSource(model);
812 });
813
814 categories_prop.changed.connect([&] (glib::Object<DeeModel> model)
815 {
816 EnsureCategoryAnnotation(lens, model, lens->global_results()->model());
817 categories_merger_.AddSource(model);
818 });
819
820 filters_prop.changed.connect([&] (glib::Object<DeeModel> model)
821 {
822 filters_merger_.AddSource(model);
823 });
824
825 /*
826 * Register pre-existing categories up front
827 * FIXME: Do the same for results?
828 */
829 DeeModel* cats = categories_prop();
830 DeeModelIter* cats_iter;
831 DeeModelIter* cats_end;
832 for (cats_iter = dee_model_get_first_iter(cats), cats_end = dee_model_get_last_iter(cats);
833 cats_iter != cats_end;
834 cats_iter = dee_model_next(cats, cats_iter))
835 {
836 categories_merger_.OnSourceRowAdded(cats, cats_iter);
837 }
838}
839
840HomeLens::HomeLens(std::string const& name, std::string const& description, std::string const& search_hint)
841 : Lens("home.lens", "", "", name, PKGDATADIR"/lens-nav-home.svg",
842 description, search_hint, true, "",
843 ModelType::LOCAL)
844 , pimpl(new Impl(this))
845{
846 count.SetGetterFunction(sigc::mem_fun(&pimpl->lenses_, &Lenses::LensList::size));
847 search_in_global = false;
848}
849
850HomeLens::~HomeLens()
851{
852 delete pimpl;
853}
854
855void HomeLens::AddLenses(Lenses& lenses)
856{
857 for (auto lens : lenses.GetLenses())
858 {
859 pimpl->OnLensAdded(lens);
860 }
861
862 lenses.lens_added.connect(sigc::mem_fun(pimpl, &HomeLens::Impl::OnLensAdded));
863}
864
865Lenses::LensList HomeLens::GetLenses() const
866{
867 return pimpl->lenses_;
868}
869
870Lens::Ptr HomeLens::GetLens(std::string const& lens_id) const
871{
872 for (auto lens: pimpl->lenses_)
873 {
874 if (lens->id == lens_id)
875 {
876 return lens;
877 }
878 }
879
880 return Lens::Ptr();
881}
882
883Lens::Ptr HomeLens::GetLensAtIndex(std::size_t index) const
884{
885 try
886 {
887 return pimpl->lenses_.at(index);
888 }
889 catch (std::out_of_range& error)
890 {
891 LOG_WARN(logger) << error.what();
892 }
893
894 return Lens::Ptr();
895}
896
897void HomeLens::GlobalSearch(std::string const& search_string)
898{
899 LOG_WARN(logger) << "Global search not enabled for HomeLens class."
900 << " Ignoring query '" << search_string << "'";
901}
902
903void HomeLens::Search(std::string const& search_string)
904{
905 LOG_DEBUG(logger) << "Search '" << search_string << "'";
906
907 /* Reset running search counter */
908 pimpl->running_searches_ = 0;
909
910 for (auto lens: pimpl->lenses_)
911 {
912 if (lens->search_in_global())
913 {
914 LOG_DEBUG(logger) << " - Global search on '" << lens->id() << "' for '"
915 << search_string << "'";
916 lens->view_type = ViewType::HOME_VIEW;
917 lens->GlobalSearch(search_string);
918 pimpl->running_searches_++;
919 }
920 }
921}
922
923void HomeLens::Activate(std::string const& uri)
924{
925 LOG_DEBUG(logger) << "Activate '" << uri << "'";
926
927 Lens::Ptr lens = pimpl->FindLensForUri(uri);
928
929 /* Fall back to default handling of URIs if no lens is found.
930 * - Although, this shouldn't really happen */
931 if (lens)
932 {
933 LOG_DEBUG(logger) << "Activation request passed to '" << lens->id() << "'";
934 lens->Activate(uri);
935 }
936 else
937 {
938 LOG_WARN(logger) << "Unable to find a lens for activating '" << uri
939 << "'. Using fallback activation.";
940 activated.emit(uri, HandledType::NOT_HANDLED, Hints());
941 }
942}
943
944void HomeLens::Preview(std::string const& uri)
945{
946 LOG_DEBUG(logger) << "Preview '" << uri << "'";
947
948 Lens::Ptr lens = pimpl->FindLensForUri(uri);
949
950 if (lens)
951 lens->Preview(uri);
952 else
953 LOG_WARN(logger) << "Unable to find a lens for previewing '" << uri << "'";
954}
955
956}
957}
0958
=== added file 'UnityCore/HomeLens.h'
--- UnityCore/HomeLens.h 1970-01-01 00:00:00 +0000
+++ UnityCore/HomeLens.h 2012-01-26 08:37:26 +0000
@@ -0,0 +1,79 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3 * Copyright (C) 2012 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Mikkel Kamstrup Erlandsen <mikkel.kamstrup@canonical.com>
18 */
19
20#ifndef UNITY_HOME_LENS_H
21#define UNITY_HOME_LENS_H
22
23#include <vector>
24#include <memory>
25#include <sigc++/signal.h>
26#include <sigc++/trackable.h>
27
28#include "Lenses.h"
29#include "Lens.h"
30
31namespace unity
32{
33namespace dash
34{
35
36/**
37 * A special Lens implementation that merges together a set of source Lens
38 * instances.
39 *
40 * NOTE: Changes in the filter models are currently not propagated back to the
41 * the source lenses. If we want to support filters on the dash home
42 * screen this needs to be addressed.
43 */
44class HomeLens : public Lens, public Lenses
45{
46public:
47 typedef std::shared_ptr<HomeLens> Ptr;
48
49 /**
50 * Should be constructed with i18n arguments:
51 * _("Home"), _("Home screen"), _("Search")
52 */
53 HomeLens(std::string const& name, std::string const& description, std::string const& search_hint);
54 virtual ~HomeLens();
55
56 void AddLenses(Lenses& lenses);
57
58 Lenses::LensList GetLenses() const;
59 Lens::Ptr GetLens(std::string const& lens_id) const;
60 Lens::Ptr GetLensAtIndex(std::size_t index) const;
61
62 void GlobalSearch(std::string const& search_string);
63 void Search(std::string const& search_string);
64 void Activate(std::string const& uri);
65 void Preview(std::string const& uri);
66
67private:
68 class Impl;
69 class ModelMerger;
70 class ResultsMerger;
71 class CategoryMerger;
72 class CategoryRegistry;
73 Impl* pimpl;
74};
75
76}
77}
78
79#endif
080
=== modified file 'UnityCore/Lens.cpp'
--- UnityCore/Lens.cpp 2012-01-17 16:02:39 +0000
+++ UnityCore/Lens.cpp 2012-01-26 08:37:26 +0000
@@ -51,11 +51,12 @@
51 string const& description,51 string const& description,
52 string const& search_hint,52 string const& search_hint,
53 bool visible,53 bool visible,
54 string const& shortcut);54 string const& shortcut,
55 ModelType model_type);
5556
56 ~Impl();57 ~Impl();
5758
58 void OnProxyConnected();59 void OnProxyConnectionChanged();
59 void OnProxyDisconnected();60 void OnProxyDisconnected();
6061
61 void ResultsModelUpdated(unsigned long long begin_seqnum, 62 void ResultsModelUpdated(unsigned long long begin_seqnum,
@@ -94,6 +95,7 @@
94 string const& search_hint() const;95 string const& search_hint() const;
95 bool visible() const;96 bool visible() const;
96 bool search_in_global() const;97 bool search_in_global() const;
98 bool set_search_in_global(bool val);
97 string const& shortcut() const;99 string const& shortcut() const;
98 Results::Ptr const& results() const;100 Results::Ptr const& results() const;
99 Results::Ptr const& global_results() const;101 Results::Ptr const& global_results() const;
@@ -121,7 +123,7 @@
121123
122 string private_connection_name_;124 string private_connection_name_;
123125
124 glib::DBusProxy proxy_;126 glib::DBusProxy* proxy_;
125 glib::Object<GCancellable> search_cancellable_;127 glib::Object<GCancellable> search_cancellable_;
126 glib::Object<GCancellable> global_search_cancellable_;128 glib::Object<GCancellable> global_search_cancellable_;
127129
@@ -138,7 +140,8 @@
138 string const& description,140 string const& description,
139 string const& search_hint,141 string const& search_hint,
140 bool visible,142 bool visible,
141 string const& shortcut)143 string const& shortcut,
144 ModelType model_type)
142 : owner_(owner)145 : owner_(owner)
143 , id_(id)146 , id_(id)
144 , dbus_name_(dbus_name)147 , dbus_name_(dbus_name)
@@ -150,20 +153,46 @@
150 , visible_(visible)153 , visible_(visible)
151 , search_in_global_(false)154 , search_in_global_(false)
152 , shortcut_(shortcut)155 , shortcut_(shortcut)
153 , results_(new Results())156 , results_(new Results(model_type))
154 , global_results_(new Results())157 , global_results_(new Results(model_type))
155 , categories_(new Categories())158 , categories_(new Categories(model_type))
156 , filters_(new Filters())159 , filters_(new Filters(model_type))
157 , connected_(false)160 , connected_(false)
158 , proxy_(dbus_name, dbus_path, "com.canonical.Unity.Lens")161 , proxy_(NULL)
159 , results_variant_(NULL)162 , results_variant_(NULL)
160 , global_results_variant_(NULL)163 , global_results_variant_(NULL)
161{164{
162 proxy_.connected.connect(sigc::mem_fun(this, &Lens::Impl::OnProxyConnected));165 if (model_type == ModelType::REMOTE)
163 proxy_.disconnected.connect(sigc::mem_fun(this, &Lens::Impl::OnProxyDisconnected));166 {
164 proxy_.Connect("Changed", sigc::mem_fun(this, &Lens::Impl::OnChanged));167 proxy_ = new glib::DBusProxy(dbus_name, dbus_path, "com.canonical.Unity.Lens");
168 proxy_->connected.connect(sigc::mem_fun(this, &Lens::Impl::OnProxyConnectionChanged));
169 proxy_->disconnected.connect(sigc::mem_fun(this, &Lens::Impl::OnProxyDisconnected));
170 proxy_->Connect("Changed", sigc::mem_fun(this, &Lens::Impl::OnChanged));
171 }
172
173 /* Technically these signals will only be fired by remote models, but we
174 * connect them no matter the ModelType. Dee may grow support in the future.
175 */
165 results_->end_transaction.connect(sigc::mem_fun(this, &Lens::Impl::ResultsModelUpdated));176 results_->end_transaction.connect(sigc::mem_fun(this, &Lens::Impl::ResultsModelUpdated));
166 global_results_->end_transaction.connect(sigc::mem_fun(this, &Lens::Impl::GlobalResultsModelUpdated));177 global_results_->end_transaction.connect(sigc::mem_fun(this, &Lens::Impl::GlobalResultsModelUpdated));
178
179 owner_->id.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::id));
180 owner_->dbus_name.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::dbus_name));
181 owner_->dbus_path.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::dbus_path));
182 owner_->name.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::name));
183 owner_->icon_hint.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::icon_hint));
184 owner_->description.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::description));
185 owner_->search_hint.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::search_hint));
186 owner_->visible.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::visible));
187 owner_->search_in_global.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::search_in_global));
188 owner_->search_in_global.SetSetterFunction(sigc::mem_fun(this, &Lens::Impl::set_search_in_global));
189 owner_->shortcut.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::shortcut));
190 owner_->results.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::results));
191 owner_->global_results.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::global_results));
192 owner_->categories.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::categories));
193 owner_->filters.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::filters));
194 owner_->connected.SetGetterFunction(sigc::mem_fun(this, &Lens::Impl::connected));
195 owner_->view_type.changed.connect(sigc::mem_fun(this, &Lens::Impl::OnViewTypeChanged));
167}196}
168197
169Lens::Impl::~Impl()198Lens::Impl::~Impl()
@@ -176,13 +205,19 @@
176 {205 {
177 g_cancellable_cancel (global_search_cancellable_);206 g_cancellable_cancel (global_search_cancellable_);
178 }207 }
208
209 if (proxy_)
210 delete proxy_;
179}211}
180212
181void Lens::Impl::OnProxyConnected()213void Lens::Impl::OnProxyConnectionChanged()
182{214{
183 proxy_.Call("InfoRequest");215 if (proxy_->IsConnected())
184 ViewType current_view_type = owner_->view_type;216 {
185 proxy_.Call("SetViewType", g_variant_new("(u)", current_view_type));217 proxy_->Call("InfoRequest");
218 ViewType current_view_type = owner_->view_type;
219 proxy_->Call("SetViewType", g_variant_new("(u)", current_view_type));
220 }
186}221}
187222
188void Lens::Impl::OnProxyDisconnected()223void Lens::Impl::OnProxyDisconnected()
@@ -387,12 +422,13 @@
387422
388void Lens::Impl::OnViewTypeChanged(ViewType view_type)423void Lens::Impl::OnViewTypeChanged(ViewType view_type)
389{424{
390 proxy_.Call("SetViewType", g_variant_new("(u)", view_type));425 if (proxy_ && proxy_->IsConnected())
426 proxy_->Call("SetViewType", g_variant_new("(u)", view_type));
391}427}
392428
393void Lens::Impl::GlobalSearch(std::string const& search_string)429void Lens::Impl::GlobalSearch(std::string const& search_string)
394{430{
395 LOG_DEBUG(logger) << "Global Searching " << id_ << " for " << search_string;431 LOG_DEBUG(logger) << "Global Searching '" << id_ << "' for '" << search_string << "'";
396432
397 GVariantBuilder b;433 GVariantBuilder b;
398 g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));434 g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
@@ -407,7 +443,7 @@
407 global_results_variant_ = NULL;443 global_results_variant_ = NULL;
408 }444 }
409445
410 proxy_.Call("GlobalSearch",446 proxy_->Call("GlobalSearch",
411 g_variant_new("(sa{sv})",447 g_variant_new("(sa{sv})",
412 search_string.c_str(),448 search_string.c_str(),
413 &b),449 &b),
@@ -418,7 +454,13 @@
418454
419void Lens::Impl::Search(std::string const& search_string)455void Lens::Impl::Search(std::string const& search_string)
420{456{
421 LOG_DEBUG(logger) << "Searching " << id_ << " for " << search_string;457 LOG_DEBUG(logger) << "Searching '" << id_ << "' for '" << search_string << "'";
458
459 if (!proxy_->IsConnected())
460 {
461 LOG_DEBUG(logger) << "Skipping search. Proxy not connected. ('" << id_ << "')";
462 return;
463 }
422464
423 GVariantBuilder b;465 GVariantBuilder b;
424 g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));466 g_variant_builder_init(&b, G_VARIANT_TYPE("a{sv}"));
@@ -432,23 +474,29 @@
432 results_variant_ = NULL;474 results_variant_ = NULL;
433 }475 }
434476
435 proxy_.Call("Search",477 proxy_->Call("Search",
436 g_variant_new("(sa{sv})",478 g_variant_new("(sa{sv})",
437 search_string.c_str(),479 search_string.c_str(),
438 &b),480 &b),
439 sigc::mem_fun(this, &Lens::Impl::OnSearchFinished),481 sigc::mem_fun(this, &Lens::Impl::OnSearchFinished),
440 search_cancellable_);482 search_cancellable_);
441483
442 g_variant_builder_clear(&b);484 g_variant_builder_clear(&b);
443}485}
444486
445void Lens::Impl::Activate(std::string const& uri)487void Lens::Impl::Activate(std::string const& uri)
446{488{
447 LOG_DEBUG(logger) << "Activating " << uri << " on " << id_;489 LOG_DEBUG(logger) << "Activating '" << uri << "' on '" << id_ << "'";
448490
449 proxy_.Call("Activate",491 if (!proxy_->IsConnected())
450 g_variant_new("(su)", uri.c_str(), 0),492 {
451 sigc::mem_fun(this, &Lens::Impl::ActivationReply));493 LOG_DEBUG(logger) << "Skipping activation. Proxy not connected. ('" << id_ << "')";
494 return;
495 }
496
497 proxy_->Call("Activate",
498 g_variant_new("(su)", uri.c_str(), 0),
499 sigc::mem_fun(this, &Lens::Impl::ActivationReply));
452}500}
453501
454void Lens::Impl::ActivationReply(GVariant* parameters)502void Lens::Impl::ActivationReply(GVariant* parameters)
@@ -468,11 +516,17 @@
468516
469void Lens::Impl::Preview(std::string const& uri)517void Lens::Impl::Preview(std::string const& uri)
470{518{
471 LOG_DEBUG(logger) << "Previewing " << uri << " on " << id_;519 LOG_DEBUG(logger) << "Previewing '" << uri << "' on '" << id_ << "'";
472520
473 proxy_.Call("Preview",521 if (!proxy_->IsConnected())
474 g_variant_new("(s)", uri.c_str()),522 {
475 sigc::mem_fun(this, &Lens::Impl::PreviewReply));523 LOG_DEBUG(logger) << "Skipping preview. Proxy not connected. ('" << id_ << "')";
524 return;
525 }
526
527 proxy_->Call("Preview",
528 g_variant_new("(s)", uri.c_str()),
529 sigc::mem_fun(this, &Lens::Impl::PreviewReply));
476}530}
477531
478void Lens::Impl::PreviewReply(GVariant* parameters)532void Lens::Impl::PreviewReply(GVariant* parameters)
@@ -535,6 +589,17 @@
535 return search_in_global_;589 return search_in_global_;
536}590}
537591
592bool Lens::Impl::set_search_in_global(bool val)
593{
594 if (search_in_global_ != val)
595 {
596 search_in_global_ = val;
597 owner_->search_in_global.EmitChanged(val);
598 }
599
600 return search_in_global_;
601}
602
538string const& Lens::Impl::shortcut() const603string const& Lens::Impl::shortcut() const
539{604{
540 return shortcut_;605 return shortcut_;
@@ -584,25 +649,33 @@
584 description_,649 description_,
585 search_hint_,650 search_hint_,
586 visible_,651 visible_,
587 shortcut_))652 shortcut_,
588{653 ModelType::REMOTE))
589 id.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::id));654{}
590 dbus_name.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::dbus_name));655
591 dbus_path.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::dbus_path));656Lens::Lens(string const& id_,
592 name.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::name));657 string const& dbus_name_,
593 icon_hint.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::icon_hint));658 string const& dbus_path_,
594 description.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::description));659 string const& name_,
595 search_hint.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::search_hint));660 string const& icon_hint_,
596 visible.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::visible));661 string const& description_,
597 search_in_global.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::search_in_global));662 string const& search_hint_,
598 shortcut.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::shortcut));663 bool visible_,
599 results.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::results));664 string const& shortcut_,
600 global_results.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::global_results));665 ModelType model_type)
601 categories.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::categories));666
602 filters.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::filters));667 : pimpl(new Impl(this,
603 connected.SetGetterFunction(sigc::mem_fun(pimpl, &Lens::Impl::connected));668 id_,
604 view_type.changed.connect(sigc::mem_fun(pimpl, &Lens::Impl::OnViewTypeChanged));669 dbus_name_,
605}670 dbus_path_,
671 name_,
672 icon_hint_,
673 description_,
674 search_hint_,
675 visible_,
676 shortcut_,
677 model_type))
678{}
606679
607Lens::~Lens()680Lens::~Lens()
608{681{
609682
=== modified file 'UnityCore/Lens.h'
--- UnityCore/Lens.h 2012-01-17 13:31:52 +0000
+++ UnityCore/Lens.h 2012-01-26 08:37:26 +0000
@@ -67,12 +67,23 @@
67 bool visible = true,67 bool visible = true,
68 std::string const& shortcut = "");68 std::string const& shortcut = "");
6969
70 ~Lens();70 Lens(std::string const& id,
7171 std::string const& dbus_name,
72 void GlobalSearch(std::string const& search_string);72 std::string const& dbus_path,
73 void Search(std::string const& search_string);73 std::string const& name,
74 void Activate(std::string const& uri);74 std::string const& icon,
75 void Preview(std::string const& uri);75 std::string const& description,
76 std::string const& search_hint,
77 bool visible,
78 std::string const& shortcut,
79 ModelType model_type);
80
81 virtual ~Lens();
82
83 virtual void GlobalSearch(std::string const& search_string);
84 virtual void Search(std::string const& search_string);
85 virtual void Activate(std::string const& uri);
86 virtual void Preview(std::string const& uri);
7687
77 nux::RWProperty<std::string> id;88 nux::RWProperty<std::string> id;
78 nux::RWProperty<std::string> dbus_name;89 nux::RWProperty<std::string> dbus_name;
7990
=== modified file 'UnityCore/Model-inl.h'
--- UnityCore/Model-inl.h 2012-01-10 14:14:17 +0000
+++ UnityCore/Model-inl.h 2012-01-26 08:37:26 +0000
@@ -34,10 +34,28 @@
3434
35template<class RowAdaptor>35template<class RowAdaptor>
36Model<RowAdaptor>::Model()36Model<RowAdaptor>::Model()
37 : model_type_(ModelType::REMOTE)
38{
39 Init();
40}
41
42template<class RowAdaptor>
43Model<RowAdaptor>::Model (ModelType model_type)
44 : model_type_(model_type)
45{
46 Init();
47
48 if (model_type == ModelType::LOCAL)
49 swarm_name = ":local";
50}
51
52template<class RowAdaptor>
53void Model<RowAdaptor>::Init ()
37{54{
38 swarm_name.changed.connect(sigc::mem_fun(this, &Model<RowAdaptor>::OnSwarmNameChanged));55 swarm_name.changed.connect(sigc::mem_fun(this, &Model<RowAdaptor>::OnSwarmNameChanged));
39 count.SetGetterFunction(sigc::mem_fun(this, &Model<RowAdaptor>::get_count));56 count.SetGetterFunction(sigc::mem_fun(this, &Model<RowAdaptor>::get_count));
40 seqnum.SetGetterFunction(sigc::mem_fun(this, &Model<RowAdaptor>::get_seqnum));57 seqnum.SetGetterFunction(sigc::mem_fun(this, &Model<RowAdaptor>::get_seqnum));
58 model.SetGetterFunction(sigc::mem_fun(this, &Model<RowAdaptor>::get_model));
41}59}
4260
43template<class RowAdaptor>61template<class RowAdaptor>
@@ -52,7 +70,29 @@
52 if (model_)70 if (model_)
53 dee_model_clear(model_);71 dee_model_clear(model_);
5472
55 model_ = dee_shared_model_new(swarm_name.c_str());73 switch(model_type_)
74 {
75 case ModelType::LOCAL:
76 model_ = dee_sequence_model_new();
77 break;
78 case ModelType::REMOTE:
79 model_ = dee_shared_model_new(swarm_name.c_str());
80 sig_manager_.Add(new TransactionSignalType(model_,
81 "begin-transaction",
82 sigc::mem_fun(this, &Model<RowAdaptor>::OnTransactionBegin)));
83
84 sig_manager_.Add(new TransactionSignalType(model_,
85 "end-transaction",
86 sigc::mem_fun(this, &Model<RowAdaptor>::OnTransactionEnd)));
87 break;
88 default:
89 LOG_ERROR(_model_inl_logger) << "Unexpected ModelType " << model_type_;
90 break;
91 }
92
93 model.EmitChanged(model_);
94
95
56 renderer_tag_ = dee_model_register_tag(model_, NULL);96 renderer_tag_ = dee_model_register_tag(model_, NULL);
5797
58 sig_manager_.Add(new RowSignalType(model_,98 sig_manager_.Add(new RowSignalType(model_,
@@ -66,14 +106,6 @@
66 sig_manager_.Add(new RowSignalType(model_,106 sig_manager_.Add(new RowSignalType(model_,
67 "row-removed",107 "row-removed",
68 sigc::mem_fun(this, &Model<RowAdaptor>::OnRowRemoved)));108 sigc::mem_fun(this, &Model<RowAdaptor>::OnRowRemoved)));
69
70 sig_manager_.Add(new TransactionSignalType(model_,
71 "begin-transaction",
72 sigc::mem_fun(this, &Model<RowAdaptor>::OnTransactionBegin)));
73
74 sig_manager_.Add(new TransactionSignalType(model_,
75 "end-transaction",
76 sigc::mem_fun(this, &Model<RowAdaptor>::OnTransactionEnd)));
77}109}
78110
79template<class RowAdaptor>111template<class RowAdaptor>
@@ -148,6 +180,12 @@
148 return 0;180 return 0;
149}181}
150182
183template<class RowAdaptor>
184glib::Object<DeeModel> Model<RowAdaptor>::get_model()
185{
186 return model_;
187}
188
151}189}
152}190}
153191
154192
=== modified file 'UnityCore/Model.h'
--- UnityCore/Model.h 2012-01-10 14:14:17 +0000
+++ UnityCore/Model.h 2012-01-26 08:37:26 +0000
@@ -35,6 +35,12 @@
35namespace dash35namespace dash
36{36{
3737
38enum ModelType
39{
40 REMOTE,
41 LOCAL
42};
43
38/* This template class encapsulates the basics of talking to a DeeSharedModel,44/* This template class encapsulates the basics of talking to a DeeSharedModel,
39 * however it is a template as you can choose your own RowAdaptor (see45 * however it is a template as you can choose your own RowAdaptor (see
40 * ResultsRowAdaptor.h for an example) which then presents the data in the rows46 * ResultsRowAdaptor.h for an example) which then presents the data in the rows
@@ -47,6 +53,7 @@
47 typedef std::shared_ptr<Model> Ptr;53 typedef std::shared_ptr<Model> Ptr;
4854
49 Model();55 Model();
56 Model (ModelType model_type);
50 virtual ~Model();57 virtual ~Model();
5158
52 const RowAdaptor RowAtIndex(std::size_t index);59 const RowAdaptor RowAtIndex(std::size_t index);
@@ -54,6 +61,7 @@
54 nux::Property<std::string> swarm_name;61 nux::Property<std::string> swarm_name;
55 nux::ROProperty<std::size_t> count;62 nux::ROProperty<std::size_t> count;
56 nux::ROProperty<unsigned long long> seqnum;63 nux::ROProperty<unsigned long long> seqnum;
64 nux::ROProperty<glib::Object<DeeModel>> model;
5765
58 sigc::signal<void, RowAdaptor&> row_added;66 sigc::signal<void, RowAdaptor&> row_added;
59 sigc::signal<void, RowAdaptor&> row_changed;67 sigc::signal<void, RowAdaptor&> row_changed;
@@ -63,6 +71,7 @@
63 sigc::signal<void, unsigned long long, unsigned long long> end_transaction;71 sigc::signal<void, unsigned long long, unsigned long long> end_transaction;
6472
65private:73private:
74 void Init();
66 void OnRowAdded(DeeModel* model, DeeModelIter* iter);75 void OnRowAdded(DeeModel* model, DeeModelIter* iter);
67 void OnRowChanged(DeeModel* model, DeeModelIter* iter);76 void OnRowChanged(DeeModel* model, DeeModelIter* iter);
68 void OnRowRemoved(DeeModel* model, DeeModelIter* iter);77 void OnRowRemoved(DeeModel* model, DeeModelIter* iter);
@@ -71,11 +80,13 @@
71 void OnSwarmNameChanged(std::string const& swarm_name);80 void OnSwarmNameChanged(std::string const& swarm_name);
72 std::size_t get_count();81 std::size_t get_count();
73 unsigned long long get_seqnum();82 unsigned long long get_seqnum();
83 glib::Object<DeeModel> get_model();
7484
75private:85private:
76 glib::Object<DeeModel> model_;86 glib::Object<DeeModel> model_;
77 glib::SignalManager sig_manager_;87 glib::SignalManager sig_manager_;
78 DeeModelTag* renderer_tag_;88 DeeModelTag* renderer_tag_;
89 ModelType model_type_;
79};90};
8091
81}92}
8293
=== modified file 'UnityCore/Results.cpp'
--- UnityCore/Results.cpp 2011-07-25 10:54:25 +0000
+++ UnityCore/Results.cpp 2012-01-26 08:37:26 +0000
@@ -31,6 +31,14 @@
31 row_removed.connect(sigc::mem_fun(this, &Results::OnRowRemoved));31 row_removed.connect(sigc::mem_fun(this, &Results::OnRowRemoved));
32}32}
3333
34Results::Results(ModelType model_type)
35 : Model<Result>::Model(model_type)
36{
37 row_added.connect(sigc::mem_fun(this, &Results::OnRowAdded));
38 row_changed.connect(sigc::mem_fun(this, &Results::OnRowChanged));
39 row_removed.connect(sigc::mem_fun(this, &Results::OnRowRemoved));
40}
41
34void Results::OnRowAdded(Result& result)42void Results::OnRowAdded(Result& result)
35{43{
36 result_added.emit(result);44 result_added.emit(result);
3745
=== modified file 'UnityCore/Results.h'
--- UnityCore/Results.h 2011-07-28 13:35:05 +0000
+++ UnityCore/Results.h 2012-01-26 08:37:26 +0000
@@ -36,6 +36,7 @@
36 typedef std::shared_ptr<Results> Ptr;36 typedef std::shared_ptr<Results> Ptr;
3737
38 Results();38 Results();
39 Results(ModelType model_type);
3940
40 sigc::signal<void, Result const&> result_added;41 sigc::signal<void, Result const&> result_added;
41 sigc::signal<void, Result const&> result_changed;42 sigc::signal<void, Result const&> result_changed;
4243
=== modified file 'com.canonical.Unity.gschema.xml'
--- com.canonical.Unity.gschema.xml 2011-12-12 18:18:38 +0000
+++ com.canonical.Unity.gschema.xml 2012-01-26 08:37:26 +0000
@@ -21,7 +21,7 @@
21 <description>Whether the home screen should be expanded.</description>21 <description>Whether the home screen should be expanded.</description>
22 </key>22 </key>
23 </schema>23 </schema>
24 <schema path="/desktop/unity/launcher/" id="com.canonical.Unity.Launcher" gettext-domain="unity">24 <schema path="/desktop/unity/launcher/" id="com.canonical.Unity.Launcher" gettext-domain="unity">
25 <key type="as" name="favorites">25 <key type="as" name="favorites">
26 <default>[ 'ubiquity-gtkui.desktop', 'nautilus-home.desktop', 'firefox.desktop', 'libreoffice-writer.desktop', 'libreoffice-calc.desktop', 'libreoffice-impress.desktop', 'ubuntu-software-center.desktop', 'ubuntuone-installer.desktop', 'gnome-control-center.desktop' ]</default>26 <default>[ 'ubiquity-gtkui.desktop', 'nautilus-home.desktop', 'firefox.desktop', 'libreoffice-writer.desktop', 'libreoffice-calc.desktop', 'libreoffice-impress.desktop', 'ubuntu-software-center.desktop', 'ubuntuone-installer.desktop', 'gnome-control-center.desktop' ]</default>
27 <summary>List of desktop file ids for favorites on the launcher.</summary>27 <summary>List of desktop file ids for favorites on the launcher.</summary>
@@ -33,7 +33,7 @@
33 <description>This is a detection key for the favorite migration script to know whether the needed migration is done or not.</description>33 <description>This is a detection key for the favorite migration script to know whether the needed migration is done or not.</description>
34 </key>34 </key>
35 </schema>35 </schema>
36 <schema path="/desktop/unity/panel/" id="com.canonical.Unity.Panel" gettext-domain="unity">36 <schema path="/desktop/unity/panel/" id="com.canonical.Unity.Panel" gettext-domain="unity">
37 <key type="as" name="systray-whitelist">37 <key type="as" name="systray-whitelist">
38 <default>[ 'JavaEmbeddedFrame', 'Wine', 'scp-dbus-service', 'Update-notifier' ]</default>38 <default>[ 'JavaEmbeddedFrame', 'Wine', 'scp-dbus-service', 'Update-notifier' ]</default>
39 <summary>List of client names, resource classes or wm classes to allow in the Panel's systray implementation.</summary>39 <summary>List of client names, resource classes or wm classes to allow in the Panel's systray implementation.</summary>
@@ -46,5 +46,12 @@
46 <summary>List of device uuid for favorites on the launcher.</summary>46 <summary>List of device uuid for favorites on the launcher.</summary>
47 <description>These devices are shown in the Launcher by default.</description>47 <description>These devices are shown in the Launcher by default.</description>
48 </key>48 </key>
49 </schema>49 </schema>
50 <schema path="/desktop/unity/dash/" id="com.canonical.Unity.Dash" gettext-domain="unity">
51 <key type="as" name="home-lens-ordering">
52 <default>[ 'applications.lens', 'files.lens', 'music.lens' ]</default>
53 <summary>List of lens ids specifying how lenses should be ordered in the Dash home screen.</summary>
54 <description>The categories listed on the Dash home screen will be ordered according to this list. Lenses not appearing in this list will not have any particular ordering and will always sort after lenses specified in this list.</description>
55 </key>
56 </schema>
50</schemalist>57</schemalist>
5158
=== modified file 'plugins/unityshell/src/DashView.cpp'
--- plugins/unityshell/src/DashView.cpp 2012-01-17 13:31:52 +0000
+++ plugins/unityshell/src/DashView.cpp 2012-01-26 08:37:26 +0000
@@ -47,6 +47,7 @@
4747
48DashView::DashView()48DashView::DashView()
49 : nux::View(NUX_TRACKER_LOCATION)49 : nux::View(NUX_TRACKER_LOCATION)
50 , home_lens_(new HomeLens(_("Home"), _("Home screen"), _("Search")))
50 , active_lens_view_(0)51 , active_lens_view_(0)
51 , last_activated_uri_("")52 , last_activated_uri_("")
52 , searching_timeout_id_(0)53 , searching_timeout_id_(0)
@@ -67,6 +68,8 @@
67 mouse_down.connect(sigc::mem_fun(this, &DashView::OnMouseButtonDown));68 mouse_down.connect(sigc::mem_fun(this, &DashView::OnMouseButtonDown));
6869
69 Relayout();70 Relayout();
71
72 home_lens_->AddLenses(lenses_);
70 lens_bar_->Activate("home.lens");73 lens_bar_->Activate("home.lens");
71}74}
7275
@@ -81,6 +84,23 @@
81 ubus_manager_.SendMessage(UBUS_BACKGROUND_REQUEST_COLOUR_EMIT);84 ubus_manager_.SendMessage(UBUS_BACKGROUND_REQUEST_COLOUR_EMIT);
82 visible_ = true;85 visible_ = true;
83 search_bar_->text_entry()->SelectAll();86 search_bar_->text_entry()->SelectAll();
87
88 /* Give the lenses a chance to prep data before we map them */
89 lens_bar_->Activate(active_lens_view_->lens()->id());
90 if (active_lens_view_->lens()->id() == "home.lens")
91 {
92 for (auto lens : lenses_.GetLenses())
93 {
94 lens->view_type = ViewType::HOME_VIEW;
95 LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HOME_VIEW
96 << " on '" << lens->id() << "'";
97 }
98
99 home_lens_->view_type = ViewType::LENS_VIEW;
100 LOG_DEBUG(logger) << "Setting ViewType " << ViewType::LENS_VIEW
101 << " on '" << home_lens_->id() << "'";
102 }
103
84 renderer_.AboutToShow();104 renderer_.AboutToShow();
85}105}
86106
@@ -88,6 +108,17 @@
88{108{
89 visible_ = false;109 visible_ = false;
90 renderer_.AboutToHide();110 renderer_.AboutToHide();
111
112 for (auto lens : lenses_.GetLenses())
113 {
114 lens->view_type = ViewType::HIDDEN;
115 LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HIDDEN
116 << " on '" << lens->id() << "'";
117 }
118
119 home_lens_->view_type = ViewType::HIDDEN;
120 LOG_DEBUG(logger) << "Setting ViewType " << ViewType::HIDDEN
121 << " on '" << home_lens_->id() << "'";
91}122}
92123
93void DashView::SetupViews()124void DashView::SetupViews()
@@ -111,9 +142,9 @@
111 lenses_layout_ = new nux::VLayout();142 lenses_layout_ = new nux::VLayout();
112 content_layout_->AddView(lenses_layout_, 1, nux::MINOR_POSITION_LEFT);143 content_layout_->AddView(lenses_layout_, 1, nux::MINOR_POSITION_LEFT);
113144
114 home_view_ = new HomeView();145 home_view_ = new LensView(home_lens_);
115 active_lens_view_ = home_view_;146 active_lens_view_ = home_view_;
116 lens_views_["home.lens"] = home_view_;147 lens_views_[home_lens_->id] = home_view_;
117 lenses_layout_->AddView(home_view_);148 lenses_layout_->AddView(home_view_);
118149
119 lens_bar_ = new LensBar();150 lens_bar_ = new LensBar();
@@ -239,7 +270,6 @@
239270
240 std::string id = AnalyseLensURI(uri.Str());271 std::string id = AnalyseLensURI(uri.Str());
241272
242 home_view_->search_string = "";
243 lens_bar_->Activate(id);273 lens_bar_->Activate(id);
244274
245 if ((id == "home.lens" && handled_type != GOTO_DASH_URI ) || !visible_)275 if ((id == "home.lens" && handled_type != GOTO_DASH_URI ) || !visible_)
@@ -336,7 +366,6 @@
336{366{
337 std::string id = lens->id;367 std::string id = lens->id;
338 lens_bar_->AddLens(lens);368 lens_bar_->AddLens(lens);
339 home_view_->AddLens(lens);
340369
341 LensView* view = new LensView(lens);370 LensView* view = new LensView(lens);
342 view->SetVisible(false);371 view->SetVisible(false);
@@ -362,15 +391,17 @@
362 for (auto it: lens_views_)391 for (auto it: lens_views_)
363 {392 {
364 bool id_matches = it.first == id;393 bool id_matches = it.first == id;
394 ViewType view_type = id_matches ? LENS_VIEW : (view == home_view_ ? HOME_VIEW : HIDDEN);
365 it.second->SetVisible(id_matches);395 it.second->SetVisible(id_matches);
366 it.second->view_type = id_matches ? LENS_VIEW : (view == home_view_ ? HOME_VIEW : HIDDEN);396 it.second->view_type = view_type;
397
398 LOG_DEBUG(logger) << "Setting ViewType " << view_type
399 << " on '" << it.first << "'";
367 }400 }
368401
369 search_bar_->search_string = view->search_string;402 search_bar_->search_string = view->search_string;
370 if (view != home_view_)403 search_bar_->search_hint = view->lens()->search_hint;
371 search_bar_->search_hint = view->lens()->search_hint;404
372 else
373 search_bar_->search_hint = _("Search");
374 bool expanded = view->filters_expanded;405 bool expanded = view->filters_expanded;
375 search_bar_->showing_filters = expanded;406 search_bar_->showing_filters = expanded;
376407
@@ -553,6 +584,7 @@
553 ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);584 ubus_manager_.SendMessage(UBUS_PLACE_VIEW_CLOSE_REQUEST);
554 else585 else
555 search_bar_->search_string = "";586 search_bar_->search_string = "";
587
556 return true;588 return true;
557 }589 }
558 return false;590 return false;
559591
=== modified file 'plugins/unityshell/src/DashView.h'
--- plugins/unityshell/src/DashView.h 2012-01-16 15:31:59 +0000
+++ plugins/unityshell/src/DashView.h 2012-01-26 08:37:26 +0000
@@ -27,10 +27,10 @@
27#include <Nux/View.h>27#include <Nux/View.h>
28#include <Nux/VLayout.h>28#include <Nux/VLayout.h>
29#include <UnityCore/FilesystemLenses.h>29#include <UnityCore/FilesystemLenses.h>
30#include <UnityCore/HomeLens.h>
3031
31#include "BackgroundEffectHelper.h"32#include "BackgroundEffectHelper.h"
32#include "DashSearchBar.h"33#include "DashSearchBar.h"
33#include "HomeView.h"
34#include "Introspectable.h"34#include "Introspectable.h"
35#include "LensBar.h"35#include "LensBar.h"
36#include "LensView.h"36#include "LensView.h"
@@ -95,6 +95,7 @@
95 std::string AnalyseLensURI(std::string const& uri);95 std::string AnalyseLensURI(std::string const& uri);
96 void UpdateLensFilter(std::string lens, std::string filter, std::string value);96 void UpdateLensFilter(std::string lens, std::string filter, std::string value);
97 void UpdateLensFilterValue(Filter::Ptr filter, std::string value);97 void UpdateLensFilterValue(Filter::Ptr filter, std::string value);
98 void EnsureLensesInitialized();
9899
99 bool AcceptKeyNavFocus();100 bool AcceptKeyNavFocus();
100 bool InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character);101 bool InspectKeyEvent(unsigned int eventType, unsigned int key_sym, const char* character);
@@ -108,6 +109,7 @@
108private:109private:
109 UBusManager ubus_manager_;110 UBusManager ubus_manager_;
110 FilesystemLenses lenses_;111 FilesystemLenses lenses_;
112 HomeLens::Ptr home_lens_;
111 LensViews lens_views_;113 LensViews lens_views_;
112114
113115
@@ -118,7 +120,7 @@
118 nux::VLayout* lenses_layout_;120 nux::VLayout* lenses_layout_;
119 LensBar* lens_bar_;121 LensBar* lens_bar_;
120122
121 HomeView* home_view_;123 LensView* home_view_;
122 LensView* active_lens_view_;124 LensView* active_lens_view_;
123125
124 // Drawing related126 // Drawing related
125127
=== removed file 'plugins/unityshell/src/HomeView.cpp'
--- plugins/unityshell/src/HomeView.cpp 2011-12-21 16:49:59 +0000
+++ plugins/unityshell/src/HomeView.cpp 1970-01-01 00:00:00 +0000
@@ -1,252 +0,0 @@
1/*
2 * Copyright (C) 2010 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
17 */
18
19#include "HomeView.h"
20
21#include <boost/lexical_cast.hpp>
22
23#include <NuxCore/Logger.h>
24
25#include "DashStyle.h"
26#include "ResultRendererTile.h"
27#include "UBusMessages.h"
28
29namespace unity
30{
31namespace dash
32{
33
34namespace
35{
36nux::logging::Logger logger("unity.dash.homeview");
37}
38
39// This is so we can override the scroll bar for the view.
40class HomeScrollView: public nux::ScrollView
41{
42public:
43 HomeScrollView(nux::VScrollBar* scroll_bar, NUX_FILE_LINE_DECL)
44 : nux::ScrollView(NUX_FILE_LINE_PARAM)
45 {
46 SetVScrollBar(scroll_bar);
47 }
48};
49
50
51
52NUX_IMPLEMENT_OBJECT_TYPE(HomeView);
53
54HomeView::HomeView()
55 : fix_renderering_id_(0)
56{
57 SetupViews();
58
59 search_string.changed.connect([&](std::string const& search)
60 {
61 for (auto lens : lenses_)
62 lens->GlobalSearch(search);
63
64 for (auto group: categories_)
65 {
66 group->SetVisible(search != "" && counts_[group]);
67 }
68 home_view_->SetVisible(search == "");
69 scroll_view_->SetVisible(search != "");
70
71 QueueDraw();
72 });
73}
74
75HomeView::~HomeView()
76{
77 if (fix_renderering_id_)
78 g_source_remove(fix_renderering_id_);
79}
80
81void HomeView::SetupViews()
82{
83 layout_ = new nux::HLayout(NUX_TRACKER_LOCATION);
84 layout_->SetHorizontalExternalMargin(7);
85
86 scroll_view_ = new HomeScrollView(new PlacesVScrollBar(NUX_TRACKER_LOCATION),
87 NUX_TRACKER_LOCATION);
88 scroll_view_->EnableVerticalScrollBar(true);
89 scroll_view_->EnableHorizontalScrollBar(false);
90 scroll_view_->SetVisible(false);
91 layout_->AddView(scroll_view_);
92
93 scroll_layout_ = new nux::VLayout();
94 scroll_view_->SetLayout(scroll_layout_);
95
96 home_view_ = new PlacesHomeView();
97 layout_->AddView(home_view_);
98
99 SetLayout(layout_);
100}
101
102void HomeView::AddLens(Lens::Ptr lens)
103{
104 lenses_.push_back(lens);
105
106 std::string name = lens->name;
107 std::string icon_hint = lens->icon_hint;
108
109 LOG_DEBUG(logger) << "Lens added " << name;
110
111 PlacesGroup* group = new PlacesGroup();
112 group->SetName(name.c_str());
113 group->SetIcon(icon_hint.c_str());
114 group->SetExpanded(false);
115 group->SetVisible(false);
116 group->expanded.connect(sigc::mem_fun(this, &HomeView::OnGroupExpanded));
117 categories_.push_back(group);
118 counts_[group] = 0;
119
120 ResultViewGrid* grid = new ResultViewGrid(NUX_TRACKER_LOCATION);
121 grid->expanded = false;
122 grid->SetModelRenderer(new ResultRendererTile(NUX_TRACKER_LOCATION));
123 grid->UriActivated.connect([&, lens] (std::string const& uri) { uri_activated.emit(uri); lens->Activate(uri); });
124 group->SetChildView(grid);
125
126 Results::Ptr results = lens->global_results;
127 results->result_added.connect([&, group, grid] (Result const& result)
128 {
129 grid->AddResult(const_cast<Result&>(result));
130 counts_[group]++;
131 UpdateCounts(group);
132 });
133
134 results->result_removed.connect([&, group, grid] (Result const& result)
135 {
136 grid->RemoveResult(const_cast<Result&>(result));
137 counts_[group]--;
138 UpdateCounts(group);
139 });
140
141
142 scroll_layout_->AddView(group, 0);
143}
144
145void HomeView::UpdateCounts(PlacesGroup* group)
146{
147 group->SetCounts(dash::Style::Instance().GetDefaultNColumns(), counts_[group]);
148 group->SetVisible(counts_[group]);
149
150 QueueFixRenderering();
151}
152
153void HomeView::OnGroupExpanded(PlacesGroup* group)
154{
155 ResultViewGrid* grid = static_cast<ResultViewGrid*>(group->GetChildView());
156 grid->expanded = group->GetExpanded();
157 ubus_manager_.SendMessage(UBUS_PLACE_VIEW_QUEUE_DRAW);
158}
159
160void HomeView::OnColumnsChanged()
161{
162 unsigned int columns = dash::Style::Instance().GetDefaultNColumns();
163
164 for (auto group: categories_)
165 {
166 group->SetCounts(columns, counts_[group]);
167 }
168}
169
170void HomeView::QueueFixRenderering()
171{
172 if (fix_renderering_id_)
173 return;
174
175 fix_renderering_id_ = g_idle_add_full (G_PRIORITY_DEFAULT, (GSourceFunc)FixRenderering, this, NULL);
176}
177
178gboolean HomeView::FixRenderering(HomeView* self)
179{
180 std::list<Area*> children = self->scroll_layout_->GetChildren();
181 std::list<Area*>::reverse_iterator rit;
182 bool found_one = false;
183
184 for (rit = children.rbegin(); rit != children.rend(); ++rit)
185 {
186 PlacesGroup* group = static_cast<PlacesGroup*>(*rit);
187
188 if (group->IsVisible())
189 group->SetDrawSeparator(found_one);
190
191 found_one = group->IsVisible();
192 }
193
194 self->fix_renderering_id_ = 0;
195 return FALSE;
196}
197
198void HomeView::Draw(nux::GraphicsEngine& gfx_context, bool force_draw)
199{
200 nux::Geometry geo = GetGeometry();
201
202 gfx_context.PushClippingRectangle(geo);
203 nux::GetPainter().PaintBackground(gfx_context, geo);
204 gfx_context.PopClippingRectangle();
205}
206
207void HomeView::DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw)
208{
209 gfx_context.PushClippingRectangle(GetGeometry());
210
211 layout_->ProcessDraw(gfx_context, force_draw);
212
213 gfx_context.PopClippingRectangle();
214}
215
216void HomeView::ActivateFirst()
217{
218 for (auto lens: lenses_)
219 {
220 Results::Ptr results = lens->global_results;
221 if (results->count())
222 {
223 Result result = results->RowAtIndex(0);
224 if (result.uri != "")
225 {
226 uri_activated(result.uri);
227 lens->Activate(result.uri);
228 return;
229 }
230 }
231 }
232}
233
234
235// Keyboard navigation
236bool HomeView::AcceptKeyNavFocus()
237{
238 return false;
239}
240
241// Introspectable
242std::string HomeView::GetName() const
243{
244 return "HomeView";
245}
246
247void HomeView::AddProperties(GVariantBuilder* builder)
248{}
249
250
251}
252}
2530
=== removed file 'plugins/unityshell/src/HomeView.h'
--- plugins/unityshell/src/HomeView.h 2011-12-16 01:55:42 +0000
+++ plugins/unityshell/src/HomeView.h 1970-01-01 00:00:00 +0000
@@ -1,93 +0,0 @@
1/*
2 * Copyright (C) 2010 Canonical Ltd
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
17 */
18
19#ifndef UNITY_HOME_VIEW_H_
20#define UNITY_HOME_VIEW_H_
21
22#include <string>
23
24#include <NuxGraphics/GraphicsEngine.h>
25#include <Nux/Nux.h>
26#include <Nux/HLayout.h>
27#include <Nux/View.h>
28#include <Nux/VLayout.h>
29#include <UnityCore/Lens.h>
30
31#include "LensView.h"
32#include "PlacesGroup.h"
33#include "PlacesHomeView.h"
34#include "ResultViewGrid.h"
35#include "UBusWrapper.h"
36
37namespace unity
38{
39namespace dash
40{
41
42class HomeView : public LensView
43{
44 NUX_DECLARE_OBJECT_TYPE(HomeView, LensView);
45 typedef std::vector<PlacesGroup*> CategoryGroups;
46 typedef std::map<PlacesGroup*, unsigned int> ResultCounts;
47 typedef std::vector<Lens::Ptr> Lenses;
48
49public:
50 HomeView();
51 ~HomeView();
52
53 void AddLens(Lens::Ptr lens);
54 void ActivateFirst();
55
56private:
57 void SetupViews();
58
59 void OnResultAdded(Result const& result);
60 void OnResultRemoved(Result const& result);
61 void UpdateCounts(PlacesGroup* group);
62 void OnGroupExpanded(PlacesGroup* group);
63 void OnColumnsChanged();
64 void QueueFixRenderering();
65
66 static gboolean FixRenderering(HomeView* self);
67
68 void Draw(nux::GraphicsEngine& gfx_context, bool force_draw);
69 void DrawContent(nux::GraphicsEngine& gfx_context, bool force_draw);
70
71 bool AcceptKeyNavFocus();
72 std::string GetName() const;
73 void AddProperties(GVariantBuilder* builder);
74
75private:
76 UBusManager ubus_manager_;
77 CategoryGroups categories_;
78 ResultCounts counts_;
79 Lenses lenses_;
80
81 nux::HLayout* layout_;
82 nux::ScrollView* scroll_view_;
83 nux::VLayout* scroll_layout_;
84
85 PlacesHomeView* home_view_;
86
87 guint fix_renderering_id_;
88};
89
90
91}
92}
93#endif
940
=== modified file 'plugins/unityshell/src/LensBar.cpp'
--- plugins/unityshell/src/LensBar.cpp 2011-12-08 01:23:11 +0000
+++ plugins/unityshell/src/LensBar.cpp 2012-01-26 08:37:26 +0000
@@ -148,10 +148,20 @@
148148
149void LensBar::SetActive(LensBarIcon* activated)149void LensBar::SetActive(LensBarIcon* activated)
150{150{
151 bool state_changed = false;
152
151 for (auto icon: icons_)153 for (auto icon: icons_)
152 icon->active = icon == activated;154 {
153155 bool state = icon == activated;
154 lens_activated.emit(activated->id);156
157 if (icon->active != state)
158 state_changed = true;
159
160 icon->active = state;
161 }
162
163 if (state_changed)
164 lens_activated.emit(activated->id);
155}165}
156166
157void LensBar::ActivateNext()167void LensBar::ActivateNext()
158168
=== modified file 'plugins/unityshell/src/LensView.cpp'
--- plugins/unityshell/src/LensView.cpp 2011-12-14 16:18:41 +0000
+++ plugins/unityshell/src/LensView.cpp 2012-01-26 08:37:26 +0000
@@ -219,7 +219,12 @@
219 group->SetExpanded(false);219 group->SetExpanded(false);
220 group->SetVisible(false);220 group->SetVisible(false);
221 group->expanded.connect(sigc::mem_fun(this, &LensView::OnGroupExpanded));221 group->expanded.connect(sigc::mem_fun(this, &LensView::OnGroupExpanded));
222 categories_.push_back(group);222
223
224 /* Add the group at the correct offset into the categories vector */
225 categories_.insert(categories_.begin() + index, group);
226
227 /* Reset result count */
223 counts_[group] = 0;228 counts_[group] = 0;
224229
225 ResultViewGrid* grid = new ResultViewGrid(NUX_TRACKER_LOCATION);230 ResultViewGrid* grid = new ResultViewGrid(NUX_TRACKER_LOCATION);
@@ -232,7 +237,11 @@
232 grid->UriActivated.connect([&] (std::string const& uri) { uri_activated.emit(uri); lens_->Activate(uri); });237 grid->UriActivated.connect([&] (std::string const& uri) { uri_activated.emit(uri); lens_->Activate(uri); });
233 group->SetChildView(grid);238 group->SetChildView(grid);
234239
235 scroll_layout_->AddView(group, 0);240 /* We need the full range of method args so we can specify the offset
241 * of the group into the layout */
242 scroll_layout_->AddView(group, 0, nux::MinorDimensionPosition::eAbove,
243 nux::MinorDimensionSize::eFull, 100.0f,
244 (nux::LayoutPosition)index);
236}245}
237246
238void LensView::OnResultAdded(Result const& result)247void LensView::OnResultAdded(Result const& result)
239248
=== removed file 'plugins/unityshell/src/PlacesHomeView.cpp'
--- plugins/unityshell/src/PlacesHomeView.cpp 2011-12-14 20:04:23 +0000
+++ plugins/unityshell/src/PlacesHomeView.cpp 1970-01-01 00:00:00 +0000
@@ -1,378 +0,0 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3 * Copyright (C) 2010 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
18 */
19
20#include "config.h"
21
22#include <Nux/Nux.h>
23#include <Nux/BaseWindow.h>
24#include <Nux/HLayout.h>
25#include <Nux/Layout.h>
26#include <Nux/WindowCompositor.h>
27
28#include <NuxImage/CairoGraphics.h>
29#include <NuxImage/ImageSurface.h>
30
31#include <NuxGraphics/GLThread.h>
32#include <NuxGraphics/RenderingPipe.h>
33
34#include <glib.h>
35#include <glib/gi18n-lib.h>
36#include <gio/gdesktopappinfo.h>
37#include "ubus-server.h"
38#include "UBusMessages.h"
39
40#include "PlacesHomeView.h"
41#include "PlacesSimpleTile.h"
42
43#include "DashStyle.h"
44#include <UnityCore/GLibWrapper.h>
45#include <UnityCore/Variant.h>
46
47#include <string>
48#include <vector>
49
50#define DELTA_DOUBLE_REQUEST 500000000
51
52namespace unity
53{
54
55enum
56{
57 TYPE_PLACE = 0,
58 TYPE_EXEC
59};
60
61class Shortcut : public PlacesSimpleTile
62{
63public:
64 Shortcut(const char* icon, const char* name, int size)
65 : PlacesSimpleTile(icon, name, size),
66 _id(0),
67 _place_id(NULL),
68 _place_section(0),
69 _exec(NULL)
70 {
71 SetDndEnabled(false, false);
72 }
73
74 ~Shortcut()
75 {
76 g_free(_place_id);
77 g_free(_exec);
78 }
79
80 int _id;
81 gchar* _place_id;
82 guint32 _place_section;
83 char* _exec;
84};
85
86PlacesHomeView::PlacesHomeView()
87 : _ubus_handle(0)
88{
89 dash::Style& style = dash::Style::Instance();
90
91 SetName(_("Shortcuts"));
92 SetIcon(PKGDATADIR"/shortcuts_group_icon.png");
93 SetDrawSeparator(false);
94
95 _layout = new nux::GridHLayout(NUX_TRACKER_LOCATION);
96 _layout->SetReconfigureParentLayoutOnGeometryChange(true);
97 SetChildLayout(_layout);
98
99 _layout->ForceChildrenSize(true);
100 _layout->SetChildrenSize(style.GetHomeTileWidth(), style.GetHomeTileHeight());
101 _layout->EnablePartialVisibility(false);
102 _layout->MatchContentSize(true);
103 _layout->SetLeftAndRightPadding(32);
104 _layout->SetSpaceBetweenChildren(32, 32);
105 _layout->SetMinMaxSize((style.GetHomeTileWidth() * 4) + (32 * 5),
106 (style.GetHomeTileHeight() * 2) + 32);
107
108 _ubus_handle = ubus_server_register_interest(ubus_server_get_default(),
109 UBUS_PLACE_VIEW_SHOWN,
110 (UBusCallback) &PlacesHomeView::DashVisible,
111 this);
112
113 //In case the GConf key is invalid (e.g. when an app was uninstalled), we
114 //rely on a fallback "whitelist" mechanism instead of showing nothing at all
115 _browser_alternatives.push_back("firefox");
116 _browser_alternatives.push_back("chromium-browser");
117 _browser_alternatives.push_back("epiphany-browser");
118 _browser_alternatives.push_back("midori");
119
120 _photo_alternatives.push_back("shotwell");
121 _photo_alternatives.push_back("f-spot");
122 _photo_alternatives.push_back("gthumb");
123 _photo_alternatives.push_back("gwenview");
124 _photo_alternatives.push_back("eog");
125
126 _email_alternatives.push_back("evolution");
127 _email_alternatives.push_back("thunderbird");
128 _email_alternatives.push_back("claws-mail");
129 _email_alternatives.push_back("kmail");
130
131 _music_alternatives.push_back("banshee-1");
132 _music_alternatives.push_back("rhythmbox");
133 _music_alternatives.push_back("totem");
134 _music_alternatives.push_back("vlc");
135
136 expanded.connect(sigc::mem_fun(this, &PlacesHomeView::Refresh));
137
138 Refresh();
139}
140
141PlacesHomeView::~PlacesHomeView()
142{
143 if (_ubus_handle != 0)
144 ubus_server_unregister_interest(ubus_server_get_default(), _ubus_handle);
145}
146
147void
148PlacesHomeView::DashVisible(GVariant* data, void* val)
149{
150 PlacesHomeView* self = (PlacesHomeView*)val;
151 self->Refresh();
152}
153
154void
155PlacesHomeView::Refresh(PlacesGroup*foo)
156{
157 Shortcut* shortcut = NULL;
158 gchar* markup = NULL;
159 const char* temp = "<big>%s</big>";
160 int icon_size = dash::Style::Instance().GetHomeTileIconSize();
161
162 _layout->Clear();
163
164 // Media Apps
165 markup = g_strdup_printf(temp, _("Media Apps"));
166 shortcut = new Shortcut(PKGDATADIR"/find_media_apps.png",
167 markup,
168 icon_size);
169 shortcut->_id = TYPE_PLACE;
170 shortcut->_place_id = g_strdup("applications.lens?filter_type=media");
171 shortcut->_place_section = 9;
172 _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
173 shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
174 g_free(markup);
175
176 // Internet Apps
177 markup = g_strdup_printf(temp, _("Internet Apps"));
178 shortcut = new Shortcut(PKGDATADIR"/find_internet_apps.png",
179 markup,
180 icon_size);
181 shortcut->_id = TYPE_PLACE;
182 shortcut->_place_id = g_strdup("applications.lens?filter_type=internet");
183 shortcut->_place_section = 8;
184 _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
185 shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
186 g_free(markup);
187
188 // More Apps
189 markup = g_strdup_printf(temp, _("More Apps"));
190 shortcut = new Shortcut(PKGDATADIR"/find_more_apps.png",
191 markup,
192 icon_size);
193 shortcut->_id = TYPE_PLACE;
194 shortcut->_place_id = g_strdup("applications.lens");
195 shortcut->_place_section = 0;
196 _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
197 shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
198 g_free(markup);
199
200 // Find Files
201 markup = g_strdup_printf(temp, _("Find Files"));
202 shortcut = new Shortcut(PKGDATADIR"/find_files.png",
203 markup,
204 icon_size);
205 shortcut->_id = TYPE_PLACE;
206 shortcut->_place_id = g_strdup("files.lens");
207 shortcut->_place_section = 0;
208 _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
209 shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
210 g_free(markup);
211
212 // Browser
213 CreateShortcutFromMime("x-scheme-handler/http", _("Browse the Web"), _browser_alternatives);
214
215 // Photos
216 // FIXME: Need to figure out the default
217 CreateShortcutFromExec("shotwell", _("View Photos"), _photo_alternatives);
218
219 CreateShortcutFromMime("x-scheme-handler/mailto", _("Check Email"), _email_alternatives);
220
221 CreateShortcutFromMime("audio/x-vorbis+ogg", _("Listen to Music"), _music_alternatives);
222
223 SetExpanded(true);
224 SetCounts(8, 8);
225
226 QueueDraw();
227 _layout->QueueDraw();
228 QueueRelayout();
229}
230
231void
232PlacesHomeView::CreateShortcutFromExec(const char* exec,
233 const char* name,
234 std::vector<std::string>& alternatives)
235{
236 dash::Style& style = dash::Style::Instance();
237 Shortcut* shortcut = NULL;
238 gchar* id;
239 gchar* markup;
240 gchar* icon;
241 gchar* real_exec;
242 GDesktopAppInfo* info;
243
244 markup = g_strdup_printf("<big>%s</big>", name);
245
246 // We're going to try and create a desktop id from a exec string. Now, this is hairy at the
247 // best of times but the following is the closest best-guess without having to do D-Bus
248 // roundtrips to BAMF.
249 if (exec)
250 {
251 char* basename;
252
253 if (exec[0] == '/')
254 basename = g_path_get_basename(exec);
255 else
256 basename = g_strdup(exec);
257
258 id = g_strdup_printf("%s.desktop", basename);
259
260 g_free(basename);
261 }
262 else
263 {
264 id = g_strdup_printf("%s.desktop", alternatives[0].c_str());
265 }
266
267 info = g_desktop_app_info_new(id);
268 std::vector<std::string>::iterator iter = alternatives.begin();
269 while (iter != alternatives.end())
270 {
271 if (!G_IS_DESKTOP_APP_INFO(info))
272 {
273 id = g_strdup_printf("%s.desktop", (*iter).c_str());
274 info = g_desktop_app_info_new(id);
275 iter++;
276 }
277
278 if (G_IS_DESKTOP_APP_INFO(info))
279 {
280 icon = g_icon_to_string(g_app_info_get_icon(G_APP_INFO(info)));
281 real_exec = g_strdup(g_app_info_get_executable(G_APP_INFO(info)));
282
283 shortcut = new Shortcut(icon, markup, style.GetHomeTileIconSize());
284 shortcut->_id = TYPE_EXEC;
285 shortcut->_exec = real_exec;
286 _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
287 shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
288
289 g_free(icon);
290
291 break;
292 }
293 }
294
295 g_free(id);
296 g_free(markup);
297}
298
299void PlacesHomeView::CreateShortcutFromMime(const char* mime,
300 const char* name,
301 std::vector<std::string>& alternatives)
302{
303 dash::Style& style = dash::Style::Instance();
304 GAppInfo* info = g_app_info_get_default_for_type(mime, FALSE);
305
306 // If it was invalid check alternatives for backup
307 if (!G_IS_DESKTOP_APP_INFO(info))
308 {
309 for (auto alt: alternatives)
310 {
311 std::string id = alt + ".desktop";
312 info = G_APP_INFO(g_desktop_app_info_new(id.c_str()));
313
314 if (G_IS_DESKTOP_APP_INFO(info))
315 break;
316 }
317 }
318
319 if (G_IS_DESKTOP_APP_INFO(info))
320 {
321 glib::String icon(g_icon_to_string(g_app_info_get_icon(G_APP_INFO(info))));
322 glib::String markup(g_strdup_printf("<big>%s</big>", name));
323
324 Shortcut* shortcut = new Shortcut(icon.Value(), markup.Value(), style.GetHomeTileIconSize());
325 shortcut->_id = TYPE_EXEC;
326 shortcut->_exec = g_strdup (g_app_info_get_executable(G_APP_INFO(info)));;
327 shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
328 _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
329
330 g_object_unref(info);
331 }
332}
333
334void
335PlacesHomeView::OnShortcutClicked(PlacesTile* tile)
336{
337 Shortcut* shortcut = static_cast<Shortcut*>(tile);
338 int id = shortcut->_id;
339
340 if (id == TYPE_PLACE)
341 {
342 ubus_server_send_message(ubus_server_get_default(),
343 UBUS_PLACE_ENTRY_ACTIVATE_REQUEST,
344 g_variant_new("(sus)",
345 shortcut->_place_id,
346 shortcut->_place_section,
347 ""));
348 }
349 else if (id == TYPE_EXEC)
350 {
351 GError* error = NULL;
352
353 if (!g_spawn_command_line_async(shortcut->_exec, &error))
354 {
355 g_warning("%s: Unable to launch %s: %s",
356 G_STRFUNC,
357 shortcut->_exec,
358 error->message);
359 g_error_free(error);
360 }
361
362 ubus_server_send_message(ubus_server_get_default(),
363 UBUS_PLACE_VIEW_CLOSE_REQUEST,
364 NULL);
365 }
366}
367
368std::string PlacesHomeView::GetName() const
369{
370 return "PlacesHomeView";
371}
372
373void PlacesHomeView::AddProperties(GVariantBuilder* builder)
374{
375 unity::variant::BuilderWrapper(builder).add(GetGeometry());
376}
377
378} // namespace unity
3790
=== removed file 'plugins/unityshell/src/PlacesHomeView.h'
--- plugins/unityshell/src/PlacesHomeView.h 2011-12-14 20:04:23 +0000
+++ plugins/unityshell/src/PlacesHomeView.h 1970-01-01 00:00:00 +0000
@@ -1,72 +0,0 @@
1// -*- Mode: C++; indent-tabs-mode: nil; tab-width: 2 -*-
2/*
3 * Copyright (C) 2010 Canonical Ltd
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 3 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Authored by: Neil Jagdish Patel <neil.patel@canonical.com>
18 */
19
20#ifndef PLACES_HOME_VIEW_H
21#define PLACES_HOME_VIEW_H
22
23#include <Nux/View.h>
24#include <Nux/Layout.h>
25#include <Nux/TextureArea.h>
26#include <NuxGraphics/GraphicsEngine.h>
27
28#include "Introspectable.h"
29
30#include <Nux/GridHLayout.h>
31
32#include "PlacesTile.h"
33#include "PlacesGroup.h"
34
35namespace unity
36{
37
38class PlacesHomeView : public unity::debug::Introspectable, public PlacesGroup
39{
40public:
41 PlacesHomeView();
42 ~PlacesHomeView();
43
44 void Refresh(PlacesGroup* foo =NULL);
45
46protected:
47 // Introspectable methods
48 std::string GetName() const;
49 void AddProperties(GVariantBuilder* builder);
50
51private:
52 static void DashVisible(GVariant* data, void* val);
53 void OnShortcutClicked(PlacesTile* _tile);
54 void CreateShortcutFromExec(const char* exec,
55 const char* name,
56 std::vector<std::string>& alternatives);
57 void CreateShortcutFromMime(const char* mime,
58 const char* name,
59 std::vector<std::string>& alternatives);
60
61private:
62 nux::GridHLayout* _layout;
63 std::vector<std::string> _browser_alternatives;
64 std::vector<std::string> _photo_alternatives;
65 std::vector<std::string> _email_alternatives;
66 std::vector<std::string> _music_alternatives;
67
68 guint _ubus_handle;
69};
70
71}
72#endif
730
=== modified file 'plugins/unityshell/src/ResultViewGrid.cpp'
--- plugins/unityshell/src/ResultViewGrid.cpp 2012-01-02 21:39:28 +0000
+++ plugins/unityshell/src/ResultViewGrid.cpp 2012-01-26 08:37:26 +0000
@@ -626,8 +626,19 @@
626 int half_width = recorded_dash_width_ / 2;626 int half_width = recorded_dash_width_ / 2;
627 int half_height = recorded_dash_height_;627 int half_height = recorded_dash_height_;
628628
629 int offset_x = MAX(MIN((x_position - half_width) / (half_width / 10), 5), -5);629 int offset_x, offset_y;
630 int offset_y = MAX(MIN(((y_position + absolute_y) - half_height) / (half_height / 10), 5), -5);630
631 /* Guard against divide-by-zero. SIGFPEs are not mythological
632 * contrary to popular belief */
633 if (half_width >= 10)
634 offset_x = MAX(MIN((x_position - half_width) / (half_width / 10), 5), -5);
635 else
636 offset_x = 0;
637
638 if (half_height >= 10)
639 offset_y = MAX(MIN(((y_position + absolute_y) - half_height) / (half_height / 10), 5), -5);
640 else
641 offset_y = 0;
631642
632 if (recorded_dash_width_ < 1 || recorded_dash_height_ < 1)643 if (recorded_dash_width_ < 1 || recorded_dash_height_ < 1)
633 {644 {
634645
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2012-01-16 16:03:32 +0000
+++ po/POTFILES.in 2012-01-26 08:37:26 +0000
@@ -3,7 +3,6 @@
3plugins/unityshell/src/LauncherController.cpp3plugins/unityshell/src/LauncherController.cpp
4plugins/unityshell/src/PanelMenuView.cpp4plugins/unityshell/src/PanelMenuView.cpp
5plugins/unityshell/src/PlacesGroup.cpp5plugins/unityshell/src/PlacesGroup.cpp
6plugins/unityshell/src/PlacesHomeView.cpp
7plugins/unityshell/src/SpacerLauncherIcon.cpp6plugins/unityshell/src/SpacerLauncherIcon.cpp
8plugins/unityshell/src/TrashLauncherIcon.cpp7plugins/unityshell/src/TrashLauncherIcon.cpp
9plugins/unityshell/src/BFBLauncherIcon.cpp8plugins/unityshell/src/BFBLauncherIcon.cpp
109
=== added file 'po/unity.pot'
--- po/unity.pot 1970-01-01 00:00:00 +0000
+++ po/unity.pot 2012-01-26 08:37:26 +0000
@@ -0,0 +1,120 @@
1# SOME DESCRIPTIVE TITLE.
2# Copyright (C) YEAR Canonical\ Ltd
3# This file is distributed under the same license as the PACKAGE package.
4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5#
6#, fuzzy
7msgid ""
8msgstr ""
9"Project-Id-Version: PACKAGE VERSION\n"
10"Report-Msgid-Bugs-To: ayatana-dev@lists.launchpad.net\n"
11"POT-Creation-Date: 2011-08-23 20:58-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
15"Language: \n"
16"MIME-Version: 1.0\n"
17"Content-Type: text/plain; charset=CHARSET\n"
18"Content-Transfer-Encoding: 8bit\n"
19
20msgid "Keep in launcher"
21msgstr ""
22
23msgid "Quit"
24msgstr ""
25
26msgid "Open"
27msgstr ""
28
29msgid "Eject"
30msgstr ""
31
32msgid "Eject parent drive"
33msgstr ""
34
35msgid "Safely remove"
36msgstr ""
37
38msgid "Safely remove parent drive"
39msgstr ""
40
41msgid "Unmount"
42msgstr ""
43
44msgid "The drive has been successfully ejected"
45msgstr ""
46
47msgid "Workspace Switcher"
48msgstr ""
49
50msgid "Search"
51msgstr ""
52
53msgid "Search across all places"
54msgstr ""
55
56msgid "See fewer results"
57msgstr ""
58
59msgid "Shortcuts"
60msgstr ""
61
62#. Media Apps
63msgid "Media Apps"
64msgstr ""
65
66#. Internet Apps
67msgid "Internet Apps"
68msgstr ""
69
70#. More Apps
71msgid "More Apps"
72msgstr ""
73
74#. Find Files
75msgid "Find Files"
76msgstr ""
77
78msgid "Browse the Web"
79msgstr ""
80
81#. Photos
82#. FIXME: Need to figure out the default
83msgid "View Photos"
84msgstr ""
85
86msgid "Check Email"
87msgstr ""
88
89msgid "Listen to Music"
90msgstr ""
91
92msgid "Drop To Add Application"
93msgstr ""
94
95msgid "Trash"
96msgstr ""
97
98msgid "Empty Trash..."
99msgstr ""
100
101msgid "Empty all items from Trash?"
102msgstr ""
103
104msgid "All items in the Trash will be permanently deleted."
105msgstr ""
106
107msgid "Empty Trash"
108msgstr ""
109
110msgid "Launcher & Menus"
111msgstr ""
112
113msgid "<b>Show the launcher when the pointer:</b>"
114msgstr ""
115
116msgid "Pushes the left edge of the screen"
117msgstr ""
118
119msgid "Touches the top left corner of the screen"
120msgstr ""
0121
=== removed file 'po/unity.pot'
--- po/unity.pot 2012-01-15 14:03:49 +0000
+++ po/unity.pot 1970-01-01 00:00:00 +0000
@@ -1,120 +0,0 @@
1# SOME DESCRIPTIVE TITLE.
2# Copyright (C) YEAR Canonical\ Ltd
3# This file is distributed under the same license as the PACKAGE package.
4# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5#
6#, fuzzy
7msgid ""
8msgstr ""
9"Project-Id-Version: PACKAGE VERSION\n"
10"Report-Msgid-Bugs-To: ayatana-dev@lists.launchpad.net\n"
11"POT-Creation-Date: 2011-08-23 20:58-0400\n"
12"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14"Language-Team: LANGUAGE <LL@li.org>\n"
15"Language: \n"
16"MIME-Version: 1.0\n"
17"Content-Type: text/plain; charset=CHARSET\n"
18"Content-Transfer-Encoding: 8bit\n"
19
20msgid "Keep in launcher"
21msgstr ""
22
23msgid "Quit"
24msgstr ""
25
26msgid "Open"
27msgstr ""
28
29msgid "Eject"
30msgstr ""
31
32msgid "Eject parent drive"
33msgstr ""
34
35msgid "Safely remove"
36msgstr ""
37
38msgid "Safely remove parent drive"
39msgstr ""
40
41msgid "Unmount"
42msgstr ""
43
44msgid "The drive has been successfully ejected"
45msgstr ""
46
47msgid "Workspace Switcher"
48msgstr ""
49
50msgid "Search"
51msgstr ""
52
53msgid "Search across all places"
54msgstr ""
55
56msgid "See fewer results"
57msgstr ""
58
59msgid "Shortcuts"
60msgstr ""
61
62#. Media Apps
63msgid "Media Apps"
64msgstr ""
65
66#. Internet Apps
67msgid "Internet Apps"
68msgstr ""
69
70#. More Apps
71msgid "More Apps"
72msgstr ""
73
74#. Find Files
75msgid "Find Files"
76msgstr ""
77
78msgid "Browse the Web"
79msgstr ""
80
81#. Photos
82#. FIXME: Need to figure out the default
83msgid "View Photos"
84msgstr ""
85
86msgid "Check Email"
87msgstr ""
88
89msgid "Listen to Music"
90msgstr ""
91
92msgid "Drop To Add Application"
93msgstr ""
94
95msgid "Trash"
96msgstr ""
97
98msgid "Empty Trash..."
99msgstr ""
100
101msgid "Empty all items from Trash?"
102msgstr ""
103
104msgid "All items in the Trash will be permanently deleted."
105msgstr ""
106
107msgid "Empty Trash"
108msgstr ""
109
110msgid "Launcher & Menus"
111msgstr ""
112
113msgid "<b>Show the launcher when the pointer:</b>"
114msgstr ""
115
116msgid "Pushes the left edge of the screen"
117msgstr ""
118
119msgid "Touches the top left corner of the screen"
120msgstr ""
1210
=== modified file 'standalone-clients/CMakeLists.txt'
--- standalone-clients/CMakeLists.txt 2012-01-17 11:30:30 +0000
+++ standalone-clients/CMakeLists.txt 2012-01-26 08:37:26 +0000
@@ -76,8 +76,6 @@
76 ${UNITY_SRC}/FontSettings.h76 ${UNITY_SRC}/FontSettings.h
77 ${UNITY_SRC}/IMTextEntry.cpp77 ${UNITY_SRC}/IMTextEntry.cpp
78 ${UNITY_SRC}/IMTextEntry.h78 ${UNITY_SRC}/IMTextEntry.h
79 ${UNITY_SRC}/PlacesHomeView.cpp
80 ${UNITY_SRC}/PlacesHomeView.h
81 ${UNITY_SRC}/PlacesGroup.cpp79 ${UNITY_SRC}/PlacesGroup.cpp
82 ${UNITY_SRC}/PlacesGroup.h80 ${UNITY_SRC}/PlacesGroup.h
83 ${UNITY_SRC}/PlacesTile.cpp81 ${UNITY_SRC}/PlacesTile.cpp
@@ -86,8 +84,6 @@
86 ${UNITY_SRC}/PlacesSimpleTile.h84 ${UNITY_SRC}/PlacesSimpleTile.h
87 ${UNITY_SRC}/PlacesVScrollBar.cpp85 ${UNITY_SRC}/PlacesVScrollBar.cpp
88 ${UNITY_SRC}/PlacesVScrollBar.h86 ${UNITY_SRC}/PlacesVScrollBar.h
89 ${UNITY_SRC}/HomeView.cpp
90 ${UNITY_SRC}/HomeView.h
91 ${UNITY_SRC}/DashStyle.cpp87 ${UNITY_SRC}/DashStyle.cpp
92 ${UNITY_SRC}/IconLoader.cpp88 ${UNITY_SRC}/IconLoader.cpp
93 ${UNITY_SRC}/IconLoader.h89 ${UNITY_SRC}/IconLoader.h
9490
=== modified file 'standalone-clients/standalone_dash.cpp'
--- standalone-clients/standalone_dash.cpp 2012-01-15 22:14:35 +0000
+++ standalone-clients/standalone_dash.cpp 2012-01-26 08:37:26 +0000
@@ -77,18 +77,8 @@
77 self->Init ();77 self->Init ();
78}78}
7979
80void
81ControlThread (nux::NThread* thread,
82 void* data)
83{
84 // sleep for 3 seconds
85 nux::SleepForMilliseconds (3000);
86 printf ("ControlThread successfully started\n");
87}
88
89int main(int argc, char **argv)80int main(int argc, char **argv)
90{81{
91 nux::SystemThread* st = NULL;
92 nux::WindowThread* wt = NULL;82 nux::WindowThread* wt = NULL;
9383
94 gtk_init (&argc, &argv);84 gtk_init (&argc, &argv);
@@ -109,13 +99,7 @@
109 &TestRunner::InitWindowThread,99 &TestRunner::InitWindowThread,
110 test_runner);100 test_runner);
111101
112 st = nux::CreateSystemThread (NULL, ControlThread, wt);
113
114 if (st)
115 st->Start (NULL);
116
117 wt->Run (NULL);102 wt->Run (NULL);
118 delete st;
119 delete wt;103 delete wt;
120 return 0;104 return 0;
121}105}
122106
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2012-01-25 17:26:09 +0000
+++ tests/CMakeLists.txt 2012-01-26 08:37:26 +0000
@@ -119,11 +119,12 @@
119 test_glib_variant.cpp119 test_glib_variant.cpp
120 ${CMAKE_CURRENT_BINARY_DIR}/test_glib_signals_utils_marshal.cpp120 ${CMAKE_CURRENT_BINARY_DIR}/test_glib_signals_utils_marshal.cpp
121 test_favorite_store_gsettings.cpp121 test_favorite_store_gsettings.cpp
122 test_home_lens.cpp
122 test_shortcut_model.cpp123 test_shortcut_model.cpp
123 test_shortcut_private.cpp124 test_shortcut_private.cpp
124 test_introspection.cpp125 test_introspection.cpp
125 test_main_xless.cpp126 test_main_xless.cpp
126 test_grabhandle.cpp127 test_grabhandle.cpp
127 ${UNITY_SRC}/AbstractLauncherIcon.h128 ${UNITY_SRC}/AbstractLauncherIcon.h
128 ${UNITY_SRC}/AbstractShortcutHint.h129 ${UNITY_SRC}/AbstractShortcutHint.h
129 ${UNITY_SRC}/Animator.cpp130 ${UNITY_SRC}/Animator.cpp
@@ -152,11 +153,11 @@
152 ${UNITY_SRC}/Timer.h153 ${UNITY_SRC}/Timer.h
153 ${UNITY_SRC}/WindowManager.cpp154 ${UNITY_SRC}/WindowManager.cpp
154 ${UNITY_SRC}/WindowManager.h155 ${UNITY_SRC}/WindowManager.h
155 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.cpp156 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle.cpp
156 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.cpp157 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-group.cpp
157 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.cpp158 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-impl-factory.cpp
158 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.cpp159 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-grab-handle-layout.cpp
159 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-texture.cpp160 ${CMAKE_SOURCE_DIR}/plugins/unity-mt-grab-handles/src/unity-mt-texture.cpp
160 )161 )
161 target_link_libraries(test-gtest-xless ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIB} ${GMOCK_MAIN_LIB})162 target_link_libraries(test-gtest-xless ${GTEST_BOTH_LIBRARIES} ${GMOCK_LIB} ${GMOCK_MAIN_LIB})
162 add_test(UnityGTestXless test-gtest-xless)163 add_test(UnityGTestXless test-gtest-xless)
163164
=== added file 'tests/test_home_lens.cpp'
--- tests/test_home_lens.cpp 1970-01-01 00:00:00 +0000
+++ tests/test_home_lens.cpp 2012-01-26 08:37:26 +0000
@@ -0,0 +1,362 @@
1#include <gtest/gtest.h>
2#include <glib-object.h>
3#include <dee.h>
4#include <string>
5#include <iostream>
6#include <stdexcept>
7#include <map>
8#include <memory>
9#include <sigc++/signal.h>
10#include <sigc++/trackable.h>
11
12#include <UnityCore/GLibWrapper.h>
13#include <UnityCore/Variant.h>
14#include <UnityCore/HomeLens.h>
15#include <UnityCore/Lens.h>
16#include <UnityCore/Lenses.h>
17
18#include "test_utils.h"
19
20using namespace std;
21using namespace unity::dash;
22
23namespace
24{
25
26/*
27 * FORWARDS
28 */
29
30class StaticTestLens;
31
32typedef struct {
33 StaticTestLens* lens;
34 gchar* search_string;
35} LensSearchClosure;
36
37static gboolean dispatch_global_search(gpointer userdata);
38
39
40/*
41 * Mock Lens instance that does not use DBus. The default search does like this:
42 * For input "bar" output:
43 *
44 * i = 0
45 * for letter in "bar":
46 * put result row [ "uri+$letter+$lens_id", "icon+$letter+$lens_id", i % 3, "mime+$letter+$lens_id", ...]
47 * i++
48 *
49 * The mock lens has 3 categories:
50 *
51 * 0) "cat0+$lens_id"
52 * 1) "cat1+$lens_id"
53 * 2) "Shared cat"
54 */
55class StaticTestLens : public Lens
56{
57public:
58 typedef std::shared_ptr<StaticTestLens> Ptr;
59
60 StaticTestLens(string const& id, string const& name, string const& description, string const& search_hint)
61 : Lens(id, "", "", name, "lens-icon.png",
62 description, search_hint, true, "",
63 ModelType::LOCAL)
64 {
65 search_in_global(true);
66
67 DeeModel* cats = categories()->model();
68 DeeModel* results = global_results()->model();
69 DeeModel* flters = filters()->model();
70
71 // Set model schemas
72 dee_model_set_schema(cats, "s", "s", "s", "a{sv}", NULL);
73 dee_model_set_schema(results, "s", "s", "u", "s", "s", "s", "s", NULL);
74 dee_model_set_schema(flters, "s", "s", "s", "s", "a{sv}", "b", "b", "b", NULL);
75
76 // Populate categories model
77 ostringstream cat0, cat1;
78 cat0 << "cat0+" << id;
79 cat1 << "cat1+" << id;
80 GVariantBuilder b;
81 g_variant_builder_init(&b, G_VARIANT_TYPE_VARDICT);
82 GVariant *asv = g_variant_builder_end(&b);
83
84 dee_model_append(cats, cat0.str().c_str(), "icon.png", "tile-vertical", asv);
85 dee_model_append(cats, cat1.str().c_str(), "icon.png", "tile-vertical", asv);
86 dee_model_append(cats, "Shared cat", "icon.png", "tile-vertical", asv);
87 }
88
89 virtual ~StaticTestLens() {}
90
91 virtual void DoGlobalSearch(string const& search_string)
92 {
93 DeeModel* model = global_results()->model();
94 GVariant** row_buf = g_new(GVariant*, 8);
95
96 row_buf[1] = g_variant_new_string("");
97 row_buf[3] = g_variant_new_string("");
98 row_buf[4] = g_variant_new_string("");
99 row_buf[5] = g_variant_new_string("");
100 row_buf[6] = g_variant_new_string("");
101 row_buf[7] = NULL;
102
103 unsigned int i;
104 for (i = 0; i < search_string.size(); i++)
105 {
106 ostringstream uri;
107 uri << "uri+" << search_string.at(i) << "+" << id();
108 row_buf[0] = g_variant_new_string(uri.str().c_str());
109 row_buf[2] = g_variant_new_uint32(i % 3);
110
111 dee_model_append_row(model, row_buf);
112 }
113
114 g_free(row_buf);
115 }
116
117 void GlobalSearch(string const& search_string)
118 {
119 /* Dispatch search async, because that's */
120 LensSearchClosure* closure = g_new0(LensSearchClosure, 1);
121 closure->lens = this;
122 closure->search_string = g_strdup(search_string.c_str());
123 g_idle_add(dispatch_global_search, closure);
124 }
125
126 void Search(string const& search_string)
127 {
128
129 }
130
131 void Activate(string const& uri)
132 {
133
134 }
135
136 void Preview(string const& uri)
137 {
138
139 }
140
141};
142
143static gboolean dispatch_global_search(gpointer userdata)
144{
145 LensSearchClosure* closure = (LensSearchClosure*) userdata;
146
147 closure->lens->DoGlobalSearch(closure->search_string);
148
149 g_free(closure->search_string);
150 g_free(closure);
151
152 return FALSE;
153}
154
155/*
156 * Mock Lenses class
157 */
158class StaticTestLenses : public Lenses
159{
160public:
161 typedef std::shared_ptr<StaticTestLenses> Ptr;
162
163 StaticTestLenses()
164 {
165 count.SetGetterFunction(sigc::mem_fun(&list_, &Lenses::LensList::size));
166 }
167
168 virtual ~StaticTestLenses() {}
169
170 Lenses::LensList GetLenses() const
171 {
172 return list_;
173 }
174
175 Lens::Ptr GetLens(std::string const& lens_id) const
176 {
177 for (auto lens : list_)
178 {
179 if (lens->id() == lens_id)
180 return lens;
181 }
182 return Lens::Ptr();
183 }
184
185 Lens::Ptr GetLensAtIndex(std::size_t index) const
186 {
187 return list_.at(index);
188 }
189
190protected:
191 Lenses::LensList list_;
192};
193
194class TwoStaticTestLenses : public StaticTestLenses
195{
196public:
197 TwoStaticTestLenses()
198 : lens_1_(new StaticTestLens("first.lens", "First Lens", "The very first lens", "First search hint"))
199 , lens_2_(new StaticTestLens("second.lens", "Second Lens", "The second lens", "Second search hint"))
200 {
201 list_.push_back(lens_1_);
202 list_.push_back(lens_2_);
203 }
204
205private:
206 Lens::Ptr lens_1_;
207 Lens::Ptr lens_2_;
208};
209
210TEST(TestHomeLens, TestConstruction)
211{
212 HomeLens home_lens_("name", "description", "searchhint");
213
214 EXPECT_EQ(home_lens_.id(), "home.lens");
215 EXPECT_EQ(home_lens_.connected, false);
216 EXPECT_EQ(home_lens_.search_in_global, false);
217 EXPECT_EQ(home_lens_.name, "name");
218 EXPECT_EQ(home_lens_.description, "description");
219 EXPECT_EQ(home_lens_.search_hint, "searchhint");
220}
221
222TEST(TestHomeLens, TestInitiallyEmpty)
223{
224 HomeLens home_lens_("name", "description", "searchhint");
225 DeeModel* results = home_lens_.results()->model();
226 DeeModel* categories = home_lens_.categories()->model();;
227 DeeModel* filters = home_lens_.filters()->model();;
228
229 EXPECT_EQ(dee_model_get_n_rows(results), 0);
230 EXPECT_EQ(dee_model_get_n_rows(categories), 0);
231 EXPECT_EQ(dee_model_get_n_rows(filters), 0);
232
233 EXPECT_EQ(home_lens_.count(), 0);
234}
235
236TEST(TestHomeLens, TestTwoStaticLenses)
237{
238 HomeLens home_lens_("name", "description", "searchhint");
239 TwoStaticTestLenses lenses_;
240
241 home_lens_.AddLenses(lenses_);
242
243 EXPECT_EQ(home_lens_.count, (size_t) 2);
244
245 /* Test iteration of registered lensess */
246 map<string,string> remaining;
247 remaining["first.lens"] = "";
248 remaining["second.lens"] = "";
249 for (auto lens : home_lens_.GetLenses())
250 {
251 remaining.erase(lens->id());
252 }
253
254 EXPECT_EQ(remaining.size(), 0);
255
256 /* Test sorting and GetAtIndex */
257 EXPECT_EQ(home_lens_.GetLensAtIndex(0)->id(), "first.lens");
258 EXPECT_EQ(home_lens_.GetLensAtIndex(1)->id(), "second.lens");
259}
260
261TEST(TestHomeLens, TestCategoryMerging)
262{
263 HomeLens home_lens_("name", "description", "searchhint");
264 TwoStaticTestLenses lenses_;
265 DeeModel* cats = home_lens_.categories()->model();
266 DeeModelIter* iter;
267 unsigned int cat0_first = 0,
268 cat1_first = 1,
269 cat_shared = 2,
270 cat0_second = 3,
271 cat1_second = 4;
272 const unsigned int NAME_COLUMN = 0;
273
274 home_lens_.AddLenses(lenses_);
275
276 EXPECT_EQ(dee_model_get_n_rows(cats), 5); // 5 because each lens has 3 cats, but 1 is shared between them
277
278 /* Validate the merged categories */
279 iter = dee_model_get_iter_at_row(cats, cat0_first);
280 EXPECT_EQ("cat0+first.lens", string(dee_model_get_string(cats, iter, NAME_COLUMN)));
281
282 iter = dee_model_get_iter_at_row(cats, cat1_first);
283 EXPECT_EQ("cat1+first.lens", string(dee_model_get_string(cats, iter, NAME_COLUMN)));
284
285 iter = dee_model_get_iter_at_row(cats, cat_shared);
286 EXPECT_EQ("Shared cat", string(dee_model_get_string(cats, iter, NAME_COLUMN)));
287
288 iter = dee_model_get_iter_at_row(cats, cat0_second);
289 EXPECT_EQ("cat0+second.lens", string(dee_model_get_string(cats, iter, NAME_COLUMN)));
290
291 iter = dee_model_get_iter_at_row(cats, cat1_second);
292 EXPECT_EQ("cat1+second.lens", string(dee_model_get_string(cats, iter, NAME_COLUMN)));
293}
294
295// It's not that we must not support filters. It is just not implemented yet.
296// But we actively test against it to make sure we don't end up with broken
297// filters in the UI. When/if we land support for filters on the home screen
298// this test should obviously be removed
299TEST(TestHomeLens, TestIgnoreFilters)
300{
301 HomeLens home_lens_("name", "description", "searchhint");
302 TwoStaticTestLenses lenses_;
303 DeeModel* filters = home_lens_.filters()->model();
304
305 EXPECT_EQ(dee_model_get_n_rows(filters), 0);
306}
307
308TEST(TestHomeLens, TestOneSearch)
309{
310 HomeLens home_lens_("name", "description", "searchhint");
311 TwoStaticTestLenses lenses_;
312 DeeModel* results = home_lens_.results()->model();
313 DeeModel* cats = home_lens_.categories()->model();
314 DeeModel* filters = home_lens_.filters()->model();
315 DeeModelIter* iter;
316 unsigned int cat0_first = 0,
317 cat1_first = 1,
318 cat_shared = 2,
319 cat0_second = 3,
320 cat1_second = 4;
321 const unsigned int URI_COLUMN = 0;
322 const unsigned int CAT_COLUMN = 2;
323
324 home_lens_.AddLenses(lenses_);
325
326 home_lens_.Search("ape");
327
328 Utils::WaitForTimeoutMSec();
329
330 /* Validate counts */
331 EXPECT_EQ(dee_model_get_n_rows(results), 6); // 3 hits from each lens
332 EXPECT_EQ(dee_model_get_n_rows(cats), 5); // 5 because each lens has 3 cats, but 1 is shared between them
333 EXPECT_EQ(dee_model_get_n_rows(filters), 0); // We ignore filters deliberately currently
334
335 /* Validate results. In particular that we get the correct merged
336 * category offsets assigned */
337 iter = dee_model_get_iter_at_row(results, 0);
338 EXPECT_EQ(string("uri+a+first.lens"), string(dee_model_get_string(results, iter, URI_COLUMN)));
339 EXPECT_EQ(cat0_first, dee_model_get_uint32(results, iter, CAT_COLUMN));
340
341 iter = dee_model_get_iter_at_row(results, 1);
342 EXPECT_EQ(string("uri+p+first.lens"), string(dee_model_get_string(results, iter, URI_COLUMN)));
343 EXPECT_EQ(cat1_first, dee_model_get_uint32(results, iter, CAT_COLUMN));
344
345 iter = dee_model_get_iter_at_row(results, 2);
346 EXPECT_EQ(string("uri+e+first.lens"), string(dee_model_get_string(results, iter, URI_COLUMN)));
347 EXPECT_EQ(cat_shared, dee_model_get_uint32(results, iter, CAT_COLUMN));
348
349 iter = dee_model_get_iter_at_row(results, 3);
350 EXPECT_EQ(string("uri+a+second.lens"), string(dee_model_get_string(results, iter, URI_COLUMN)));
351 EXPECT_EQ(cat0_second, dee_model_get_uint32(results, iter, CAT_COLUMN));
352
353 iter = dee_model_get_iter_at_row(results, 4);
354 EXPECT_EQ(string("uri+p+second.lens"), string(dee_model_get_string(results, iter, URI_COLUMN)));
355 EXPECT_EQ(cat1_second, dee_model_get_uint32(results, iter, CAT_COLUMN));
356
357 iter = dee_model_get_iter_at_row(results, 5);
358 EXPECT_EQ(string("uri+e+second.lens"), string(dee_model_get_string(results, iter, URI_COLUMN)));
359 EXPECT_EQ(cat_shared, dee_model_get_uint32(results, iter, CAT_COLUMN));
360}
361
362}
0363
=== modified file 'tests/test_utils.h'
--- tests/test_utils.h 2011-12-02 12:03:27 +0000
+++ tests/test_utils.h 2012-01-26 08:37:26 +0000
@@ -51,6 +51,33 @@
51 return g_timeout_add_seconds(timeout_duration, TimeoutCallback, timeout_reached);51 return g_timeout_add_seconds(timeout_duration, TimeoutCallback, timeout_reached);
52 }52 }
5353
54 static guint32 ScheduleTimeoutMSec(bool* timeout_reached, unsigned int timeout_duration = 10)
55 {
56 return g_timeout_add(timeout_duration, TimeoutCallback, timeout_reached);
57 }
58
59 static void WaitForTimeout(unsigned int timeout_duration = 10)
60 {
61 bool timeout_reached = false;
62 guint32 timeout_id = ScheduleTimeout(&timeout_reached, timeout_duration);
63
64 while (!timeout_reached)
65 g_main_context_iteration(g_main_context_get_thread_default(), TRUE);
66
67 g_source_remove(timeout_id);
68 }
69
70 static void WaitForTimeoutMSec(unsigned int timeout_duration = 10)
71 {
72 bool timeout_reached = false;
73 guint32 timeout_id = ScheduleTimeoutMSec(&timeout_reached, timeout_duration);
74
75 while (!timeout_reached)
76 g_main_context_iteration(g_main_context_get_thread_default(), TRUE);
77
78 g_source_remove(timeout_id);
79 }
80
54private:81private:
55 static gboolean TimeoutCallback(gpointer data)82 static gboolean TimeoutCallback(gpointer data)
56 {83 {