Back to index

bamf  0.2.120
bamf-application.c
Go to the documentation of this file.
00001 /*
00002  * Copyright 2010 Canonical Ltd.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of either or both of the following licenses:
00006  *
00007  * 1) the GNU Lesser General Public License version 3, as published by the
00008  * Free Software Foundation; and/or
00009  * 2) the GNU Lesser General Public License version 2.1, as published by
00010  * the Free Software Foundation.
00011  *
00012  * This program is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranties of
00014  * MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
00015  * PURPOSE.  See the applicable version of the GNU Lesser General Public
00016  * License for more details.
00017  *
00018  * You should have received a copy of both the GNU Lesser General Public
00019  * License version 3 and version 2.1 along with this program.  If not, see
00020  * <http://www.gnu.org/licenses/>
00021  *
00022  * Authored by: Jason Smith <jason.smith@canonical.com>
00023  *              Neil Jagdish Patel <neil.patel@canonical.com>
00024  *
00025  */
00033 #if HAVE_CONFIG_H
00034 #include <config.h>
00035 #endif
00036 
00037 #include "bamf-application.h"
00038 #include "bamf-window.h"
00039 #include "bamf-factory.h"
00040 #include "bamf-application-private.h"
00041 #include "bamf-view-private.h"
00042 
00043 #include <gio/gdesktopappinfo.h>
00044 #include <dbus/dbus.h>
00045 #include <dbus/dbus-glib.h>
00046 #include <dbus/dbus-glib-lowlevel.h>
00047 #include <string.h>
00048 
00049 G_DEFINE_TYPE (BamfApplication, bamf_application, BAMF_TYPE_VIEW);
00050 
00051 #define BAMF_APPLICATION_GET_PRIVATE(o) \
00052   (G_TYPE_INSTANCE_GET_PRIVATE ((o), BAMF_TYPE_APPLICATION, BamfApplicationPrivate))
00053 
00054 enum
00055 {
00056   WINDOW_ADDED,
00057   WINDOW_REMOVED,
00058 
00059   LAST_SIGNAL,
00060 };
00061 
00062 static guint application_signals[LAST_SIGNAL] = { 0 };
00063 
00064 struct _BamfApplicationPrivate
00065 {
00066   DBusGConnection *connection;
00067   DBusGProxy      *proxy;
00068   gchar           *application_type;
00069   gchar           *desktop_file;
00070   GList           *cached_xids;
00071   int              show_stubs;
00072 };
00073 
00074 const gchar *
00075 bamf_application_get_desktop_file (BamfApplication *application)
00076 {
00077   BamfApplicationPrivate *priv;
00078   gchar *file;
00079   GError *error = NULL;
00080 
00081   g_return_val_if_fail (BAMF_IS_APPLICATION (application), FALSE);
00082   priv = application->priv;
00083 
00084   if (priv->desktop_file)
00085     return priv->desktop_file;
00086 
00087   if (!_bamf_view_remote_ready (BAMF_VIEW (application)))
00088     return NULL;
00089 
00090   if (!dbus_g_proxy_call (priv->proxy,
00091                           "DesktopFile",
00092                           &error,
00093                           G_TYPE_INVALID,
00094                           G_TYPE_STRING, &file,
00095                           G_TYPE_INVALID))
00096     {
00097       g_warning ("Failed to fetch path: %s", error->message);
00098       g_error_free (error);
00099 
00100       return NULL;
00101     }
00102 
00103   if (file && file[0] == '\0')
00104     {
00105       g_free (file);
00106       file = NULL;
00107     }
00108 
00109   priv->desktop_file = file;
00110   return file;
00111 }
00112 
00113 const gchar *
00114 bamf_application_get_application_type (BamfApplication *application)
00115 {
00116   BamfApplicationPrivate *priv;
00117   gchar *type;
00118   GError *error = NULL;
00119 
00120   g_return_val_if_fail (BAMF_IS_APPLICATION (application), FALSE);
00121   priv = application->priv;
00122 
00123   if (priv->application_type)
00124     return priv->application_type;
00125 
00126   if (!_bamf_view_remote_ready (BAMF_VIEW (application)))
00127     return NULL;
00128 
00129   if (!dbus_g_proxy_call (priv->proxy,
00130                           "ApplicationType",
00131                           &error,
00132                           G_TYPE_INVALID,
00133                           G_TYPE_STRING, &type,
00134                           G_TYPE_INVALID))
00135     {
00136       g_warning ("Failed to fetch path: %s", error->message);
00137       g_error_free (error);
00138 
00139       return NULL;
00140     }
00141 
00142   priv->application_type = type;
00143   return type;
00144 }
00145 
00146 GArray *
00147 bamf_application_get_xids (BamfApplication *application)
00148 {
00149   BamfApplicationPrivate *priv;
00150   GArray *xids;
00151   GError *error = NULL;
00152 
00153   g_return_val_if_fail (BAMF_IS_APPLICATION (application), FALSE);
00154   priv = application->priv;
00155 
00156   if (!_bamf_view_remote_ready (BAMF_VIEW (application)))
00157     return NULL;
00158 
00159   if (!dbus_g_proxy_call (priv->proxy,
00160                           "Xids",
00161                           &error,
00162                           G_TYPE_INVALID,
00163                           DBUS_TYPE_G_UINT_ARRAY, &xids,
00164                           G_TYPE_INVALID))
00165     {
00166       g_warning ("Failed to fetch xids: %s", error->message);
00167       g_error_free (error);
00168 
00169       return NULL;
00170     }
00171 
00172   return xids;
00173 }
00174 
00175 GList *
00176 bamf_application_get_windows (BamfApplication *application)
00177 {
00178   GList *children, *l;
00179   GList *windows = NULL;
00180   BamfView *view;
00181 
00182   g_return_val_if_fail (BAMF_IS_APPLICATION (application), NULL);
00183 
00184   children = bamf_view_get_children (BAMF_VIEW (application));
00185 
00186   for (l = children; l; l = l->next)
00187     {
00188       view = l->data;
00189 
00190       if (BAMF_IS_WINDOW (view));
00191         {
00192           windows = g_list_prepend (windows, view);
00193         }
00194     }
00195 
00196   g_list_free (children);
00197   return windows;
00198 }
00199 
00200 gboolean
00201 bamf_application_get_show_menu_stubs (BamfApplication * application)
00202 {
00203   BamfApplicationPrivate *priv;
00204   GError *error = NULL;
00205   gboolean result;
00206 
00207   g_return_val_if_fail (BAMF_IS_APPLICATION (application), TRUE);
00208 
00209   priv = application->priv;
00210 
00211   if (!_bamf_view_remote_ready (BAMF_VIEW (application)))
00212     return TRUE;
00213 
00214   if (priv->show_stubs == -1)
00215     {
00216       if (!dbus_g_proxy_call (application->priv->proxy,
00217                               "ShowStubs",
00218                               &error,
00219                               G_TYPE_INVALID,
00220                               G_TYPE_BOOLEAN, &result,
00221                               G_TYPE_INVALID))
00222         {
00223           g_warning ("Failed to fetch show_stubs: %s", error->message);
00224           g_error_free (error);
00225 
00226           return TRUE;
00227         }
00228 
00229       if (result)
00230         priv->show_stubs = 1;
00231       else
00232         priv->show_stubs = 0;
00233     }
00234 
00235   return priv->show_stubs;
00236 }
00237 
00238 static BamfClickBehavior
00239 bamf_application_get_click_suggestion (BamfView *view)
00240 {
00241   if (!bamf_view_is_running (view))
00242     return BAMF_CLICK_BEHAVIOR_OPEN;
00243   return 0;
00244 }
00245 
00246 static void
00247 bamf_application_on_window_added (DBusGProxy *proxy, char *path, BamfApplication *self)
00248 {
00249   BamfView *view;
00250   BamfFactory *factory;
00251 
00252   g_return_if_fail (BAMF_IS_APPLICATION (self));
00253 
00254   factory = _bamf_factory_get_default ();
00255   view = _bamf_factory_view_for_path_type (factory, path, BAMF_FACTORY_WINDOW);
00256 
00257   if (BAMF_IS_WINDOW (view))
00258     {
00259       guint32 xid = bamf_window_get_xid (BAMF_WINDOW (view));
00260 
00261       if (!g_list_find (self->priv->cached_xids, GUINT_TO_POINTER (xid)))
00262       {
00263         self->priv->cached_xids = g_list_prepend (self->priv->cached_xids, GUINT_TO_POINTER (xid));
00264       }
00265 
00266       g_signal_emit (G_OBJECT (self), application_signals[WINDOW_ADDED], 0, view);
00267     }
00268 }
00269 
00270 static void
00271 bamf_application_on_window_removed (DBusGProxy *proxy, char *path, BamfApplication *self)
00272 {
00273   BamfView *view;
00274   BamfFactory *factory;
00275 
00276   g_return_if_fail (BAMF_IS_APPLICATION (self));
00277 
00278   factory = _bamf_factory_get_default ();
00279   view = _bamf_factory_view_for_path_type (factory, path, BAMF_FACTORY_WINDOW);
00280 
00281   if (BAMF_IS_WINDOW (view))
00282     {
00283       guint32 xid = bamf_window_get_xid (BAMF_WINDOW (view));
00284       self->priv->cached_xids = g_list_remove (self->priv->cached_xids, GUINT_TO_POINTER (xid));
00285 
00286       g_signal_emit (G_OBJECT (self), application_signals[WINDOW_REMOVED], 0, view);
00287     }
00288 }
00289 
00290 GList *
00291 _bamf_application_get_cached_xids (BamfApplication *self)
00292 {
00293   g_return_val_if_fail (BAMF_IS_APPLICATION (self), NULL);
00294 
00295   return self->priv->cached_xids;
00296 }
00297 
00298 static void
00299 bamf_application_unset_proxy (BamfApplication* self)
00300 {
00301   BamfApplicationPrivate *priv;
00302 
00303   g_return_if_fail (BAMF_IS_APPLICATION (self));
00304   priv = self->priv;
00305 
00306   if (!priv->proxy)
00307     return;
00308 
00309   dbus_g_proxy_disconnect_signal (priv->proxy,
00310                                  "WindowAdded",
00311                                  (GCallback) bamf_application_on_window_added,
00312                                  self);
00313 
00314   dbus_g_proxy_disconnect_signal (priv->proxy,
00315                                  "WindowRemoved",
00316                                  (GCallback) bamf_application_on_window_removed,
00317                                  self);
00318 
00319   g_object_unref (priv->proxy);
00320   priv->proxy = NULL;
00321 }
00322 
00323 static void
00324 bamf_application_dispose (GObject *object)
00325 {
00326   BamfApplication *self;
00327   BamfApplicationPrivate *priv;
00328 
00329   self = BAMF_APPLICATION (object);
00330   priv = self->priv;
00331 
00332   if (priv->application_type)
00333     {
00334       g_free (priv->application_type);
00335       priv->application_type = NULL;
00336     }
00337 
00338   if (priv->desktop_file)
00339     {
00340       g_free (priv->desktop_file);
00341       priv->desktop_file = NULL;
00342     }
00343 
00344   if (priv->cached_xids)
00345     {
00346       g_list_free (priv->cached_xids);
00347       priv->cached_xids = NULL;
00348     }
00349 
00350   bamf_application_unset_proxy (self);
00351 
00352   if (G_OBJECT_CLASS (bamf_application_parent_class)->dispose)
00353     G_OBJECT_CLASS (bamf_application_parent_class)->dispose (object);
00354 }
00355 
00356 static void
00357 bamf_application_set_path (BamfView *view, const char *path)
00358 {
00359   BamfApplication *self;
00360   BamfApplicationPrivate *priv;
00361 
00362   self = BAMF_APPLICATION (view);
00363   priv = self->priv;
00364 
00365   bamf_application_unset_proxy (self);
00366   priv->proxy = dbus_g_proxy_new_for_name (priv->connection,
00367                                            "org.ayatana.bamf",
00368                                            path,
00369                                            "org.ayatana.bamf.application");
00370   if (priv->proxy == NULL)
00371     {
00372       g_critical ("Unable to get org.ayatana.bamf.application application");
00373       return;
00374     }
00375 
00376   dbus_g_proxy_add_signal (priv->proxy,
00377                            "WindowAdded",
00378                            G_TYPE_STRING,
00379                            G_TYPE_INVALID);
00380 
00381   dbus_g_proxy_add_signal (priv->proxy,
00382                            "WindowRemoved",
00383                            G_TYPE_STRING,
00384                            G_TYPE_INVALID);
00385 
00386   dbus_g_proxy_connect_signal (priv->proxy,
00387                                "WindowAdded",
00388                                (GCallback) bamf_application_on_window_added,
00389                                self,
00390                                NULL);
00391 
00392   dbus_g_proxy_connect_signal (priv->proxy,
00393                                "WindowRemoved",
00394                                (GCallback) bamf_application_on_window_removed,
00395                                self,
00396                                NULL);
00397 
00398   GList *children, *l;
00399   children = bamf_view_get_children (view);
00400 
00401   if (priv->cached_xids)
00402     {
00403       g_list_free (priv->cached_xids);
00404       priv->cached_xids = NULL;
00405     }
00406 
00407   for (l = children; l; l = l->next)
00408     {
00409       if (!BAMF_IS_WINDOW (l->data))
00410         continue;
00411 
00412       guint32 xid = bamf_window_get_xid (BAMF_WINDOW (l->data));
00413       priv->cached_xids = g_list_prepend (priv->cached_xids, GUINT_TO_POINTER (xid));
00414     }
00415 
00416   g_list_free (children);
00417 }
00418 
00419 static void
00420 bamf_application_load_data_from_file (BamfApplication *self)
00421 {
00422   GDesktopAppInfo *desktop_info;
00423   GIcon *gicon;
00424   char *name;
00425   char *icon;
00426   GKeyFile * keyfile;
00427   GError *error;
00428 
00429   keyfile = g_key_file_new();
00430   if (!g_key_file_load_from_file(keyfile, self->priv->desktop_file, G_KEY_FILE_NONE, NULL)) {
00431       g_key_file_free(keyfile);
00432     return;
00433   }
00434 
00435   desktop_info = g_desktop_app_info_new_from_keyfile (keyfile);
00436 
00437   if (!desktop_info)
00438     return;
00439 
00440   name = g_strdup (g_app_info_get_name (G_APP_INFO (desktop_info)));
00441 
00442   if (g_key_file_has_key(keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-FullName", NULL))
00443               {
00444                 /* Grab the better name if its available */
00445                 gchar *fullname = NULL;
00446                 error = NULL;
00447                 fullname = g_key_file_get_locale_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-FullName", NULL, &error);
00448                 if (error != NULL)
00449                   {
00450                     g_error_free (error);
00451                     if (fullname)
00452                       g_free (fullname);
00453                   }
00454                 else
00455                   {
00456                     g_free (name);
00457                     name = fullname;
00458                   }
00459               }
00460 
00461   _bamf_view_set_name (BAMF_VIEW (self), name);
00462 
00463   gicon = g_app_info_get_icon (G_APP_INFO (desktop_info));
00464   icon = g_icon_to_string (gicon);
00465 
00466   if (!icon)
00467     icon = g_strdup ("application-default-icon");
00468 
00469   _bamf_view_set_icon (BAMF_VIEW (self), icon);
00470   g_free (icon);
00471   g_key_file_free (keyfile);
00472   g_free (name);
00473   g_object_unref (desktop_info);
00474 }
00475 
00476 static void
00477 bamf_application_class_init (BamfApplicationClass *klass)
00478 {
00479   GObjectClass *obj_class = G_OBJECT_CLASS (klass);
00480   BamfViewClass *view_class = BAMF_VIEW_CLASS (klass);
00481 
00482   obj_class->dispose     = bamf_application_dispose;
00483   view_class->set_path   = bamf_application_set_path;
00484   view_class->click_behavior = bamf_application_get_click_suggestion;
00485 
00486   g_type_class_add_private (obj_class, sizeof (BamfApplicationPrivate));
00487 
00488   application_signals [WINDOW_ADDED] =
00489        g_signal_new ("window-added",
00490                      G_OBJECT_CLASS_TYPE (klass),
00491                      0,
00492                      0, NULL, NULL,
00493                      g_cclosure_marshal_VOID__OBJECT,
00494                      G_TYPE_NONE, 1,
00495                      BAMF_TYPE_VIEW);
00496 
00497   application_signals [WINDOW_REMOVED] =
00498        g_signal_new ("window-removed",
00499                      G_OBJECT_CLASS_TYPE (klass),
00500                      0,
00501                      0, NULL, NULL,
00502                      g_cclosure_marshal_VOID__OBJECT,
00503                      G_TYPE_NONE, 1,
00504                      BAMF_TYPE_VIEW);
00505 }
00506 
00507 
00508 static void
00509 bamf_application_init (BamfApplication *self)
00510 {
00511   BamfApplicationPrivate *priv;
00512   GError           *error = NULL;
00513 
00514   priv = self->priv = BAMF_APPLICATION_GET_PRIVATE (self);
00515   priv->show_stubs = -1;
00516 
00517   priv->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
00518   if (priv->connection == NULL)
00519     {
00520       g_critical ("Failed to open connection to bus: %s",
00521                error != NULL ? error->message : "Unknown");
00522       if (error)
00523         g_error_free (error);
00524       return;
00525     }
00526 }
00527 
00528 BamfApplication *
00529 bamf_application_new (const char * path)
00530 {
00531   BamfApplication *self;
00532   self = g_object_new (BAMF_TYPE_APPLICATION, NULL);
00533 
00534   _bamf_view_set_path (BAMF_VIEW (self), path);
00535 
00536   return self;
00537 }
00538 
00539 BamfApplication *
00540 bamf_application_new_favorite (const char * favorite_path)
00541 {
00542   BamfApplication *self;
00543   GKeyFile        *desktop_keyfile;
00544   GKeyFileFlags    flags;
00545   gchar           *type;
00546   gboolean         supported = FALSE;
00547 
00548   // check that we support this kind of desktop file
00549   desktop_keyfile = g_key_file_new ();
00550   flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
00551   if (g_key_file_load_from_file (desktop_keyfile, favorite_path, flags, NULL))
00552     {
00553       type = g_key_file_get_string (desktop_keyfile, "Desktop Entry", "Type", NULL);
00554       if (g_strcmp0 (type, "Application") == 0)
00555         supported = TRUE;
00556 
00557       g_key_file_free (desktop_keyfile);
00558       g_free (type);
00559     }
00560   if (!supported)
00561     return NULL;
00562 
00563   self = g_object_new (BAMF_TYPE_APPLICATION, NULL);
00564 
00565   self->priv->desktop_file = g_strdup (favorite_path);
00566   bamf_application_load_data_from_file (self);
00567 
00568   return self;
00569 }