Merge lp:~osomon/unity-2d/webfavs into lp:unity-2d/0.4
- webfavs
- Merge into maverick
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Florian Boucault | ||||
Approved revision: | 394 | ||||
Merged at revision: | 402 | ||||
Proposed branch: | lp:~osomon/unity-2d/webfavs | ||||
Merge into: | lp:unity-2d/0.4 | ||||
Diff against target: |
928 lines (+617/-12) 18 files modified
.bzrignore (+2/-0) debian/unity-2d-launcher.install (+1/-0) launcher/Launcher.qml (+6/-0) launcher/UnityApplications/CMakeLists.txt (+5/-0) launcher/UnityApplications/launcherapplication.cpp (+77/-4) launcher/UnityApplications/launcherapplication.h (+7/-1) launcher/UnityApplications/launcherapplicationslist.cpp (+17/-0) launcher/UnityApplications/launcherapplicationslist.h (+2/-0) launcher/UnityApplications/webfavorite.cpp (+240/-0) launcher/UnityApplications/webfavorite.h (+62/-0) launcher/app/CMakeLists.txt (+15/-0) launcher/app/launcher.cpp (+6/-0) launcher/app/launcher.xml (+22/-0) launcher/app/launchercontrol.cpp (+55/-0) launcher/app/launchercontrol.h (+47/-0) launcher/app/launcherview.cpp (+46/-7) launcher/app/launcherview.h (+4/-0) launcher/app/unity-2d-launcher.service.in (+3/-0) |
||||
To merge this branch: | bzr merge lp:~osomon/unity-2d/webfavs | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Florian Boucault (community) | Approve | ||
Review via email: mp+46831@code.launchpad.net |
This proposal supersedes a proposal from 2011-01-18.
Commit message
[launcher] Add web favorites support.
Web pages can be bookmarked in the launcher like other favorite applications. They will open in the default browser.
Two mechanisms are provided to favorite a web page:
- drag’n’drop a URL from your browser’s address bar to the launcher (tested with firefox and chromium)
- a DBus API that accepts a URL string as parameter (interface: com.canonical.
The full URL, including the scheme (e.g. "http://") must be provided.
Browser plugins could be implemented to bookmark the current page using the DBus API.
Description of the change
This branch implements web favorites support (bug #669926).
See details of the functionality implemented in the commit message.
- 373. By Olivier Tilloy
-
Cope with chromium’s unfriendly handling of drag events
in order to accept URLs dragged from its address bar as web favorites. - 374. By Olivier Tilloy
-
Enclose the URL in double quotes in the desktop file.
This fixes opening web favorites with a fragment in the URL. - 375. By Olivier Tilloy
-
Cope with badly configured web servers that don’t return error codes on non-existing files.
If the data is not an actual image, try the next one.
Florian Boucault (fboucault) wrote : | # |
- 376. By Olivier Tilloy
-
Made the web scrapper a child object of the application, so if the application is deleted (i.e. removed from the launcher)
before the scrapping is complete, the launcher won’t segfault.
Olivier Tilloy (osomon) wrote : | # |
Fixed in revision 376.
The actual issue was not with duplicate web favorites, but with deleting a favorite before its icon had been fetched.
Florian Boucault (fboucault) wrote : | # |
static const QString REL_STORE = ".local/
don't use abbreviations. In this case, you don't need REL_STORE at all, get rid of it.
Florian Boucault (fboucault) wrote : | # |
In LauncherApplica
don't use abbreviation:
gboolean res = [...]
Florian Boucault (fboucault) wrote : | # |
Duplicated code detected inn LauncherApplica
QFile file(desktop_file);
file.
file.
file.close();
[...]
QFile file(filename);
file.
file.
file.close();
And that duplication has a delta:
file.
vs.
file.
Florian Boucault (fboucault) wrote : | # |
In LauncherApplica
application-
does not seem necessary since application has already the same desktop filename set. I suppose it's a hack to workaround a bug. Please document it.
Florian Boucault (fboucault) wrote : | # |
In webscrapper.cpp same remark as above for REL_ICON_STORE, drop REL_ICON_STORE and rename ABS_ICON_STORE into ICON_STORE.
- 377. By Olivier Tilloy
-
Cosmetics: renamed a local variable to a more evocative name.
- 378. By Olivier Tilloy
-
Simplified the code that checks for the existence of specific directories in $HOME/.local/share/ a good deal.
- 379. By Olivier Tilloy
-
Comment to explain a hack.
Florian Boucault (fboucault) wrote : | # |
webscrapper's functionality is probably implemented in QWebSettings:
- dependency on libqtwebkit
- possible bugs: https:/
- 380. By Olivier Tilloy
-
Factor out some common code that writes a desktop file to disk.
Olivier Tilloy (osomon) wrote : | # |
I ruled out the dependency on QtWebkit from the beginning as it seemed like a huge one for little benefits (all the more so if it has bugs which we’d need to work around).
- 381. By Olivier Tilloy
-
Major refactoring to isolate all the code related to web favorites in its own class.
WebScrapper was renamed to WebFavorite.Missing bit: the Applications need to monitor their desktop files for changes,k
the hack that was in place to avoid this has been removed. - 382. By Olivier Tilloy
-
Applications now monitor their desktop file for live changes.
This fixes the deferred update of the icon and name of web favorites. - 383. By Olivier Tilloy
-
Remove some useless leftover includes.
- 384. By Olivier Tilloy
-
Do not check for null-ness, delete does it already.
Florian Boucault (fboucault) wrote : | # |
Looks pretty good.
Make LauncherApplica
More comments to come.
- 385. By Olivier Tilloy
-
Make LauncherApplica
tion::setDeskto pFile(… ) a public slot, so it can be directly invoked
when the file system watcher detects that the desktop file has changed.
Florian Boucault (fboucault) wrote : | # |
14:32 < Kaleo> oSoMoN: I don't know if you tried but changing contents in a desktop file displayed in the launcher does not work really well
14:32 < Kaleo> oSoMoN: for example I take gcalctool's desktop file
14:32 < Kaleo> oSoMoN: change the icon or description or exec string
14:32 < Kaleo> oSoMoN: and nothing seems to really get updated
Florian Boucault (fboucault) wrote : | # |
I get a SEGFAULT in a special case.
Steps to reproduce:
1. Add a webfav
3. Remove the webfav *before* the favicon and title have been retrieved
- 386. By Olivier Tilloy
-
Fix desktop file monitoring in various tricky cases, including a potential bug in QT.
This is largely documented. - 387. By Olivier Tilloy
-
When a desktop file is deleted, remove the corresponding application from the launcher.
Olivier Tilloy (osomon) wrote : | # |
All the issues introduced with the major refactoring (see revision 381) should now be fixed.
As a bonus, applications now monitor their desktop files for changes and update their attributes accordingly. If a desktop file is deleted (which may happen for example when a package is removed), the corresponding launcher entry will be removed from favorites, and removed from the launcher if not running.
Please test again and review the changes.
- 388. By Olivier Tilloy
-
Merge the latest changes from the trunk, resolving conflicts in launcher/
app/launcher. cpp.
Florian Boucault (fboucault) wrote : | # |
Issues with commits 386 and 387:
- emitting desktopFileChanged with an empty string when the property's value has not really changed is misleading and will cause trouble in the future.
- signal handlers (slot methods) should not be prefixed 'slot'; 'on' is the convention for prefixing signal handlers though it's much better to name the method by what it actually does when possible.
- m_removedDeskto
- LauncherApplica
Thinking that will resolve these issues:
Essentially you do _not_ want to touch desktop_file() at all. It is fine for it to point to a non existent file. What needs to happen is to apply the regular treatment for applications that have no desktop files:
- if the application is running then it should be impossible to favorite it and if it is already favorited, then it should be un-favorited
- if the application is not running then it means it is favorited and should be un-favorited
Bottom line: whenever the desktop file's existence changes LauncherApplica
Implementing that roughly equates to:
- reverting commit 387
- realising that LauncherApplica
- making LauncherApplica
- OPTIONALLY remember the desktop files of the applications that were favorited by the user and monitor the parent folder of the desktop file for their creation and re-add them to the launcher as favorites them when appropriate (that will also avoid implementing the grace period timer for vi & co at the expense of having a tiny visual glitch where the icon disappears and then reappears immediately in the launcher)
Olivier Tilloy (osomon) wrote : | # |
I agree on most of your points, I’ll update my code accordingly. Please see my comments below.
> signal handlers (slot methods) should not be prefixed 'slot';
> 'on' is the convention for prefixing signal handlers though
> it's much better to name the method by what it actually does
> when possible.
This convention is not part of the coding guidelines. A quick analysis of the header files shows that the two prefixes currently happily coexist in the trunk:
$ grep -r --include="*.h" "void\ on" * | wc -l
32
$ grep -r --include="*.h" "void\ slot" * | wc -l
19
I agree that it would be better to name the methods according to what they actually do, so I’ll do that.
> OPTIONALLY remember the desktop files of the applications that
> were favorited by the user and monitor the parent folder of the
> desktop file for their creation and re-add them to the launcher
> as favorites them when appropriate (that will also avoid implementing
> the grace period timer for vi & co at the expense of having a tiny
> visual glitch where the icon disappears and then reappears
> immediately in the launcher)
This is by no means a "tiny" visual glitch, it’s rather a major issue that will make the launcher look broken. The grace period is fine, it just delays the removal by one second, which I think is perfectly acceptable. Note that it’s not just "vi & co", it’s also the more frequent use case of an application being upgraded: the new desktop file is copied over the old one, effectively equivalent to the file being removed and then written again.
On top of that, monitoring several directories that may contain several hundreds of desktop files has a cost. And if an application is really removed for good, keeping on monitoring the parent folder of its desktop file is useless.
Florian Boucault (fboucault) wrote : | # |
> I agree on most of your points, I’ll update my code accordingly. Please see my
> comments below.
>
> > signal handlers (slot methods) should not be prefixed 'slot';
> > 'on' is the convention for prefixing signal handlers though
> > it's much better to name the method by what it actually does
> > when possible.
>
> This convention is not part of the coding guidelines. A quick analysis of the
> header files shows that the two prefixes currently happily coexist in the
> trunk:
>
> $ grep -r --include="*.h" "void\ on" * | wc -l
> 32
> $ grep -r --include="*.h" "void\ slot" * | wc -l
> 19
>
The convention is indeed not in the CODING file though the prefix "on" is in majority in the code base, not only in C++ as you shown already but also in QML:
$ grep -r --include="*.qml" \ on[A-Z] . | wc -l
168
> I agree that it would be better to name the methods according to what they
> actually do, so I’ll do that.
That's the best option indeed.
>
>
> > OPTIONALLY remember the desktop files of the applications that
> > were favorited by the user and monitor the parent folder of the
> > desktop file for their creation and re-add them to the launcher
> > as favorites them when appropriate (that will also avoid implementing
> > the grace period timer for vi & co at the expense of having a tiny
> > visual glitch where the icon disappears and then reappears
> > immediately in the launcher)
>
> This is by no means a "tiny" visual glitch, it’s rather a major issue that
> will make the launcher look broken. The grace period is fine, it just delays
> the removal by one second, which I think is perfectly acceptable. Note that
> it’s not just "vi & co", it’s also the more frequent use case of an
> application being upgraded: the new desktop file is copied over the old one,
> effectively equivalent to the file being removed and then written again.
> On top of that, monitoring several directories that may contain several
> hundreds of desktop files has a cost. And if an application is really removed
> for good, keeping on monitoring the parent folder of its desktop file is
> useless.
I agree. I misread your comment in the code and completely ignored that important case. The timer is therefore not optional.
- 389. By Olivier Tilloy
-
Merged the latest changes to trunk.
- 390. By Olivier Tilloy
-
Removed a useless member: m_removedDeskto
pFile was always equal to desktop_file(). - 391. By Olivier Tilloy
-
When a desktop file is removed, simply mark the application as not sticky.
The launcher will act accordingly. - 392. By Olivier Tilloy
-
Only applications with a valid, existent desktop file can be favorited.
- 393. By Olivier Tilloy
-
Cosmetics: renamed some slots.
Florian Boucault (fboucault) wrote : | # |
Olivier, have you pushed everything you wanted to? Can I review?
Olivier Tilloy (osomon) wrote : | # |
Yes, it is ready for review!
- 394. By Olivier Tilloy
-
Merge the latest changes from the trunk, resolving several conflicts.
Olivier Tilloy (osomon) wrote : | # |
Sorry, it turns out recent changes in the trunk prevented this branch from merging correctly, I fixed those issues, it is now really ready for review.
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2011-01-27 16:42:10 +0000 |
3 | +++ .bzrignore 2011-02-10 08:40:10 +0000 |
4 | @@ -3,6 +3,8 @@ |
5 | |
6 | launcher/UnityApplications/libUnityApplications.so* |
7 | launcher/app/unity-2d-launcher |
8 | +launcher/app/launcheradaptor.* |
9 | +launcher/app/unity-2d-launcher.service |
10 | launcher/tests/launchermenutest |
11 | launcher/tests/launcherviewtest |
12 | |
13 | |
14 | === modified file 'debian/unity-2d-launcher.install' |
15 | --- debian/unity-2d-launcher.install 2011-01-14 21:41:39 +0000 |
16 | +++ debian/unity-2d-launcher.install 2011-02-10 08:40:10 +0000 |
17 | @@ -1,4 +1,5 @@ |
18 | usr/bin/unity-2d-launcher |
19 | +usr/share/dbus-1/services/unity-2d-launcher.service |
20 | usr/share/applications/unity-2d-launcher.desktop |
21 | usr/share/unity-2d/launcher/*.qml |
22 | usr/share/unity-2d/launcher/launchermenu.css |
23 | |
24 | === modified file 'launcher/Launcher.qml' |
25 | --- launcher/Launcher.qml 2011-02-08 11:33:57 +0000 |
26 | +++ launcher/Launcher.qml 2011-02-10 08:40:10 +0000 |
27 | @@ -147,5 +147,11 @@ |
28 | Connections { |
29 | target: launcherView |
30 | onDesktopFileDropped: applications.insertFavoriteApplication(path) |
31 | + onWebpageUrlDropped: applications.insertWebFavorite(url) |
32 | + } |
33 | + |
34 | + Connections { |
35 | + target: launcherControl |
36 | + onAddWebFavorite: applications.insertWebFavorite(url) |
37 | } |
38 | } |
39 | |
40 | === modified file 'launcher/UnityApplications/CMakeLists.txt' |
41 | --- launcher/UnityApplications/CMakeLists.txt 2011-02-09 10:48:43 +0000 |
42 | +++ launcher/UnityApplications/CMakeLists.txt 2011-02-10 08:40:10 +0000 |
43 | @@ -3,6 +3,7 @@ |
44 | pkg_check_modules(QTGCONF REQUIRED libqtgconf) |
45 | pkg_check_modules(QTDEE REQUIRED libqtdee) |
46 | pkg_check_modules(DBUSMENUQT REQUIRED dbusmenu-qt) |
47 | +pkg_check_modules(GLIB REQUIRED glib-2.0) |
48 | pkg_check_modules(GDK REQUIRED gdk-2.0) |
49 | pkg_check_modules(GIO REQUIRED gio-2.0) |
50 | pkg_check_modules(WNCK REQUIRED libwnck-1.0) |
51 | @@ -21,6 +22,7 @@ |
52 | launcherplaceslist.cpp |
53 | trash.cpp |
54 | launchermenu.cpp |
55 | + webfavorite.cpp |
56 | plugin.cpp |
57 | workspaces.cpp |
58 | ) |
59 | @@ -37,6 +39,7 @@ |
60 | launcherplaceslist.h |
61 | trash.h |
62 | launchermenu.h |
63 | + webfavorite.h |
64 | plugin.h |
65 | workspaces.h |
66 | ) |
67 | @@ -59,6 +62,7 @@ |
68 | ${QTGCONF_INCLUDE_DIRS} |
69 | ${QTDEE_INCLUDE_DIRS} |
70 | ${DBUSMENUQT_INCLUDE_DIRS} |
71 | + ${GLIB_INCLUDE_DIRS} |
72 | ${GDK_INCLUDE_DIRS} |
73 | ${GIO_INCLUDE_DIRS} |
74 | ${WNCK_INCLUDE_DIRS} |
75 | @@ -72,6 +76,7 @@ |
76 | ${QTGCONF_LDFLAGS} |
77 | ${QTDEE_LDFLAGS} |
78 | ${DBUSMENUQT_LDFLAGS} |
79 | + ${GLIB_LDFLAGS} |
80 | ${GDK_LDFLAGS} |
81 | ${GIO_LDFLAGS} |
82 | ${WNCK_LDFLAGS} |
83 | |
84 | === modified file 'launcher/UnityApplications/launcherapplication.cpp' |
85 | --- launcher/UnityApplications/launcherapplication.cpp 2011-02-09 16:44:38 +0000 |
86 | +++ launcher/UnityApplications/launcherapplication.cpp 2011-02-10 08:40:10 +0000 |
87 | @@ -39,9 +39,15 @@ |
88 | #include <QAction> |
89 | #include <QDBusInterface> |
90 | #include <QDBusReply> |
91 | +#include <QFile> |
92 | +#include <QFileSystemWatcher> |
93 | |
94 | -LauncherApplication::LauncherApplication() : |
95 | - m_application(NULL), m_appInfo(NULL), m_sticky(false), m_has_visible_window(false) |
96 | +LauncherApplication::LauncherApplication() |
97 | + : m_application(NULL) |
98 | + , m_desktopFileWatcher(NULL) |
99 | + , m_appInfo(NULL) |
100 | + , m_sticky(false) |
101 | + , m_has_visible_window(false) |
102 | { |
103 | /* Make sure wnck_set_client_type is called only once */ |
104 | static bool client_type_set = false; |
105 | @@ -174,7 +180,7 @@ |
106 | } |
107 | |
108 | void |
109 | -LauncherApplication::setDesktopFile(QString desktop_file) |
110 | +LauncherApplication::setDesktopFile(const QString& desktop_file) |
111 | { |
112 | QByteArray byte_array = desktop_file.toUtf8(); |
113 | gchar *file = byte_array.data(); |
114 | @@ -200,6 +206,73 @@ |
115 | emit nameChanged(name()); |
116 | emit iconChanged(icon()); |
117 | } |
118 | + |
119 | + monitorDesktopFile(this->desktop_file()); |
120 | +} |
121 | + |
122 | +void |
123 | +LauncherApplication::monitorDesktopFile(const QString& path) |
124 | +{ |
125 | + /* Monitor the desktop file for live changes */ |
126 | + if (m_desktopFileWatcher == NULL) { |
127 | + m_desktopFileWatcher = new QFileSystemWatcher(this); |
128 | + connect(m_desktopFileWatcher, SIGNAL(fileChanged(const QString&)), |
129 | + SLOT(onDesktopFileChanged(const QString&))); |
130 | + } |
131 | + |
132 | + /* If the file is already being monitored, we shouldn’t need to do anything. |
133 | + However it seems that in some cases, a change to the file will stop |
134 | + emiting further fileChanged signals, despite the file still being in the |
135 | + list of monitored files. This is the case when the desktop file is being |
136 | + edited in gedit for example. This may be a bug in QT itself. |
137 | + To work around this issue, remove the path and add it again. */ |
138 | + if (m_desktopFileWatcher->files().contains(path)) { |
139 | + m_desktopFileWatcher->removePath(path); |
140 | + } |
141 | + m_desktopFileWatcher->addPath(path); |
142 | +} |
143 | + |
144 | +void |
145 | +LauncherApplication::onDesktopFileChanged(const QString& path) |
146 | +{ |
147 | + if (m_desktopFileWatcher->files().contains(path) || QFile::exists(path)) { |
148 | + /* The contents of the file have changed. */ |
149 | + setDesktopFile(path); |
150 | + } |
151 | + else { |
152 | + /* The desktop file has been deleted. |
153 | + This can happen in a number of cases: |
154 | + - the package it belongs to has been uninstalled |
155 | + - the package it belongs to has been upgraded, in which case it is |
156 | + likely that the desktop file has been removed and a new version of |
157 | + it has been installed in place of the old version |
158 | + - the file has been written to using an editor that first saves to a |
159 | + temporary file and then moves this temporary file to the |
160 | + destination file, which effectively results in the file being |
161 | + temporarily deleted (vi for example does that, whereas gedit |
162 | + doesn’t) |
163 | + In the first case, we want to remove the application from the |
164 | + launcher. In the last two cases, we need to consider that the desktop |
165 | + file’s contents have changed. At this point there is no way to be |
166 | + sure that the file has been permanently removed, so we want to give |
167 | + the application a grace period before checking for real deletion. */ |
168 | + QTimer::singleShot(1000, this, SLOT(checkDesktopFileReallyRemoved())); |
169 | + } |
170 | +} |
171 | + |
172 | +void |
173 | +LauncherApplication::checkDesktopFileReallyRemoved() |
174 | +{ |
175 | + QString path = desktop_file(); |
176 | + if (QFile::exists(path)) { |
177 | + /* The desktop file hasn’t really been removed, it was only temporarily |
178 | + deleted. */ |
179 | + setDesktopFile(path); |
180 | + } |
181 | + else { |
182 | + /* The desktop file has really been removed. */ |
183 | + setSticky(false); |
184 | + } |
185 | } |
186 | |
187 | void |
188 | @@ -610,7 +683,7 @@ |
189 | bool is_running = running(); |
190 | |
191 | /* Only applications with a corresponding desktop file can be kept in the launcher */ |
192 | - if (!desktop_file().isEmpty()) { |
193 | + if (QFile::exists(desktop_file())) { |
194 | QAction* keep = new QAction(m_menu); |
195 | keep->setCheckable(is_running); |
196 | keep->setChecked(sticky()); |
197 | |
198 | === modified file 'launcher/UnityApplications/launcherapplication.h' |
199 | --- launcher/UnityApplications/launcherapplication.h 2011-02-09 16:44:38 +0000 |
200 | +++ launcher/UnityApplications/launcherapplication.h 2011-02-10 08:40:10 +0000 |
201 | @@ -32,6 +32,7 @@ |
202 | #include "bamf-application.h" |
203 | |
204 | class DBusMenuImporter; |
205 | +class QFileSystemWatcher; |
206 | |
207 | class LauncherApplication : public LauncherItem |
208 | { |
209 | @@ -62,8 +63,8 @@ |
210 | bool has_visible_window() const; |
211 | |
212 | /* setters */ |
213 | + void setDesktopFile(const QString& desktop_file); |
214 | void setSticky(bool sticky); |
215 | - void setDesktopFile(QString desktop_file); |
216 | void setBamfApplication(BamfApplication *application); |
217 | |
218 | /* methods */ |
219 | @@ -106,8 +107,12 @@ |
220 | void slotChildRemoved(BamfView*); |
221 | void onIndicatorMenuUpdated(); |
222 | |
223 | + void onDesktopFileChanged(const QString&); |
224 | + void checkDesktopFileReallyRemoved(); |
225 | + |
226 | private: |
227 | BamfApplication *m_application; |
228 | + QFileSystemWatcher *m_desktopFileWatcher; |
229 | GDesktopAppInfo *m_appInfo; |
230 | bool m_sticky; |
231 | int m_priority; |
232 | @@ -117,6 +122,7 @@ |
233 | int m_indicatorMenusReady; |
234 | |
235 | void updateBamfApplicationDependentProperties(); |
236 | + void monitorDesktopFile(const QString&); |
237 | void fetchIndicatorMenus(); |
238 | void createStaticMenuActions(); |
239 | int windowCountOnCurrentWorkspace(); |
240 | |
241 | === modified file 'launcher/UnityApplications/launcherapplicationslist.cpp' |
242 | --- launcher/UnityApplications/launcherapplicationslist.cpp 2011-01-15 01:41:03 +0000 |
243 | +++ launcher/UnityApplications/launcherapplicationslist.cpp 2011-02-10 08:40:10 +0000 |
244 | @@ -16,6 +16,7 @@ |
245 | |
246 | #include "launcherapplication.h" |
247 | #include "launcherapplicationslist.h" |
248 | +#include "webfavorite.h" |
249 | |
250 | #include "bamf-matcher.h" |
251 | #include "bamf-application.h" |
252 | @@ -129,6 +130,22 @@ |
253 | } |
254 | |
255 | void |
256 | +LauncherApplicationsList::insertWebFavorite(const QUrl& url) |
257 | +{ |
258 | + if (!url.isValid() || url.isRelative()) { |
259 | + qWarning() << "Invalid URL:" << url; |
260 | + return; |
261 | + } |
262 | + |
263 | + LauncherApplication* application = new LauncherApplication; |
264 | + WebFavorite* webfav = new WebFavorite(url, application); |
265 | + |
266 | + application->setDesktopFile(webfav->desktopFile()); |
267 | + insertApplication(application); |
268 | + application->setSticky(true); |
269 | +} |
270 | + |
271 | +void |
272 | LauncherApplicationsList::load() |
273 | { |
274 | /* FIXME: applications should be sorted depending on their priority */ |
275 | |
276 | === modified file 'launcher/UnityApplications/launcherapplicationslist.h' |
277 | --- launcher/UnityApplications/launcherapplicationslist.h 2011-01-15 01:41:03 +0000 |
278 | +++ launcher/UnityApplications/launcherapplicationslist.h 2011-02-10 08:40:10 +0000 |
279 | @@ -21,6 +21,7 @@ |
280 | #include <QList> |
281 | #include <QVariant> |
282 | #include <QString> |
283 | +#include <QUrl> |
284 | #include <QObject> |
285 | #include <QtDeclarative/qdeclarative.h> |
286 | |
287 | @@ -40,6 +41,7 @@ |
288 | int rowCount(const QModelIndex & parent = QModelIndex()) const; |
289 | |
290 | Q_INVOKABLE void insertFavoriteApplication(QString desktop_file); |
291 | + Q_INVOKABLE void insertWebFavorite(const QUrl& url); |
292 | |
293 | private: |
294 | void load(); |
295 | |
296 | === added file 'launcher/UnityApplications/webfavorite.cpp' |
297 | --- launcher/UnityApplications/webfavorite.cpp 1970-01-01 00:00:00 +0000 |
298 | +++ launcher/UnityApplications/webfavorite.cpp 2011-02-10 08:40:10 +0000 |
299 | @@ -0,0 +1,240 @@ |
300 | +/* |
301 | + * Copyright (C) 2011 Canonical, Ltd. |
302 | + * |
303 | + * Authors: |
304 | + * Olivier Tilloy <olivier.tilloy@canonical.com> |
305 | + * |
306 | + * This program is free software; you can redistribute it and/or modify |
307 | + * it under the terms of the GNU General Public License as published by |
308 | + * the Free Software Foundation; version 3. |
309 | + * |
310 | + * This program is distributed in the hope that it will be useful, |
311 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
312 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
313 | + * GNU General Public License for more details. |
314 | + * |
315 | + * You should have received a copy of the GNU General Public License |
316 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
317 | + */ |
318 | + |
319 | +#include <glib.h> |
320 | + |
321 | +#include "webfavorite.h" |
322 | + |
323 | +#include <QDir> |
324 | +#include <QtNetwork/QNetworkAccessManager> |
325 | +#include <QtNetwork/QNetworkRequest> |
326 | +#include <QtNetwork/QNetworkReply> |
327 | +#include <QRegExp> |
328 | +#include <QPixmap> |
329 | +#include <QCryptographicHash> |
330 | + |
331 | +static const QString WEBFAV_STORE = QDir::homePath() + "/.local/share/applications/"; |
332 | +static const QString ICON_STORE = QDir::homePath() + "/.local/share/icons/"; |
333 | + |
334 | +static void check_store_exists(const QString& path) |
335 | +{ |
336 | + if (!QDir(path).exists()) { |
337 | + QDir().mkpath(path); |
338 | + } |
339 | +} |
340 | + |
341 | +#define check_webfav_store_exists() check_store_exists(WEBFAV_STORE) |
342 | +#define check_icon_store_exists() check_store_exists(ICON_STORE) |
343 | + |
344 | +static const QString WEBFAV_DESKTOP_ENTRY = |
345 | + "[Desktop Entry]\n" |
346 | + "Version=1.0\n" |
347 | + "Name={name}\n" |
348 | + "Exec=xdg-open \"{url}\"\n" |
349 | + "Type=Application\n" |
350 | + "Icon=emblem-web\n" |
351 | + "Categories=Network;\n" |
352 | + "MimeType=text/html;\n" |
353 | + "StartupNotify=true\n"; |
354 | + |
355 | +static const uint MAX_REDIRECTS = 6; |
356 | + |
357 | +WebFavorite::WebFavorite(const QUrl& url, QObject* parent) |
358 | + : QObject(parent) |
359 | + , m_url(url) |
360 | + , m_redirects(0) |
361 | +{ |
362 | + m_desktopFile = WEBFAV_STORE + "webfav-" + computeUrlHash(url) + ".desktop"; |
363 | + |
364 | + QString contents = WEBFAV_DESKTOP_ENTRY; |
365 | + QByteArray encoded = url.toEncoded(); |
366 | + contents.replace("{name}", encoded); |
367 | + contents.replace("{url}", encoded); |
368 | + writeDesktopFile(contents.toUtf8()); |
369 | + |
370 | + fetchPage(); |
371 | +} |
372 | + |
373 | +WebFavorite::~WebFavorite() |
374 | +{ |
375 | +} |
376 | + |
377 | +const QString& |
378 | +WebFavorite::desktopFile() const |
379 | +{ |
380 | + return m_desktopFile; |
381 | +} |
382 | + |
383 | +void |
384 | +WebFavorite::writeDesktopFile(const QByteArray& contents) const |
385 | +{ |
386 | + check_webfav_store_exists(); |
387 | + QFile file(m_desktopFile); |
388 | + file.open(QIODevice::WriteOnly); |
389 | + file.write(contents); |
390 | + file.close(); |
391 | +} |
392 | + |
393 | +void |
394 | +WebFavorite::modifyDesktopFile(const QString& key, const QString& value) const |
395 | +{ |
396 | + GKeyFile* keyFile = g_key_file_new(); |
397 | + gboolean loaded = g_key_file_load_from_file(keyFile, m_desktopFile.toUtf8().constData(), |
398 | + (GKeyFileFlags) (G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS), NULL); |
399 | + if (loaded) { |
400 | + g_key_file_set_string(keyFile, "Desktop Entry", key.toUtf8().constData(), value.toUtf8().constData()); |
401 | + QByteArray contents = g_key_file_to_data(keyFile, NULL, NULL); |
402 | + g_key_file_free(keyFile); |
403 | + writeDesktopFile(contents); |
404 | + } |
405 | +} |
406 | + |
407 | +void |
408 | +WebFavorite::fetchPage() |
409 | +{ |
410 | + QNetworkAccessManager* manager = new QNetworkAccessManager(this); |
411 | + connect(manager, SIGNAL(finished(QNetworkReply*)), |
412 | + SLOT(slotFetchPageFinished(QNetworkReply*))); |
413 | + manager->get(QNetworkRequest(m_url)); |
414 | +} |
415 | + |
416 | +void |
417 | +WebFavorite::slotFetchPageFinished(QNetworkReply* reply) |
418 | +{ |
419 | + QNetworkAccessManager* manager = static_cast<QNetworkAccessManager*>(sender()); |
420 | + |
421 | + if (reply->error() == QNetworkReply::NoError) { |
422 | + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); |
423 | + if (redirect.isValid()) { |
424 | + m_redirects++; |
425 | + if (m_redirects < MAX_REDIRECTS) { |
426 | + m_url = redirect.toUrl(); |
427 | + fetchPage(); |
428 | + } |
429 | + } |
430 | + else { |
431 | + QString data = QString::fromUtf8(reply->readAll()); |
432 | + |
433 | + /* lookup title */ |
434 | + QRegExp reTitle("<title>(.*)</title>", Qt::CaseInsensitive); |
435 | + int index = reTitle.indexIn(data); |
436 | + if (index != -1) { |
437 | + modifyDesktopFile("Name", reTitle.cap(1).simplified()); |
438 | + } |
439 | + |
440 | + /* lookup favicons */ |
441 | + QRegExp reFavicon1("<link rel=\"apple-touch-icon\".*href=\"(.*)\"", Qt::CaseInsensitive); |
442 | + reFavicon1.setMinimal(true); |
443 | + index = reFavicon1.indexIn(data); |
444 | + if (index != -1) { |
445 | + m_favicons << reFavicon1.cap(1); |
446 | + } |
447 | + QRegExp reFavicon2("<link rel=\"(shortcut )?icon\".*href=\"(.*)\"", Qt::CaseInsensitive); |
448 | + reFavicon2.setMinimal(true); |
449 | + index = reFavicon2.indexIn(data); |
450 | + if (index != -1) { |
451 | + m_favicons << reFavicon2.cap(2); |
452 | + } |
453 | + m_favicons << "/apple-touch-icon.png"; |
454 | + m_favicons << "/favicon.ico"; |
455 | + |
456 | + m_current_favicon = m_favicons.begin(); |
457 | + m_redirects = 0; |
458 | + tryNextFavicon(); |
459 | + } |
460 | + } |
461 | + |
462 | + reply->deleteLater(); |
463 | + manager->deleteLater(); |
464 | +} |
465 | + |
466 | +void |
467 | +WebFavorite::tryNextFavicon() |
468 | +{ |
469 | + if (m_current_favicon == m_favicons.end()) { |
470 | + return; |
471 | + } |
472 | + |
473 | + QUrl url(*m_current_favicon); |
474 | + if (url.isRelative()) { |
475 | + url = m_url.resolved(url); |
476 | + } |
477 | + |
478 | + QNetworkAccessManager* manager = new QNetworkAccessManager(this); |
479 | + connect(manager, SIGNAL(finished(QNetworkReply*)), |
480 | + SLOT(slotFetchFaviconFinished(QNetworkReply*))); |
481 | + manager->get(QNetworkRequest(url)); |
482 | +} |
483 | + |
484 | +void |
485 | +WebFavorite::slotFetchFaviconFinished(QNetworkReply* reply) |
486 | +{ |
487 | + QNetworkAccessManager* manager = static_cast<QNetworkAccessManager*>(sender()); |
488 | + |
489 | + if (reply->error() == QNetworkReply::NoError) { |
490 | + QVariant redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); |
491 | + if (redirect.isValid()) { |
492 | + m_redirects++; |
493 | + if (m_redirects < MAX_REDIRECTS) { |
494 | + *m_current_favicon = redirect.toUrl().toEncoded(); |
495 | + } |
496 | + else { |
497 | + m_current_favicon++; |
498 | + m_redirects = 0; |
499 | + } |
500 | + tryNextFavicon(); |
501 | + } |
502 | + else { |
503 | + /* Check that the data is actually an image. This will cope with |
504 | + badly configured web servers that don’t return error codes on |
505 | + non-existing files. */ |
506 | + QPixmap pixmap; |
507 | + bool valid = pixmap.loadFromData(reply->readAll()); |
508 | + if (valid) { |
509 | + check_icon_store_exists(); |
510 | + QUrl url = reply->url(); |
511 | + QString filepath = ICON_STORE + computeUrlHash(url); |
512 | + QString extension = url.path().mid(url.path().lastIndexOf(".")); |
513 | + QString filename = filepath + extension; |
514 | + pixmap.save(filename); |
515 | + modifyDesktopFile("Icon", filename); |
516 | + } |
517 | + else { |
518 | + m_current_favicon++; |
519 | + m_redirects = 0; |
520 | + tryNextFavicon(); |
521 | + } |
522 | + } |
523 | + } |
524 | + else { |
525 | + m_current_favicon++; |
526 | + m_redirects = 0; |
527 | + tryNextFavicon(); |
528 | + } |
529 | + |
530 | + reply->deleteLater(); |
531 | + manager->deleteLater(); |
532 | +} |
533 | + |
534 | +QString |
535 | +WebFavorite::computeUrlHash(const QUrl& url) |
536 | +{ |
537 | + return QCryptographicHash::hash(url.toEncoded(), QCryptographicHash::Md5).toHex().constData(); |
538 | +} |
539 | + |
540 | |
541 | === added file 'launcher/UnityApplications/webfavorite.h' |
542 | --- launcher/UnityApplications/webfavorite.h 1970-01-01 00:00:00 +0000 |
543 | +++ launcher/UnityApplications/webfavorite.h 2011-02-10 08:40:10 +0000 |
544 | @@ -0,0 +1,62 @@ |
545 | +/* |
546 | + * Copyright (C) 2011 Canonical, Ltd. |
547 | + * |
548 | + * Authors: |
549 | + * Olivier Tilloy <olivier.tilloy@canonical.com> |
550 | + * |
551 | + * This program is free software; you can redistribute it and/or modify |
552 | + * it under the terms of the GNU General Public License as published by |
553 | + * the Free Software Foundation; version 3. |
554 | + * |
555 | + * This program is distributed in the hope that it will be useful, |
556 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
557 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
558 | + * GNU General Public License for more details. |
559 | + * |
560 | + * You should have received a copy of the GNU General Public License |
561 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
562 | + */ |
563 | + |
564 | +#ifndef WebFavorite_H |
565 | +#define WebFavorite_H |
566 | + |
567 | +#include <QObject> |
568 | +#include <QUrl> |
569 | +#include <QString> |
570 | +#include <QStringList> |
571 | + |
572 | +class QNetworkReply; |
573 | + |
574 | +class WebFavorite : public QObject |
575 | +{ |
576 | + Q_OBJECT |
577 | + |
578 | +public: |
579 | + WebFavorite(const QUrl& url, QObject* parent=0); |
580 | + ~WebFavorite(); |
581 | + |
582 | + const QString& desktopFile() const; |
583 | + |
584 | +private: |
585 | + QUrl m_url; |
586 | + QString m_desktopFile; |
587 | + |
588 | + uint m_redirects; |
589 | + QStringList m_favicons; |
590 | + QStringList::iterator m_current_favicon; |
591 | + |
592 | + static QString computeUrlHash(const QUrl& url); |
593 | + |
594 | + void writeDesktopFile(const QByteArray& contents) const; |
595 | + void modifyDesktopFile(const QString& key, const QString& value) const; |
596 | + |
597 | + void fetchPage(); |
598 | + void tryNextFavicon(); |
599 | + |
600 | +private Q_SLOTS: |
601 | + void slotFetchPageFinished(QNetworkReply*); |
602 | + void slotFetchFaviconFinished(QNetworkReply*); |
603 | +}; |
604 | + |
605 | +#endif // WebFavorite_H |
606 | + |
607 | |
608 | === modified file 'launcher/app/CMakeLists.txt' |
609 | --- launcher/app/CMakeLists.txt 2011-02-07 22:19:04 +0000 |
610 | +++ launcher/app/CMakeLists.txt 2011-02-10 08:40:10 +0000 |
611 | @@ -5,19 +5,28 @@ |
612 | # Sources |
613 | set(launcher_SRCS |
614 | launcherview.cpp |
615 | + launchercontrol.cpp |
616 | ) |
617 | |
618 | set(launcher_MOC_HDRS |
619 | launcherview.h |
620 | + launchercontrol.h |
621 | ) |
622 | |
623 | qt4_wrap_cpp(launcher_MOC_SRCS ${launcher_MOC_HDRS}) |
624 | |
625 | +configure_file(unity-2d-launcher.service.in unity-2d-launcher.service) |
626 | + |
627 | +qt4_add_dbus_adaptor(launcher_SRCS launcher.xml |
628 | + launchercontrol.h LauncherControl |
629 | + ) |
630 | + |
631 | # Build |
632 | add_library(uqlauncher ${launcher_SRCS} ${launcher_MOC_SRCS}) |
633 | add_executable(unity-2d-launcher launcher.cpp) |
634 | |
635 | include_directories( |
636 | + ${CMAKE_CURRENT_SOURCE_DIR} |
637 | ${CMAKE_CURRENT_BINARY_DIR} |
638 | ${GTK_INCLUDE_DIRS} |
639 | ${X11_INCLUDE_DIRS} |
640 | @@ -27,6 +36,7 @@ |
641 | target_link_libraries(uqlauncher |
642 | ${QT_QTCORE_LIBRARIES} |
643 | ${QT_QTGUI_LIBRARIES} |
644 | + ${QT_QTDBUS_LIBRARIES} |
645 | ${QT_QTDECLARATIVE_LIBRARIES} |
646 | ${GTK_LDFLAGS} |
647 | ${X11_LDFLAGS} |
648 | @@ -44,3 +54,8 @@ |
649 | install(FILES unity-2d-launcher.desktop |
650 | DESTINATION share/applications |
651 | ) |
652 | + |
653 | +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/unity-2d-launcher.service |
654 | + DESTINATION share/dbus-1/services |
655 | + ) |
656 | + |
657 | |
658 | === modified file 'launcher/app/launcher.cpp' |
659 | --- launcher/app/launcher.cpp 2011-02-07 22:19:04 +0000 |
660 | +++ launcher/app/launcher.cpp 2011-02-10 08:40:10 +0000 |
661 | @@ -31,6 +31,7 @@ |
662 | |
663 | #include "config.h" |
664 | #include "launcherview.h" |
665 | +#include "launchercontrol.h" |
666 | #include "unity2dpanel.h" |
667 | |
668 | int main(int argc, char *argv[]) |
669 | @@ -81,6 +82,11 @@ |
670 | |
671 | launcherView->rootContext()->setContextProperty("launcherView", launcherView); |
672 | launcherView->rootContext()->setContextProperty("panel", &panel); |
673 | + |
674 | + LauncherControl control; |
675 | + launcherView->rootContext()->setContextProperty("launcherControl", &control); |
676 | + control.connectToBus(); |
677 | + |
678 | launcherView->setSource(QUrl("./Launcher.qml")); |
679 | |
680 | /* Composing the QML declarative view inside the panel */ |
681 | |
682 | === added file 'launcher/app/launcher.xml' |
683 | --- launcher/app/launcher.xml 1970-01-01 00:00:00 +0000 |
684 | +++ launcher/app/launcher.xml 2011-02-10 08:40:10 +0000 |
685 | @@ -0,0 +1,22 @@ |
686 | +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> |
687 | +<node xmlns:dox="http://www.ayatana.org/dbus/dox.dtd"> |
688 | + <dox:d><![CDATA[ |
689 | + @mainpage |
690 | + |
691 | + An interface to activate the launcher in Unity2d. |
692 | + ]]></dox:d> |
693 | + <interface name="com.canonical.Unity2d.Launcher" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd"> |
694 | + <dox:d> |
695 | + An interface to activate the launcher in Unity2d. |
696 | + </dox:d> |
697 | + <method name="AddWebFavorite"> |
698 | + <dox:d><![CDATA[ |
699 | + Request a URL to be added to the launcher as a web favorite. |
700 | + ]]></dox:d> |
701 | + <arg name="url" type="s" direction="in"> |
702 | + <dox:d>The URL to be favorited.</dox:d> |
703 | + </arg> |
704 | + </method> |
705 | + </interface> |
706 | +</node> |
707 | + |
708 | |
709 | === added file 'launcher/app/launchercontrol.cpp' |
710 | --- launcher/app/launchercontrol.cpp 1970-01-01 00:00:00 +0000 |
711 | +++ launcher/app/launchercontrol.cpp 2011-02-10 08:40:10 +0000 |
712 | @@ -0,0 +1,55 @@ |
713 | +/* |
714 | + * Copyright (C) 2011 Canonical, Ltd. |
715 | + * |
716 | + * Authors: |
717 | + * Olivier Tilloy <olivier.tilloy@canonical.com> |
718 | + * |
719 | + * This program is free software; you can redistribute it and/or modify |
720 | + * it under the terms of the GNU General Public License as published by |
721 | + * the Free Software Foundation; version 3. |
722 | + * |
723 | + * This program is distributed in the hope that it will be useful, |
724 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
725 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
726 | + * GNU General Public License for more details. |
727 | + * |
728 | + * You should have received a copy of the GNU General Public License |
729 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
730 | + */ |
731 | + |
732 | +#include "launchercontrol.h" |
733 | +#include "launcheradaptor.h" |
734 | + |
735 | +#include <QtDBus/QDBusConnection> |
736 | + |
737 | +static const char* LAUNCHER_DBUS_SERVICE = "com.canonical.Unity2d.Launcher"; |
738 | +static const char* LAUNCHER_DBUS_OBJECT_PATH = "/Launcher"; |
739 | + |
740 | +LauncherControl::LauncherControl(QObject* parent) : QObject(parent) |
741 | +{ |
742 | +} |
743 | + |
744 | +LauncherControl::~LauncherControl() |
745 | +{ |
746 | + QDBusConnection::sessionBus().unregisterService(LAUNCHER_DBUS_SERVICE); |
747 | +} |
748 | + |
749 | +bool |
750 | +LauncherControl::connectToBus() |
751 | +{ |
752 | + bool ok = QDBusConnection::sessionBus().registerService(LAUNCHER_DBUS_SERVICE); |
753 | + if (!ok) { |
754 | + return false; |
755 | + } |
756 | + new LauncherAdaptor(this); |
757 | + QDBusConnection::sessionBus().registerObject(LAUNCHER_DBUS_OBJECT_PATH, this); |
758 | + |
759 | + return true; |
760 | +} |
761 | + |
762 | +void |
763 | +LauncherControl::AddWebFavorite(const QString& url) |
764 | +{ |
765 | + Q_EMIT addWebFavorite(url); |
766 | +} |
767 | + |
768 | |
769 | === added file 'launcher/app/launchercontrol.h' |
770 | --- launcher/app/launchercontrol.h 1970-01-01 00:00:00 +0000 |
771 | +++ launcher/app/launchercontrol.h 2011-02-10 08:40:10 +0000 |
772 | @@ -0,0 +1,47 @@ |
773 | +/* |
774 | + * Copyright (C) 2011 Canonical, Ltd. |
775 | + * |
776 | + * Authors: |
777 | + * Olivier Tilloy <olivier.tilloy@canonical.com> |
778 | + * |
779 | + * This program is free software; you can redistribute it and/or modify |
780 | + * it under the terms of the GNU General Public License as published by |
781 | + * the Free Software Foundation; version 3. |
782 | + * |
783 | + * This program is distributed in the hope that it will be useful, |
784 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
785 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
786 | + * GNU General Public License for more details. |
787 | + * |
788 | + * You should have received a copy of the GNU General Public License |
789 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
790 | + */ |
791 | + |
792 | +#ifndef LauncherControl_H |
793 | +#define LauncherControl_H |
794 | + |
795 | +#include <QtCore/QObject> |
796 | +#include <QtDBus/QDBusContext> |
797 | +#include <QtDeclarative/qdeclarative.h> |
798 | + |
799 | +class LauncherControl : public QObject, protected QDBusContext |
800 | +{ |
801 | + Q_OBJECT |
802 | + |
803 | +public: |
804 | + explicit LauncherControl(QObject* parent=0); |
805 | + ~LauncherControl(); |
806 | + |
807 | + bool connectToBus(); |
808 | + |
809 | +public Q_SLOTS: |
810 | + Q_NOREPLY void AddWebFavorite(const QString& url); |
811 | + |
812 | +Q_SIGNALS: |
813 | + void addWebFavorite(const QString& url); |
814 | +}; |
815 | + |
816 | +QML_DECLARE_TYPE(LauncherControl) |
817 | + |
818 | +#endif // LauncherControl_H |
819 | + |
820 | |
821 | === modified file 'launcher/app/launcherview.cpp' |
822 | --- launcher/app/launcherview.cpp 2011-01-28 01:56:32 +0000 |
823 | +++ launcher/app/launcherview.cpp 2011-02-10 08:40:10 +0000 |
824 | @@ -38,14 +38,48 @@ |
825 | setAcceptDrops(true); |
826 | } |
827 | |
828 | +QList<QUrl> |
829 | +LauncherView::getEventUrls(QDropEvent* event) |
830 | +{ |
831 | + const QMimeData* mimeData = event->mimeData(); |
832 | + if (mimeData->hasUrls()) { |
833 | + return mimeData->urls(); |
834 | + } |
835 | + else if (mimeData->hasText()) { |
836 | + /* When dragging an URL from firefox’s address bar, it is properly |
837 | + recognized as such by the event. However, the same doesn’t work |
838 | + for chromium: the URL is recognized as plain text. |
839 | + We cope with this unfriendly behaviour by trying to build a URL out |
840 | + of the text. This assumes there’s only one URL. */ |
841 | + QString text = mimeData->text(); |
842 | + QUrl url(text); |
843 | + if (url.isRelative()) { |
844 | + /* On top of that, chromium sometimes chops off the scheme… */ |
845 | + url = QUrl("http://" + text); |
846 | + } |
847 | + if (url.isValid()) { |
848 | + QList<QUrl> urls; |
849 | + urls.append(url); |
850 | + return urls; |
851 | + } |
852 | + } |
853 | + |
854 | + return QList<QUrl>(); |
855 | +} |
856 | + |
857 | void LauncherView::dragEnterEvent(QDragEnterEvent *event) |
858 | { |
859 | - // Check that data has a list of URLs and that at least one is |
860 | - // a desktop file. |
861 | - if (!event->mimeData()->hasUrls()) return; |
862 | - |
863 | - foreach (QUrl url, event->mimeData()->urls()) { |
864 | - if (url.scheme() == "file" && url.path().endsWith(".desktop")) { |
865 | + // Check that data has a list of URLs and that at least one is either |
866 | + // a desktop file or a web page. |
867 | + QList<QUrl> urls = getEventUrls(event); |
868 | + |
869 | + if (urls.isEmpty()) { |
870 | + return; |
871 | + } |
872 | + |
873 | + foreach (QUrl url, urls) { |
874 | + if ((url.scheme() == "file" && url.path().endsWith(".desktop")) || |
875 | + url.scheme().startsWith("http")) { |
876 | event->acceptProposedAction(); |
877 | break; |
878 | } |
879 | @@ -61,11 +95,16 @@ |
880 | { |
881 | bool accepted = false; |
882 | |
883 | - foreach (QUrl url, event->mimeData()->urls()) { |
884 | + QList<QUrl> urls = getEventUrls(event); |
885 | + foreach (QUrl url, urls) { |
886 | if (url.scheme() == "file" && url.path().endsWith(".desktop")) { |
887 | emit desktopFileDropped(url.path()); |
888 | accepted = true; |
889 | } |
890 | + else if (url.scheme().startsWith("http")) { |
891 | + emit webpageUrlDropped(url); |
892 | + accepted = true; |
893 | + } |
894 | } |
895 | |
896 | if (accepted) event->accept(); |
897 | |
898 | === modified file 'launcher/app/launcherview.h' |
899 | --- launcher/app/launcherview.h 2011-01-28 01:56:32 +0000 |
900 | +++ launcher/app/launcherview.h 2011-02-10 08:40:10 +0000 |
901 | @@ -21,6 +21,7 @@ |
902 | #define LAUNCHERVIEW |
903 | |
904 | #include <QDeclarativeView> |
905 | +#include <QUrl> |
906 | #include <QDragEnterEvent> |
907 | |
908 | class LauncherView : public QDeclarativeView |
909 | @@ -33,8 +34,11 @@ |
910 | |
911 | signals: |
912 | void desktopFileDropped(QString path); |
913 | + void webpageUrlDropped(const QUrl& url); |
914 | |
915 | private: |
916 | + QList<QUrl> getEventUrls(QDropEvent*); |
917 | + |
918 | void dragEnterEvent(QDragEnterEvent *event); |
919 | void dropEvent(QDropEvent *event); |
920 | void dragMoveEvent(QDragMoveEvent *event); |
921 | |
922 | === added file 'launcher/app/unity-2d-launcher.service.in' |
923 | --- launcher/app/unity-2d-launcher.service.in 1970-01-01 00:00:00 +0000 |
924 | +++ launcher/app/unity-2d-launcher.service.in 2011-02-10 08:40:10 +0000 |
925 | @@ -0,0 +1,3 @@ |
926 | +[D-BUS Service] |
927 | +Name=com.canonical.Unity2d.Launcher |
928 | +Exec=@CMAKE_INSTALL_PREFIX@/bin/unity-2d-launcher |
I get a SEGFAULT in a special case.
Steps to reproduce:
1. Add a webfav
2. Add the same webfav again
3. Remove the second webfav