Some cool improvements
[on:on.git] / shell / on.cpp
1     #include "on.h"
2
3 On::On(QWidget *parent)
4     : QWebView(parent)
5 {
6     setWindow();
7     setWebSettings();
8     setBackground();
9     setUrl(QUrl("/usr/share/on/shell/index.html"));
10     setNative();
11     connect(page()->mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), this, SLOT(setNative()));
12     setWatchers();
13 }
14
15 On::~On()
16 {
17
18 }
19
20 QString On::release() { return QString("Asturix On PreAlpha"); }
21
22 QString On::username() { return QDir::homePath().remove("/home/"); }
23
24 QString On::userDir(int dir) { return QDesktopServices::storageLocation(QDesktopServices::StandardLocation(dir)); }
25
26 QString On::userDirName(int dir) { return QDesktopServices::displayName(QDesktopServices::StandardLocation(dir)); }
27
28 QString On::lang() {
29     QString lang = QLocale::system().name().split("_").first();
30     if (lang == "C") {
31         lang = "en";
32     }
33     return lang;
34 }
35
36 // Set the window that is going to be the whole desktop
37 void On::setWindow()
38 {
39
40     if(getenv("QT_GRAPHICSSYSTEM") == 0) {
41         QApplication::setGraphicsSystem("raster");
42     }
43
44     QDesktopWidget* desktop = QApplication::desktop();
45     const QRect screen = desktop->screenGeometry(this);
46
47     static Atom atom = XInternAtom(QX11Info::display(), "_NET_WM_DESKTOP", False);
48     ulong data[1];
49     data[0] = 0xFFFFFFFF;
50     XChangeProperty(QX11Info::display(), winId(), atom, atom, 32, PropModeReplace, (unsigned char *) data, 1);
51
52     setWindowFlags(Qt::WindowStaysOnBottomHint);
53     setAttribute(Qt::WA_X11NetWmWindowTypeDesktop);
54     //showMaximized();
55
56     setGeometry(0, 24, desktop->width(), desktop->height());
57
58     if (QX11Info::isCompositingManagerRunning()) {
59         setAttribute(Qt::WA_TranslucentBackground);
60     } else {
61         setAutoFillBackground(true);
62     }
63     //setContextMenuPolicy(Qt::NoContextMenu);
64 }
65
66 // Set some things of WebKit
67 void On::setWebSettings() {
68     QSettings cfg(QSettings::IniFormat, QSettings::UserScope, "asturix", "on");
69     QString cfgDir = QFileInfo(cfg.fileName()).absolutePath() + "/";
70     settings()->setLocalStoragePath(cfgDir);
71     settings()->setIconDatabasePath(cfgDir);
72     settings()->setOfflineStoragePath(cfgDir);
73     settings()->setAttribute(QWebSettings::AcceleratedCompositingEnabled, true);
74     settings()->setAttribute(QWebSettings::JavascriptCanAccessClipboard, true);
75     settings()->setAttribute(QWebSettings::LocalContentCanAccessFileUrls, true);
76     settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, true);
77     settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
78     settings()->setAttribute(QWebSettings::LocalStorageDatabaseEnabled, true);
79     settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
80 }
81
82 // Add this to the Javascript window object so this can be accessed from web code
83 void On::setNative() {
84     page()->mainFrame()->addToJavaScriptWindowObject("native", this);
85 }
86
87 // Set the background
88 // TODO: Bind to the Dconf property so if the user changes the background On changes it too
89 // TODO: Notify On if the background changed
90 // NOTE: The background is controlled by Qt because setting it as a CSS background is too slow
91 void On::setBackground() {
92     //QConf *backgroundConf = new QConf("org.gnome.desktop.background")
93     //backgroundConf->notify("picture-uri");
94     GSettings *backgroundSettings = g_settings_new("org.gnome.desktop.background");
95     QString bg = QString(g_variant_get_string(g_settings_get_value(backgroundSettings, "picture-uri"), 0));
96     QPalette p = palette();
97     QRect rect = this->rect();
98     if (!bg.isEmpty()) {
99         bg = bg.remove("file://");
100         QPixmap background(QFileInfo(bg).canonicalFilePath());
101         QSize size(rect.width(), rect.height());
102         QPixmap pixmap(background.scaled(size));
103         p.setBrush(QPalette::Background, pixmap);
104         // TODO: Modes. picture-options: zoom, fill, span, scale, center, tile
105     } else {
106         QColor primColor(g_variant_get_string(g_settings_get_value(backgroundSettings, "primary-color"), 0));
107         QColor secColor(g_variant_get_string(g_settings_get_value(backgroundSettings, "secondary-color"), 0));
108         QString mode = QString(g_variant_get_string(g_settings_get_value(backgroundSettings, "color-shading-type"), 0));
109         QBrush brush;
110         if (mode == "solid") {
111             brush = QBrush(primColor);
112         } else if (mode == "vertical") {
113             QLinearGradient grad(QPointF(0, 0), QPointF(0, rect.height()));
114             grad.setColorAt(0, primColor);
115             grad.setColorAt(1, secColor);
116             brush = QBrush(grad);
117         } else {
118             QLinearGradient grad(QPointF(0, 0), QPointF(rect.width(), 0));
119             grad.setColorAt(0, primColor);
120             grad.setColorAt(1, secColor);
121             brush = QBrush(grad);
122         }
123         p.setBrush(QPalette::Background, brush);
124     }
125     setPalette(p);
126     p.setBrush(QPalette::Base, Qt::transparent);
127     page()->setPalette(p);
128     setAttribute(Qt::WA_OpaquePaintEvent, false);
129     g_object_unref(backgroundSettings);
130 }
131
132 // Return a string list with file uri and mode
133 QStringList On::getBackground() {
134     GSettings *background = g_settings_new("org.gnome.desktop.background");
135     QString bg = QString(g_variant_get_string(g_settings_get_value(background, "picture-uri"), 0));
136     QString mode;
137     if (!bg.isEmpty()) {
138         mode = QString(g_variant_get_string(g_settings_get_value(background, "picture-options"), 0));
139         bg = "url('"+bg+"')";
140     } else {
141         QColor primColor = QColor(g_variant_get_string(g_settings_get_value(background, "primary-color"), 0));
142         QColor secColor = QColor(g_variant_get_string(g_settings_get_value(background, "secondary-color"), 0));
143         bg = primColor.name() + "," + secColor.name();
144         mode = QString(g_variant_get_string(g_settings_get_value(background, "color-shading-type"), 0));
145     }
146     g_object_unref(background);
147     return (QStringList() << bg << mode);
148 }
149
150 QStringList On::appsMenu() {
151     return apps;
152 }
153
154 void On::setAppsMenu(QStringList list) {
155     apps = list;
156 }
157
158 // Build the apps menu in different thread
159 void On::getAppsMenuAsync() {
160     QFuture<QStringList> future = QtConcurrent::run(this, &On::getAppsMenu);
161     QStringList apps = future.result();
162     setAppsMenu(apps);
163     page()->mainFrame()->evaluateJavaScript("refreshAppsMenu();");
164 }
165
166 // Get Chromium bookmarks
167 QString On::getBookmarks() {
168     QFile file(QDir::homePath()+"/.config/chromium/Default/Bookmarks");
169     if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
170         QString contents;
171         while (!file.atEnd()) {
172             QByteArray line = file.readLine();
173             contents.append(line);
174         }
175         return contents;
176     }
177     return QString();
178 }
179
180 // Build the bookmarks list in different thread
181 // UNUSED
182 // TODO: Use it
183 void On::getBookmarksAsync() {
184     QFuture<QString> future = QtConcurrent::run(this, &On::getBookmarks);
185     QString bookmarks = future.result();
186     page()->mainFrame()->evaluateJavaScript("refreshPlacesMenu();");
187 }
188
189 // Get Chromium bookmarks
190 QString On::getFirefoxBookmarks() {
191     QFile file(QDir::homePath()+"/.mozilla/firefox/gcf8epya.default/bookmarkbackups/bookmarks-2011-07-05.json");
192     file.open(QIODevice::ReadOnly | QIODevice::Text);
193     QString contents;
194     while (!file.atEnd()) {
195         QByteArray line = file.readLine();
196         contents.append(line);
197     }
198     return contents;
199 }
200
201 // Build the bookmarks list in different thread
202 // UNUSED
203 // TODO: Use it
204 void On::getFirefoxBookmarksAsync() {
205     QFuture<QString> future = QtConcurrent::run(this, &On::getFirefoxBookmarks);
206     QString bookmarks = future.result();
207     page()->mainFrame()->evaluateJavaScript("refreshPlacesMenu();");
208 }
209
210 // Return a string list with 0: appname 1: appicon 2: appexec
211 QStringList On::getAppsMenu() {
212     QDirIterator walker("/usr/share/applications", QStringList("*.desktop"), QDir::AllEntries, QDirIterator::Subdirectories);
213     QStringList apps;
214     g_desktop_app_info_set_desktop_env(release().toAscii());
215     while(walker.hasNext()) {
216         qDebug(walker.next().toAscii());
217         GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new(walker.fileName().toAscii());
218         if (g_app_info_should_show(appInfo)) {
219             QString name = getAppName(walker.fileName());
220             QString icon = getAppIcon(walker.fileName());
221             QString exec = getAppExec(walker.fileName());
222             //if (!name.isEmpty() && !icon.isEmpty() && !exec.isEmpty()) {
223                 apps.append(name);
224                 apps.append(icon);
225                 apps.append(exec);
226             //}
227         }
228         g_object_unref(appInfo);
229     }
230     return apps;
231 }
232
233 // Get the app name of a desktop entry
234 QString On::getAppName(QString app) {
235     GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new(app.toAscii());
236     //QString a = QString::fromUtf8(g_app_info_get_name(appInfo));
237     QString a = QString::fromUtf8(g_app_info_get_display_name(appInfo));
238     g_object_unref(appInfo);
239     return a;
240 }
241
242 // Get the description name of a desktop entry
243 QString On::getAppDescription(QString app) {
244     GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new(app.toAscii());
245     QString a = g_app_info_get_description(appInfo);
246     g_object_unref(appInfo);
247     return a;
248 }
249
250 // Get the base64 of the app icon of a desktop entry
251 QString On::getAppIcon(QString app) {
252     // Get the base64 of the app icon of a desktop entry
253     GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new(app.toAscii());
254     GIcon *gicon = g_app_info_get_icon(appInfo);
255     GtkIconInfo *iconInfo = gtk_icon_theme_lookup_by_gicon(gtk_icon_theme_get_default(), gicon, 48, (GtkIconLookupFlags)0);
256     GdkPixbuf *pixbuf = gtk_icon_info_load_icon(iconInfo, NULL);
257     g_object_unref(gicon);
258
259     if (pixbuf == NULL) {
260         iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), "application-default-icon", 48, (GtkIconLookupFlags)0);
261         pixbuf = gtk_icon_info_load_icon(iconInfo, NULL);
262     }
263
264     gtk_icon_info_free(iconInfo);
265
266     QImage image(gdk_pixbuf_get_pixels(pixbuf),
267                  gdk_pixbuf_get_width(pixbuf),
268                  gdk_pixbuf_get_height(pixbuf),
269                  gdk_pixbuf_get_rowstride(pixbuf),
270                  QImage::Format_ARGB32);
271     QImage swappedImage = image.rgbSwapped();
272
273     QByteArray byteArray;
274     QBuffer buffer(&byteArray);
275     swappedImage.save(&buffer, "PNG");
276     QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data());
277
278     g_object_unref(pixbuf);
279     g_object_unref(appInfo);
280
281     return iconBase64;
282 }
283
284 // Get the categories of a desktop entry
285 // TODO: GIO doesn't provide a method to do that. Look for a form to retrieve categories
286 QString On::getAppCategories(QString app) {
287     GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new(app.toAscii());
288     QString a = /*g_desktop_app_info_get_categories(appInfo)*/"s";
289     g_object_unref(appInfo);
290     return a;
291 }
292
293 // Get the command to execute of a desktop entry
294 QString On::getAppExec(QString app) {
295     //GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new_from_filename(QString("/usr/share/applications/"+app).toAscii());
296     GAppInfo* appInfo = (GAppInfo*)g_desktop_app_info_new(app.toAscii());
297     QString a = g_app_info_get_commandline(appInfo);
298     a.remove("%u", Qt::CaseInsensitive);
299     a.remove("%f", Qt::CaseInsensitive);
300     return a;
301 }
302
303 // Get the base64 of the theme icon
304 QString On::getThemeIcon(QString icon) {
305     GtkIconInfo *iconInfo;
306     QImage *image;
307     if (icon.contains("/") && !QFile::exists(icon)) {
308         icon = "application-default-icon";
309     } else if (QFile::exists(icon)) {
310         image = new QImage(icon);
311     } else {
312         iconInfo = gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(), icon.toAscii(), 48, (GtkIconLookupFlags)0);
313         GdkPixbuf *pixbuf = gtk_icon_info_load_icon(iconInfo, NULL);
314         gtk_icon_info_free(iconInfo);
315         if (pixbuf == NULL) {
316             return QString();
317         }
318         image = new QImage(gdk_pixbuf_get_pixels(pixbuf),
319                        gdk_pixbuf_get_width(pixbuf),
320                        gdk_pixbuf_get_height(pixbuf),
321                        gdk_pixbuf_get_rowstride(pixbuf),
322                        QImage::Format_ARGB32);
323         g_object_unref(pixbuf);
324     }
325
326     QImage swappedImage = image->rgbSwapped();
327
328     QByteArray byteArray;
329     QBuffer buffer(&byteArray);
330     swappedImage.save(&buffer, "PNG");
331     QString iconBase64 = QString::fromLatin1(byteArray.toBase64().data());
332
333     return iconBase64;
334 }
335
336
337 // Set some watchers so we can monitorize dirs, files etc.
338 void On::setWatchers() {
339     QFileSystemWatcher *appsWatcher = new QFileSystemWatcher;
340     appsWatcher->addPath("/usr/share/applications/");
341     appsWatcher->addPath(QDir::homePath()+"/.local/share/applications");
342     connect(appsWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(appsChanged(QString)));
343
344     // TODO: Add Firefox & other browser support
345     QFileSystemWatcher *bookmarksWatcher = new QFileSystemWatcher;
346     bookmarksWatcher->addPath(QDir::homePath()+"/.config/chromium/Default/Bookmarks");
347     connect(bookmarksWatcher, SIGNAL(directoryChanged(QString)), this, SLOT(bookmarksChanged(QString)));
348 }
349
350 // Execute code when the /usr/share/applications dir is modified
351 // NOTE: We have to do it because Glib is not compatible with signals, so we can't define new signals
352 void On::appsChanged(QString path) {
353     getAppsMenuAsync();
354 }
355
356 // Execute code when the bookmarks dir is modified
357 void On::bookmarksChanged(QString path) {
358     getBookmarksAsync();
359 }
360
361 // Execute a command
362 // NOTE: The buffer of the process is being read and shown
363 void On::exec(QString exec) {
364     /*QProcess proc;
365     proc.start("/bin/sh -c \""+exec+"\"");*/
366     QProcess::startDetached("/bin/sh -c \""+exec+"\"");
367 }
368
369 // Get widget list
370 QStringList On::widgetList() {
371     return widgets;
372 }
373
374 void On::setWidgetList(QStringList list) {
375     widgets = list;
376 }
377
378 // Get widget list
379 QStringList On::getWidgetList() {
380     QDirIterator walker("/usr/share/on/shell/widgets", QStringList("*.json"), QDir::AllEntries, QDirIterator::Subdirectories);
381     QStringList widgets;
382     while(walker.hasNext()) {
383         qDebug(walker.next().toAscii());
384         QString id = walker.fileName().remove(".json");
385         //widgets.append(id);
386         QFile file("/usr/share/on/shell/widgets/"+id+".json");
387         file.open(QIODevice::ReadOnly | QIODevice::Text);
388         QString contents;
389         while (!file.atEnd()) {
390             QByteArray line = file.readLine();
391             contents.append(line);
392         }
393         widgets.append(contents);
394     }
395     return widgets;
396 }
397
398 // Build the widget list in different thread<div id="ext-gen1029" class="x-scroller x-layout-box-inner x-layout-box" style="-webkit-box-orient: vertical; -webkit-box-direction: normal; -webkit-box-pack: start; min-height: 753px; width: 1280px; -webkit-transform: translate3d(0px, 0px, 0px); "><div id="ext-comp-1111" class=" x-container" style="margin-top: 20px; margin-right: 20px; margin-bottom: 20px; margin-left: 20px; "><div class="x-layout-box-inner x-layout-box" id="ext-gen1350" style="-webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-box-pack: start; -webkit-box-align: center; min-width: 1240px; height: 0px; "></div></div></div>
399 void On::getWidgetListAsync() {
400     QFuture<QStringList> future = QtConcurrent::run(this, &On::getWidgetList);
401     QStringList widgets = future.result();
402     setWidgetList(widgets);
403     page()->mainFrame()->evaluateJavaScript("refreshWidgetList();");
404 }
405
406 void On::showNotification(QString title, QString content, QString themeIcon, int time) {
407     //QProcess::startDetached("notify-send "+title+" "+content+" -i "+themeIcon);
408
409     NotifyNotification *notif = notify_notification_new(title.toUtf8(), content.toUtf8(), themeIcon.toUtf8());
410     notify_notification_set_timeout(notif, time);
411     notify_notification_show(notif, NULL);
412     g_object_unref(notif);
413 }
414
415 void On::logout() {
416     //QProcess::startDetached("lxsession-logout");
417     QProcess::startDetached("gnome-session-quit --logout");
418     //close();
419 }