Merge lp:~osomon/unity/dash-custom-home-screen into lp:unity

Proposed by Olivier Tilloy on 2011-11-23
Status: Rejected
Rejected by: Gord Allott on 2012-01-20
Proposed branch: lp:~osomon/unity/dash-custom-home-screen
Merge into: lp:unity
Diff against target: 387 lines (+254/-59)
4 files modified
plugins/unityshell/src/JSONParser.cpp (+13/-0)
plugins/unityshell/src/JSONParser.h (+4/-0)
plugins/unityshell/src/PlacesHomeView.cpp (+233/-59)
plugins/unityshell/src/PlacesHomeView.h (+4/-0)
To merge this branch: bzr merge lp:~osomon/unity/dash-custom-home-screen
Reviewer Review Type Date Requested Status
Olivier Tilloy Disapprove on 2011-12-15
Gord Allott (community) 2011-11-23 Needs Information on 2011-11-29
Review via email: mp+83175@code.launchpad.net

Commit message

Allow customizing the dash’s home screen.

The contents of the custom home screen are described in a JSON file called HomeShortcutsCustomized.json.

The file is looked for in the following locations in decreasing order of priority:
    - $XDG_CONFIG_HOME/unity/ (defaults to $HOME/.config/unity/)
    - $DIR/unity/ for $DIR in $XDG_CONFIG_DIRS

The syntax of the file is as follows:
==========================================================================
{
  "shortcut1": {
    "source": $source,
    "name": $name,
    "name[fr]": $name_in_french,
    […]
    "icon": $icon
  },
  "shortcut2": {
    […]
  },
  […]
}
==========================================================================

The source attribute may either be a desktop file (full path or just its basename if it is located in a standard directory), or a lens file (basename only). This attribute is mandatory.
The 'name' attribute is optional. If present, it will override the default display name as advertised by the desktop file or by the lens. The name can be localized in several languages using the square brackets suffix notation, in which case the locale matching the system’s will be used, defaulting to the untranslated 'name' attribute if necessary.
The 'icon' attribute is optional. If present, it will override the default icon as advertised by the desktop file or by the lens. It should be a full path name.
If the source is a lens, the optional 'filter' attribute allows specifying a filter in the form "$name:$value", e.g. "type:videos" for the files lens.
If the source is a lens, the optional 'section' attribute allows specifying a section number (an integer starting at index 0).

Description of the change

So it seems that the patch I wrote some time ago to make the home screen of the dash customizable just like in unity-2d is being considered for inclusion in Oneiric…

I refreshed it to apply against the current trunk, functional testing and comments are welcome. Please bear with me as I’m not really familiar with unity’s code base, coding style and conventions.

To post a comment you must log in.

I am not sure how this relates to bug #885738...

Olivier Tilloy (osomon) wrote :

Yeah, I know this is in apparent contradiction with the description of bug #885738 and with what was discussed at UDS.
But OEM needs this functionality for a project running Oneiric, and they want the patch reviewed and approved by Unity core developers, hence this merge request. Whether it should actually be merged into trunk is beyond me, but a review and functional tests would be nice. Thanks!

Gary Ekker (gekker) wrote :

We need an SRU for oneiric that includes this patch.

As per bug #885738, this won't be a problem for precise and beyond.

Gord Allott (gordallott) wrote :

On 23/11/11 15:31, Olivier Tilloy wrote:
> Olivier Tilloy has proposed merging lp:~osomon/unity/dash-custom-home-screen into lp:unity.
>
> Requested reviews:
> Unity Team (unity-team): code functional
> Related bugs:
> Bug #785840 in unity: "Home dash icons need to be customizable"
> https://bugs.launchpad.net/unity/+bug/785840
>
> For more details, see:
> https://code.launchpad.net/~osomon/unity/dash-custom-home-screen/+merge/83175
>
> So it seems that the patch I wrote some time ago to make the home screen of the dash customizable just like in unity-2d is being considered for inclusion in Oneiric…
>
> I refreshed it to apply against the current trunk, functional testing and comments are welcome. Please bear with me as I’m not really familiar with unity’s code base, coding style and conventions.

So I have a few questions and a few notes;

I have some concerns over adding extra translation in a non standard
format, there are good well established formats for .desktop files and
in source code, but nothing for a custom json file. We should really
either be extending the desktop files to include an even more generic
name that is translated such as:
X-Unity-Generic-Action=Browse the web

or creating new desktop files and installing them somewhere like
/usr/share/unity/dash/home - using those to store the required
information. I'd like a compelling reason to be using json over desktop
files really.

I'm not a fan of having the old code and this new code sit side-by-side,
we should stick to one method and lose the other. Which means that we
should be including a default json file in unity that gets installed to
somewhere sane like /usr/share/unity/dash/.

There is also an issue in that non of this is tested, thus it can't be
approved until tests exist.

small thing, but style wise, we shouldn't be using #define's, (the
define that exists is from quite old code before we started using this
style)

namespace
{
  std::string custom_shortcuts_file =
"/unity/HomeShortcuts/Customized.json";
}

is a preferred style.

Apart from that the code looks good

 review needs-info

--
Gordon Allott
Canonical Ltd.
27 Floor, Millbank Tower
London SW1P 4QP
www.canonical.com

review: Needs Information
1741. By Olivier Tilloy on 2011-11-30

Cosmetics: replace a #define by a global variable in the unnamed namespace.

Olivier Tilloy (osomon) wrote :

Thanks for the review Gordon!

> I have some concerns over adding extra translation in a non standard
> format, there are good well established formats for .desktop files and
> in source code, but nothing for a custom json file. We should really
> either be extending the desktop files to include an even more generic
> name that is translated such as:
> X-Unity-Generic-Action=Browse the web
>
> or creating new desktop files and installing them somewhere like
> /usr/share/unity/dash/home - using those to store the required
> information. I'd like a compelling reason to be using json over desktop
> files really.

Right, installing desktop files in a custom location to override attributes such as (translated) name, icon, etc. sounds like a good option.
The reason I allowed overriding those attributes directly in the JSON file was to minimize the work of whoever wants to customize the dash, especially in the context of OEM customizations: in most cases that’s only one additional file to ship.

> I'm not a fan of having the old code and this new code sit side-by-side,
> we should stick to one method and lose the other. Which means that we
> should be including a default json file in unity that gets installed to
> somewhere sane like /usr/share/unity/dash/.

That sounds like a very good idea, thanks for the suggestion.

> There is also an issue in that non of this is tested, thus it can't be
> approved until tests exist.

Right. At this point it’s unclear to me whether we actually want this in the trunk, since the (presumably) only target is Oneiric. The point of the exercise of submitting a merge request against the trunk was to have a formal review by someone competent. I’ll clarify this with involved parties.
Obviously tests would be very nice to have, but it may be beyond the scope of the patch at this point.

> small thing, but style wise, we shouldn't be using #define's, (the
> define that exists is from quite old code before we started using this
> style)

Thanks for pointing this out, I fixed it.

Gord Allott (gordallott) wrote :

> Right. At this point it’s unclear to me whether we actually want this in the trunk, since the (presumably) only target is Oneiric. The point of the exercise of submitting a merge request against the trunk was to have a formal review by someone competent. I’ll clarify this with involved parties.
> Obviously tests would be very nice to have, but it may be beyond the scope of the patch at this point.

So if we aren't targeting trunk you need to re-submit this against lp:unity/4.0 not lp:unity - small thing, but the auto lander will put this in trunk if we leave it against lp:unity

Olivier Tilloy (osomon) wrote :

Rejecting as this functionality should target oneiric (4.0), not trunk.

Superseded by https://code.launchpad.net/~osomon/unity/4.0-dash-custom-home-screen/+merge/85852.

review: Disapprove

Unmerged revisions

1741. By Olivier Tilloy on 2011-11-30

Cosmetics: replace a #define by a global variable in the unnamed namespace.

1740. By Olivier Tilloy on 2011-11-23

Allow customizing the dash’s home screen.

The contents of the custom home screen are described in a JSON file called HomeShortcutsCustomized.json.

The file is looked for in the following locations in decreasing order of priority:
    - $XDG_CONFIG_HOME/unity/ (defaults to $HOME/.config/unity/)
    - $DIR/unity/ for $DIR in $XDG_CONFIG_DIRS

The syntax of the file is as follows:
==========================================================================
{
  "shortcut1": {
    "source": $source,
    "name": $name,
    "name[fr]": $name_in_french,
    […]
    "icon": $icon
  },
  "shortcut2": {
    […]
  },
  […]
}
==========================================================================

The source attribute may either be a desktop file (full path or just its basename if it is located in a standard directory), or a lens file (basename only). This attribute is mandatory.
The 'name' attribute is optional. If present, it will override the default display name as advertised by the desktop file or by the lens. The name can be localized in several languages using the square brackets suffix notation, in which case the locale matching the system’s will be used, defaulting to the untranslated 'name' attribute if necessary.
The 'icon' attribute is optional. If present, it will override the default icon as advertised by the desktop file or by the lens. It should be a full path name.
If the source is a lens, the optional 'filter' attribute allows specifying a filter in the form "$name:$value", e.g. "type:videos" for the files lens.
If the source is a lens, the optional 'section' attribute allows specifying a section number (an integer starting at index 0).

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'plugins/unityshell/src/JSONParser.cpp'
2--- plugins/unityshell/src/JSONParser.cpp 2011-09-15 04:28:49 +0000
3+++ plugins/unityshell/src/JSONParser.cpp 2011-11-30 10:25:11 +0000
4@@ -145,6 +145,19 @@
5 values[i] = json_array_get_double_element(array, i);
6 }
7
8+void Parser::ReadString(std::string const& node_name,
9+ std::string const& member_name,
10+ std::string& value) const
11+{
12+ JsonObject* object = GetNodeObject(node_name);
13+
14+ if (!object)
15+ return;
16+
17+ if (json_object_has_member(object, member_name.c_str()))
18+ value = json_object_get_string_member(object, member_name.c_str());
19+}
20+
21 void Parser::ReadColor(std::string const& node_name,
22 std::string const& member_name,
23 std::string const& opacity_name,
24
25=== modified file 'plugins/unityshell/src/JSONParser.h'
26--- plugins/unityshell/src/JSONParser.h 2011-09-15 04:28:49 +0000
27+++ plugins/unityshell/src/JSONParser.h 2011-11-30 10:25:11 +0000
28@@ -58,6 +58,10 @@
29 std::string const& member_name,
30 std::vector<double>& values) const;
31
32+ void ReadString(std::string const& node_name,
33+ std::string const& member_name,
34+ std::string& value) const;
35+
36 void ReadColor(std::string const& node_name,
37 std::string const& member_name,
38 std::string const& opacity_name,
39
40=== modified file 'plugins/unityshell/src/PlacesHomeView.cpp'
41--- plugins/unityshell/src/PlacesHomeView.cpp 2011-10-25 16:32:49 +0000
42+++ plugins/unityshell/src/PlacesHomeView.cpp 2011-11-30 10:25:11 +0000
43@@ -39,16 +39,25 @@
44
45 #include "PlacesHomeView.h"
46 #include "PlacesSimpleTile.h"
47+#include "JSONParser.h"
48
49 #include "DashStyle.h"
50 #include <UnityCore/GLibWrapper.h>
51 #include <UnityCore/Variant.h>
52
53+#include <sstream>
54 #include <string>
55 #include <vector>
56
57+#include <boost/algorithm/string.hpp>
58+
59 #define DELTA_DOUBLE_REQUEST 500000000
60
61+namespace
62+{
63+ const std::string custom_shortcuts_file = "/unity/HomeShortcutsCustomized.json";
64+}
65+
66 namespace unity
67 {
68
69@@ -135,7 +144,9 @@
70
71 expanded.connect(sigc::mem_fun(this, &PlacesHomeView::Refresh));
72
73- Refresh();
74+ // Wait for the asynchronous loading of the lenses to complete
75+ // before refreshing the view for the first time.
76+ _lenses.lenses_loaded.connect(sigc::bind(sigc::mem_fun(this, &PlacesHomeView::Refresh), (PlacesGroup*) NULL));
77 }
78
79 PlacesHomeView::~PlacesHomeView()
80@@ -161,64 +172,227 @@
81
82 _layout->Clear();
83
84- // Media Apps
85- markup = g_strdup_printf(temp, _("Media Apps"));
86- shortcut = new Shortcut(PKGDATADIR"/find_media_apps.png",
87- markup,
88- icon_size);
89- shortcut->_id = TYPE_PLACE;
90- shortcut->_place_id = g_strdup("applications.lens?filter_type=media");
91- shortcut->_place_section = 9;
92- _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
93- shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
94- g_free(markup);
95-
96- // Internet Apps
97- markup = g_strdup_printf(temp, _("Internet Apps"));
98- shortcut = new Shortcut(PKGDATADIR"/find_internet_apps.png",
99- markup,
100- icon_size);
101- shortcut->_id = TYPE_PLACE;
102- shortcut->_place_id = g_strdup("applications.lens?filter_type=internet");
103- shortcut->_place_section = 8;
104- _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
105- shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
106- g_free(markup);
107-
108- // More Apps
109- markup = g_strdup_printf(temp, _("More Apps"));
110- shortcut = new Shortcut(PKGDATADIR"/find_more_apps.png",
111- markup,
112- icon_size);
113- shortcut->_id = TYPE_PLACE;
114- shortcut->_place_id = g_strdup("applications.lens");
115- shortcut->_place_section = 0;
116- _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
117- shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
118- g_free(markup);
119-
120- // Find Files
121- markup = g_strdup_printf(temp, _("Find Files"));
122- shortcut = new Shortcut(PKGDATADIR"/find_files.png",
123- markup,
124- icon_size);
125- shortcut->_id = TYPE_PLACE;
126- shortcut->_place_id = g_strdup("files.lens");
127- shortcut->_place_section = 0;
128- _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
129- shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
130- g_free(markup);
131-
132- // Browser
133- CreateShortcutFromMime("x-scheme-handler/http", _("Browse the Web"), _browser_alternatives);
134-
135- // Photos
136- // FIXME: Need to figure out the default
137- CreateShortcutFromExec("shotwell", _("View Photos"), _photo_alternatives);
138-
139- CreateShortcutFromMime("x-scheme-handler/mailto", _("Check Email"), _email_alternatives);
140-
141- CreateShortcutFromMime("audio/x-vorbis+ogg", _("Listen to Music"), _music_alternatives);
142+ // The following code path allows one to replace the default dash home screen
143+ // with a custom home screen, the contents of which are described in a JSON
144+ // file. The file is looked for in the following locations in decreasing order
145+ // of priority:
146+ // - $XDG_CONFIG_HOME/unity/ (defaults to $HOME/.config/unity/)
147+ // - $DIR/unity/ for $DIR in $XDG_CONFIG_DIRS
148+ // The syntax of the file is as follows:
149+ // ==========================================================================
150+ // {
151+ // "shortcut1": {
152+ // "source": $source,
153+ // "name": $name,
154+ // "name[fr]": $name_in_french,
155+ // […]
156+ // "icon": $icon
157+ // },
158+ // "shortcut2": {
159+ // […]
160+ // },
161+ // […]
162+ // }
163+ // ==========================================================================
164+ // The source attribute may either be a desktop file (full path or just its
165+ // basename if it is located in a standard directory), or a lens file
166+ // (basename only). This attribute is mandatory.
167+ // The 'name' attribute is optional. If present, it will override the default
168+ // display name as advertised by the desktop file or by the lens. The name can
169+ // be localized in several languages using the square brackets suffix
170+ // notation, in which case the locale matching the system’s will be used,
171+ // defaulting to the untranslated 'name' attribute if necessary.
172+ // The 'icon' attribute is optional. If present, it will override the default
173+ // icon as advertised by the desktop file or by the lens. It should be a full
174+ // path name.
175+ // If the source is a lens, the optional 'filter' attribute allows specifying
176+ // a filter in the form "$name:$value", e.g. "type:videos" for the files lens.
177+ // If the source is a lens, the optional 'section' attribute allows specifying
178+ // a section number (an integer starting at index 0).
179+
180+ std::string customShortcuts;
181+ std::ostringstream file;
182+ // Look for a custom shortcuts file in the user’s home directory first.
183+ file << g_get_user_config_dir() << custom_shortcuts_file;
184+ if (g_file_test(file.str().c_str(), G_FILE_TEST_EXISTS))
185+ {
186+ customShortcuts = file.str();
187+ }
188+ else
189+ {
190+ // Fall back on the standard XDG directories.
191+ gchar** config = (gchar**) g_get_system_config_dirs();
192+ for (gint i = 0; config[i]; ++i)
193+ {
194+ std::ostringstream file;
195+ file << config[i] << custom_shortcuts_file;
196+ if (g_file_test(file.str().c_str(), G_FILE_TEST_EXISTS))
197+ {
198+ customShortcuts = file.str();
199+ break;
200+ }
201+ }
202+ }
203+ json::Parser parser;
204+ if ((customShortcuts == "") || !parser.Open(customShortcuts))
205+ {
206+ // Media Apps
207+ markup = g_strdup_printf(temp, _("Media Apps"));
208+ shortcut = new Shortcut(PKGDATADIR"/find_media_apps.png",
209+ markup,
210+ icon_size);
211+ shortcut->_id = TYPE_PLACE;
212+ shortcut->_place_id = g_strdup("applications.lens?filter_type=media");
213+ shortcut->_place_section = 9;
214+ _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
215+ shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
216+ g_free(markup);
217+
218+ // Internet Apps
219+ markup = g_strdup_printf(temp, _("Internet Apps"));
220+ shortcut = new Shortcut(PKGDATADIR"/find_internet_apps.png",
221+ markup,
222+ icon_size);
223+ shortcut->_id = TYPE_PLACE;
224+ shortcut->_place_id = g_strdup("applications.lens?filter_type=internet");
225+ shortcut->_place_section = 8;
226+ _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
227+ shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
228+ g_free(markup);
229+
230+ // More Apps
231+ markup = g_strdup_printf(temp, _("More Apps"));
232+ shortcut = new Shortcut(PKGDATADIR"/find_more_apps.png",
233+ markup,
234+ icon_size);
235+ shortcut->_id = TYPE_PLACE;
236+ shortcut->_place_id = g_strdup("applications.lens");
237+ shortcut->_place_section = 0;
238+ _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
239+ shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
240+ g_free(markup);
241+
242+ // Find Files
243+ markup = g_strdup_printf(temp, _("Find Files"));
244+ shortcut = new Shortcut(PKGDATADIR"/find_files.png",
245+ markup,
246+ icon_size);
247+ shortcut->_id = TYPE_PLACE;
248+ shortcut->_place_id = g_strdup("files.lens");
249+ shortcut->_place_section = 0;
250+ _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
251+ shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
252+ g_free(markup);
253+
254+ // Browser
255+ CreateShortcutFromMime("x-scheme-handler/http", _("Browse the Web"), _browser_alternatives);
256+
257+ // Photos
258+ // FIXME: Need to figure out the default
259+ CreateShortcutFromExec("shotwell", _("View Photos"), _photo_alternatives);
260+
261+ CreateShortcutFromMime("x-scheme-handler/mailto", _("Check Email"), _email_alternatives);
262+
263+ CreateShortcutFromMime("audio/x-vorbis+ogg", _("Listen to Music"), _music_alternatives);
264+ }
265+ else
266+ {
267+ // The custom shortcuts file can contain up to 8 shortcuts.
268+ for (int i = 0; i < 8; ++i)
269+ {
270+ std::ostringstream oss;
271+ oss << "shortcut" << (i + 1);
272+ std::string slot = oss.str();
273+ std::string source;
274+ parser.ReadString(slot, "source", source);
275+ if (source != "")
276+ {
277+ std::string icon;
278+ parser.ReadString(slot, "icon", icon);
279+
280+ std::string name;
281+ gchar** languages = (gchar**) g_get_language_names();
282+ for (gint i = 0; languages[i]; ++i)
283+ {
284+ std::ostringstream key;
285+ key << "name[" << languages[i] << "]";
286+ parser.ReadString(slot, key.str(), name);
287+ if (name != "")
288+ break;
289+ }
290+ if (name == "")
291+ {
292+ parser.ReadString(slot, "name", name);
293+ }
294+
295+ if (boost::iends_with(source, ".desktop"))
296+ {
297+ GDesktopAppInfo* info = NULL;
298+ if (boost::istarts_with(source, "/"))
299+ info = g_desktop_app_info_new_from_filename(source.c_str());
300+ else
301+ info = g_desktop_app_info_new(source.c_str());
302+ if (G_IS_DESKTOP_APP_INFO(info))
303+ {
304+ if (name == "")
305+ name = g_app_info_get_display_name(G_APP_INFO(info));
306+ if (icon == "")
307+ {
308+ gchar* cicon = g_icon_to_string(g_app_info_get_icon(G_APP_INFO(info)));
309+ icon = cicon;
310+ g_free(cicon);
311+ }
312+ gchar* exec = g_strdup(g_app_info_get_executable(G_APP_INFO(info)));
313+ markup = g_strdup_printf(temp, name.c_str());
314+ shortcut = new Shortcut(icon.c_str(), markup, icon_size);
315+ shortcut->_id = TYPE_EXEC;
316+ shortcut->_exec = exec;
317+ _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
318+ shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
319+ g_free(markup);
320+ g_object_unref(info);
321+ }
322+ }
323+ else if (boost::iends_with(source, ".lens"))
324+ {
325+ unity::dash::Lens::Ptr lens = _lenses.GetLens(source);
326+ if (lens)
327+ {
328+ if (name == "")
329+ name = lens->name;
330+ if (icon == "")
331+ icon = lens->icon_hint;
332+ }
333+ std::string filter;
334+ parser.ReadString(slot, "filter", filter);
335+ int section = -1;
336+ parser.ReadInt(slot, "section", section);
337+ markup = g_strdup_printf(temp, name.c_str());
338+ shortcut = new Shortcut(icon.c_str(), markup, icon_size);
339+ shortcut->_id = TYPE_PLACE;
340+ if (filter != "")
341+ {
342+ std::vector<std::string> filter_key_value;
343+ boost::split(filter_key_value, filter, boost::is_any_of(":"));
344+ if (filter_key_value.size() == 2)
345+ shortcut->_place_id = g_strdup_printf("%s?filter_%s=%s",
346+ source.c_str(),
347+ filter_key_value[0].c_str(),
348+ filter_key_value[1].c_str());
349+ else
350+ shortcut->_place_id = g_strdup(source.c_str());
351+ }
352+ else
353+ shortcut->_place_id = g_strdup(source.c_str());
354+ if (section != -1)
355+ shortcut->_place_section = section;
356+ _layout->AddView(shortcut, 1, nux::eLeft, nux::eFull);
357+ shortcut->sigClick.connect(sigc::mem_fun(this, &PlacesHomeView::OnShortcutClicked));
358+ g_free(markup);
359+ }
360+ }
361+ }
362+ }
363
364 SetExpanded(true);
365 SetCounts(8, 8);
366
367=== modified file 'plugins/unityshell/src/PlacesHomeView.h'
368--- plugins/unityshell/src/PlacesHomeView.h 2011-09-02 03:40:15 +0000
369+++ plugins/unityshell/src/PlacesHomeView.h 2011-11-30 10:25:11 +0000
370@@ -29,6 +29,8 @@
371
372 #include <Nux/GridHLayout.h>
373
374+#include <UnityCore/FilesystemLenses.h>
375+
376 #include "PlacesTile.h"
377 #include "PlacesGroup.h"
378
379@@ -67,6 +69,8 @@
380 std::vector<std::string> _music_alternatives;
381
382 guint _ubus_handle;
383+
384+ unity::dash::FilesystemLenses _lenses;
385 };
386
387 }