Back to index

bamf  0.2.120
bamf-matcher.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2012 Canonical Ltd
00003  *
00004  * This program is free software: you can redistribute it and/or modify
00005  * it under the terms of the GNU General Public License version 3 as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00011  * GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License
00014  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Authored by: Jason Smith <jason.smith@canonical.com>
00017  *              Marco Trevisan (TreviƱo) <3v1n0@ubuntu.com>
00018  *
00019  */
00020 
00021 #include "config.h"
00022 
00023 #include "bamf-matcher.h"
00024 #include "bamf-application.h"
00025 #include "bamf-window.h"
00026 #include "bamf-legacy-window.h"
00027 #include "bamf-legacy-window-test.h"
00028 #include "bamf-legacy-screen.h"
00029 #include "bamf-indicator-source.h"
00030 #include "bamf-xutils.h"
00031 #include <strings.h>
00032 
00033 G_DEFINE_TYPE (BamfMatcher, bamf_matcher, BAMF_DBUS_TYPE_MATCHER_SKELETON);
00034 #define BAMF_MATCHER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE(obj, \
00035                                        BAMF_TYPE_MATCHER, BamfMatcherPrivate))
00036 
00037 enum
00038 {
00039   FAVORITES_CHANGED,
00040 
00041   LAST_SIGNAL,
00042 };
00043 
00044 typedef enum
00045 {
00046   VIEW_ADDED = 0,
00047   VIEW_REMOVED
00048 } ViewChangeType;
00049 
00050 static BamfMatcher *static_matcher;
00051 static guint matcher_signals[LAST_SIGNAL] = { 0 };
00052 
00053 struct _BamfMatcherPrivate
00054 {
00055   GArray          * bad_prefixes;
00056   GArray          * good_prefixes;
00057   GArray          * known_pids;
00058   GHashTable      * desktop_id_table;
00059   GHashTable      * desktop_file_table;
00060   GHashTable      * desktop_class_table;
00061   GHashTable      * registered_pids;
00062   GHashTable      * opened_closed_paths_table;
00063   GList           * views;
00064   GList           * monitors;
00065   GList           * favorites;
00066   BamfView        * active_app;
00067   BamfView        * active_win;
00068   guint             dispatch_changes_id;
00069 };
00070 
00071 static void
00072 on_view_active_changed (BamfView *view, gboolean active, BamfMatcher *matcher)
00073 {
00074   BamfMatcherPrivate *priv;
00075   BamfView *last;
00076 
00077   g_return_if_fail (BAMF_IS_MATCHER (matcher));
00078   g_return_if_fail (BAMF_IS_VIEW (view));
00079 
00080   priv = matcher->priv;
00081 
00082   if (BAMF_IS_APPLICATION (view))
00083     {
00084       /* Do some handy short circuiting so we can assume a signal
00085        * will be generated at the end of this
00086        */
00087       if (!active && priv->active_app != view)
00088         return;
00089 
00090       if (active && priv->active_app == view)
00091         return;
00092 
00093       last = priv->active_app;
00094 
00095       if (active)
00096         priv->active_app = view;
00097       else
00098         priv->active_app = NULL;
00099 
00100       g_signal_emit_by_name (matcher, "active-application-changed",
00101                              BAMF_IS_VIEW (last) ? bamf_view_get_path (BAMF_VIEW (last)) : "",
00102                              BAMF_IS_VIEW (priv->active_app) ? bamf_view_get_path (BAMF_VIEW (priv->active_app)) : "");
00103     }
00104   else if (BAMF_IS_WINDOW (view))
00105     {
00106       /* Do some handy short circuiting so we can assume a signal
00107        * will be generated at the end of this
00108        */
00109       if (!active && priv->active_win != view)
00110         return;
00111 
00112       if (active && priv->active_win == view)
00113         return;
00114 
00115       last = priv->active_win;
00116 
00117       if (active)
00118         priv->active_win = view;
00119       else
00120         priv->active_win = NULL;
00121 
00122       g_signal_emit_by_name (matcher, "active-window-changed",
00123                              BAMF_IS_VIEW (last) ? bamf_view_get_path (BAMF_VIEW (last)) : "",
00124                              BAMF_IS_VIEW (priv->active_win) ? bamf_view_get_path (BAMF_VIEW (priv->active_win)) : "");
00125     }
00126 }
00127 
00128 static void bamf_matcher_unregister_view (BamfMatcher *self, BamfView *view);
00129 
00130 static BamfApplication *
00131 bamf_matcher_get_application_by_desktop_file (BamfMatcher *self, const char *desktop_file)
00132 {
00133   GList *l;
00134   BamfView *view;
00135 
00136   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
00137 
00138   if (!desktop_file)
00139     return NULL;
00140 
00141   for (l = self->priv->views; l; l = l->next)
00142     {
00143       view = l->data;
00144 
00145       if (!BAMF_IS_APPLICATION (view))
00146         continue;
00147 
00148       BamfApplication *app = BAMF_APPLICATION (view);
00149       const gchar *app_desktop;
00150       app_desktop = bamf_application_get_desktop_file (app);
00151 
00152       if (g_strcmp0 (desktop_file, app_desktop) == 0)
00153         {
00154           return app;
00155         }
00156     }
00157 
00158   return NULL;
00159 }
00160 
00161 static gboolean
00162 emit_paths_changed (gpointer user_data)
00163 {
00164   BamfMatcher *matcher;
00165   BamfMatcherPrivate *priv;
00166   GHashTableIter iter;
00167   guint ht_size;
00168   ViewChangeType change_type;
00169   gpointer key, value;
00170   gchar **opened_apps, **closed_apps;
00171   gint i, j;
00172 
00173   g_return_val_if_fail (BAMF_IS_MATCHER (user_data), FALSE);
00174 
00175   matcher = (BamfMatcher*) user_data;
00176   priv = matcher->priv;
00177 
00178   ht_size = g_hash_table_size (priv->opened_closed_paths_table);
00179   /* these will end with NULL pointer */
00180   opened_apps = g_new0 (gchar*, ht_size+1);
00181   closed_apps = g_new0 (gchar*, ht_size+1);
00182   i = 0;
00183   j = 0;
00184 
00185   g_hash_table_iter_init (&iter, priv->opened_closed_paths_table);
00186   while (g_hash_table_iter_next (&iter, &key, &value))
00187     {
00188       change_type = (ViewChangeType) GPOINTER_TO_INT (value);
00189       if (change_type == VIEW_ADDED)
00190         opened_apps[i++] = (gchar*) key;
00191       else
00192         closed_apps[j++] = (gchar*) key;
00193     }
00194 
00195   /* the strings are owned by the hashtable, so emit the signal and clear
00196    * the hashtable then */
00197   g_signal_emit_by_name (matcher, "running-applications-changed",
00198                          opened_apps, closed_apps);
00199 
00200   g_hash_table_remove_all (priv->opened_closed_paths_table);
00201 
00202   g_free (closed_apps);
00203   g_free (opened_apps);
00204 
00205   priv->dispatch_changes_id = 0;
00206 
00207   return FALSE;
00208 }
00209 
00210 static void bamf_matcher_prepare_path_change (BamfMatcher *self, const gchar *desktop_file, ViewChangeType change_type)
00211 {
00212   BamfMatcherPrivate *priv;
00213   BamfApplication *app;
00214 
00215   if (desktop_file == NULL) return;
00216 
00217   g_return_if_fail (BAMF_IS_MATCHER (self));
00218 
00219   priv = self->priv;
00220 
00221   /* the app was already running (ADDED) / had more instances which are still
00222    * there (REMOVED) */
00223   app = bamf_matcher_get_application_by_desktop_file (self, desktop_file);
00224 
00225   if (BAMF_IS_APPLICATION (app) && bamf_view_is_running (BAMF_VIEW (app)))
00226     {
00227       return;
00228     }
00229 
00230   if (!priv->opened_closed_paths_table)
00231     {
00232       priv->opened_closed_paths_table = g_hash_table_new_full (g_str_hash,
00233                                                                g_str_equal,
00234                                                                g_free, NULL);
00235     }
00236 
00237   g_hash_table_insert (priv->opened_closed_paths_table,
00238                        g_strdup (desktop_file), GINT_TO_POINTER (change_type));
00239 
00240   if (priv->dispatch_changes_id == 0)
00241     {
00242       priv->dispatch_changes_id = g_timeout_add (500, emit_paths_changed, self);
00243     }
00244 }
00245 
00246 static void
00247 on_view_closed (BamfView *view, BamfMatcher *self)
00248 {
00249   bamf_matcher_unregister_view (self, view);
00250 }
00251 
00252 static void
00253 bamf_matcher_register_view_stealing_ref (BamfMatcher *self, BamfView *view)
00254 {
00255   const char *path, *type;
00256   GDBusConnection *connection;
00257   GDBusInterfaceSkeleton *dbus_interface = G_DBUS_INTERFACE_SKELETON (self);
00258 
00259   connection = g_dbus_interface_skeleton_get_connection (dbus_interface);
00260   path = bamf_view_export_on_bus (view, connection);
00261   type = bamf_view_get_view_type (view);
00262 
00263   g_signal_connect (G_OBJECT (view), "closed-internal",
00264                     (GCallback) on_view_closed, self);
00265   g_signal_connect (G_OBJECT (view), "active-changed",
00266                     (GCallback) on_view_active_changed, self);
00267 
00268   if (BAMF_IS_APPLICATION (view))
00269     {
00270       bamf_matcher_prepare_path_change (self,
00271         bamf_application_get_desktop_file (BAMF_APPLICATION (view)), VIEW_ADDED);
00272     }
00273 
00274   // This steals the reference of the view
00275   self->priv->views = g_list_prepend (self->priv->views, view);
00276 
00277   g_signal_emit_by_name (self, "view-opened", path, type);
00278 
00279   // trigger manually since this is already active
00280   if (bamf_view_is_active (view))
00281     on_view_active_changed (view, TRUE, self);
00282 }
00283 
00284 static void
00285 bamf_matcher_unregister_view (BamfMatcher *self, BamfView *view)
00286 {
00287   const char * path;
00288   const char * type;
00289 
00290   path = bamf_view_get_path (view);
00291   type = bamf_view_get_view_type (view);
00292 
00293   g_signal_emit_by_name (self, "view-closed", path, type);
00294 
00295   g_signal_handlers_disconnect_by_func (G_OBJECT (view), on_view_closed, self);
00296   g_signal_handlers_disconnect_by_func (G_OBJECT (view), on_view_active_changed, self);
00297 
00298   if (BAMF_IS_APPLICATION (view))
00299     {
00300       bamf_matcher_prepare_path_change (self,
00301           bamf_application_get_desktop_file (BAMF_APPLICATION (view)),
00302           VIEW_REMOVED);
00303     }
00304 
00305   if (self->priv->active_app == view)
00306     self->priv->active_app = NULL;
00307 
00308   if (self->priv->active_win == view)
00309     self->priv->active_win = NULL;
00310 
00311   GList *listed_view = g_list_find (self->priv->views, view);
00312   if (listed_view)
00313     {
00314       self->priv->views = g_list_delete_link (self->priv->views, listed_view);
00315       g_object_unref (view);
00316     }
00317 }
00318 
00319 static char *
00320 get_open_office_window_hint (BamfMatcher * self, BamfLegacyWindow * window)
00321 {
00322   gchar *exec = NULL;
00323   const gchar *name;
00324   const gchar *class;
00325   const char *binary = NULL;
00326   const char *parameter = NULL;
00327   BamfWindowType type;
00328 
00329   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
00330   g_return_val_if_fail (BAMF_IS_LEGACY_WINDOW (window), NULL);
00331 
00332   name = bamf_legacy_window_get_name (window);
00333   class = bamf_legacy_window_get_class_name (window);
00334   type = bamf_legacy_window_get_window_type (window);
00335 
00336   if (name == NULL && class == NULL)
00337     return NULL;
00338 
00339   if (g_str_has_suffix (name, "LibreOffice Writer"))
00340     {
00341       binary = "libreoffice";
00342       parameter = "writer";
00343     }
00344   else if (g_str_has_suffix (name, "LibreOffice Calc"))
00345     {
00346       binary = "libreoffice";
00347        parameter = "calc";
00348     }
00349   else if (g_str_has_suffix (name, "LibreOffice Impress"))
00350     {
00351       binary = "libreoffice";
00352       parameter = "impress";
00353     }
00354   else if (g_str_has_suffix (name, "LibreOffice Math"))
00355     {
00356       binary = "libreoffice";
00357       parameter = "math";
00358     }
00359   else if (g_str_has_suffix (name, "LibreOffice Draw"))
00360     {
00361       binary = "libreoffice";
00362       parameter = "draw";
00363     }
00364   else if (g_strcmp0 (class, "libreoffice-startcenter") == 0)
00365     {
00366       binary = "libreoffice";
00367     }
00368   else if (g_strcmp0 (name, "LibreOffice") == 0 && type == BAMF_WINDOW_NORMAL)
00369     {
00370       binary = "libreoffice";
00371     }
00372   else if (g_str_has_suffix (name, "OpenOffice.org Writer"))
00373     {
00374       binary = "ooffice";
00375       parameter = "writer";
00376     }
00377   else if (g_str_has_suffix (name, "OpenOffice.org Calc"))
00378     {
00379       binary = "ooffice";
00380       parameter = "calc";
00381     }
00382   else if (g_str_has_suffix (name, "OpenOffice.org Impress"))
00383     {
00384       binary = "ooffice";
00385       parameter = "impress";
00386     }
00387   else if (g_str_has_suffix (name, "OpenOffice.org Math"))
00388     {
00389       binary = "ooffice";
00390       parameter = "math";
00391     }
00392   else if (g_str_has_suffix (name, "OpenOffice.org Draw"))
00393     {
00394       binary = "ooffice";
00395       parameter = "draw";
00396     }
00397   else if (g_strcmp0 (name, "OpenOffice.org") == 0 && type == BAMF_WINDOW_NORMAL)
00398     {
00399       binary = "ooffice";
00400     }
00401   else
00402     {
00403       if (type != BAMF_WINDOW_NORMAL || bamf_legacy_window_get_transient (window))
00404       {
00405         /* Child windows can generally easily be recognized by their class */
00406         if (g_strcmp0 (class, "libreoffice-writer") == 0)
00407           {
00408             binary = "libreoffice";
00409             parameter = "writer";
00410           }
00411         else if (g_strcmp0 (class, "libreoffice-calc") == 0)
00412           {
00413             binary = "libreoffice";
00414             parameter = "calc";
00415           }
00416         else if (g_strcmp0 (class, "libreoffice-impress") == 0)
00417           {
00418             binary = "libreoffice";
00419             parameter = "impress";
00420           }
00421         else if (g_strcmp0 (class, "libreoffice-math") == 0)
00422           {
00423             binary = "libreoffice";
00424             parameter = "math";
00425           }
00426         else if (g_strcmp0 (class, "libreoffice-draw") == 0)
00427           {
00428             binary = "libreoffice";
00429             parameter = "draw";
00430           }
00431       }
00432 
00433       if (!binary)
00434         {
00435           /* By default fallback to the main launcher */
00436           if (g_str_has_prefix (class, "OpenOffice"))
00437             {
00438               binary = "ooffice";
00439             }
00440           else
00441             {
00442               binary = "libreoffice";
00443             }
00444         }
00445     }
00446 
00447   if (!binary)
00448     return NULL;
00449 
00450   const char *sufix = NULL;
00451 
00452   if (g_strcmp0 (binary, "libreoffice") == 0)
00453     sufix = " %U";
00454   else if (g_strcmp0 (binary, "ooffice") == 0)
00455     sufix = " %F";
00456 
00457   GList *l;
00458   if (!parameter)
00459     exec = g_strconcat (binary, sufix, NULL);
00460   else
00461     exec = g_strconcat (binary, " --", parameter, sufix, NULL);
00462 
00463   l = g_hash_table_lookup (self->priv->desktop_file_table, exec);
00464   g_free (exec);
00465 
00466   if (!l && parameter)
00467     {
00468       exec = g_strconcat (binary, " -", parameter, sufix, NULL);
00469 
00470       l = g_hash_table_lookup (self->priv->desktop_file_table, exec);
00471       g_free (exec);
00472     }
00473 
00474   return (l ? (char *) l->data : NULL);
00475 }
00476 
00477 /* Attempts to return the binary name for a particular execution string */
00478 static char *
00479 trim_exec_string (BamfMatcher * self, char * execString)
00480 {
00481   gchar *result = NULL, *exec = NULL, *part = NULL, *tmp = NULL;
00482   gchar **parts;
00483   gint i, j;
00484   gboolean regexFail;
00485   gboolean goodPrefix = FALSE;
00486   GRegex *regex;
00487 
00488   if (!execString || (execString && execString[0] == '\0'))
00489     return NULL;
00490 
00491   exec = g_utf8_casefold (execString, -1);
00492   parts = g_strsplit (exec, " ", 0);
00493 
00494   i = 0;
00495   while (parts[i] != NULL)
00496     {
00497       part = parts[i];
00498 
00499       if (part[0] != '-')
00500         {
00501           if (goodPrefix)
00502             {
00503               gchar *tmp = g_strconcat (result, " ", part, NULL);
00504               g_free (result);
00505               result = tmp;
00506             }
00507           else
00508             {
00509               for (j = 0; j < self->priv->good_prefixes->len; j++)
00510                 {
00511                   regex = g_array_index (self->priv->good_prefixes, GRegex *, j);
00512                   if (g_regex_match (regex, part, 0, NULL))
00513                     {
00514                       goodPrefix = TRUE;
00515                       result = g_strdup (part);
00516                       break;
00517                     }
00518                 }
00519             }
00520 
00521           if (!goodPrefix)
00522             {
00523               tmp = g_utf8_strrchr (part, -1, G_DIR_SEPARATOR);
00524               if (tmp)
00525                 part = tmp + 1;
00526 
00527               regexFail = FALSE;
00528               for (j = 0; j < self->priv->bad_prefixes->len; j++)
00529                 {
00530                   regex = g_array_index (self->priv->bad_prefixes, GRegex *, j);
00531                   if (g_regex_match (regex, part, 0, NULL))
00532                     {
00533                       regexFail = TRUE;
00534                       break;
00535                     }
00536                 }
00537 
00538               if (!regexFail)
00539                 {
00540                   result = g_strdup (part);
00541                   break;
00542                 }
00543             }
00544         }
00545       else if (goodPrefix)
00546         {
00547           break;
00548         }
00549 
00550       i++;
00551     }
00552 
00553   if (!result)
00554     {
00555       result = g_strdup (execString);
00556     }
00557   else
00558     {
00559       tmp = result;
00560       
00561       regex = g_regex_new ("((\\.|-)bin|\\.py)$", 0, 0, NULL);
00562       result = g_regex_replace_literal (regex, result, -1, 0, "", 0, NULL);
00563       
00564       g_free (tmp);
00565       g_regex_unref (regex);
00566     }
00567 
00568   g_free (exec);
00569   g_strfreev (parts);
00570 
00571   return result;
00572 }
00573 
00574 static GArray *
00575 bad_prefix_strings (void)
00576 {
00577   GArray *arr = g_array_new (FALSE, TRUE, sizeof (char *));
00578 
00579   char *str = "^gksu(do)?$";
00580   g_array_append_val (arr, str);
00581 
00582   str = "^sudo$";
00583   g_array_append_val (arr, str);
00584 
00585   str = "^java$";
00586   g_array_append_val (arr, str);
00587 
00588   str = "^mono$";
00589   g_array_append_val (arr, str);
00590 
00591   str = "^ruby$";
00592   g_array_append_val (arr, str);
00593 
00594   str = "^padsp$";
00595   g_array_append_val (arr, str);
00596 
00597   str = "^aoss$";
00598   g_array_append_val (arr, str);
00599 
00600   str = "^python(\\d.\\d)?$";
00601   g_array_append_val (arr, str);
00602 
00603   str = "^(ba)?sh$";
00604   g_array_append_val (arr, str);
00605 
00606   return arr;
00607 }
00608 
00609 static GArray *
00610 good_prefix_strings (void)
00611 {
00612   GArray *arr = g_array_new (FALSE, TRUE, sizeof (char *));
00613 
00614   char *str = "^gnome-control-center$";
00615   g_array_append_val (arr, str);
00616 
00617   return arr;
00618 }
00619 
00620 static GArray *
00621 pid_parent_tree (BamfMatcher *self, gint pid)
00622 {
00623   BamfMatcherPrivate *priv;
00624   GArray *tree;
00625   gint known_pid;
00626   gint i;
00627 
00628   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
00629 
00630   priv = self->priv;
00631 
00632   tree = g_array_new (FALSE, TRUE, sizeof (gint));
00633 
00634   g_array_append_val (tree, pid);
00635 
00636   glibtop_proc_uid buf;
00637   glibtop_get_proc_uid (&buf, pid);
00638 
00639   pid = buf.ppid;
00640 
00641   while (pid > 1)
00642     {
00643       for (i = 0; i < priv->known_pids->len; i++)
00644         {
00645           /* ensure we dont match onto a terminal by mistake */
00646           known_pid = g_array_index (priv->known_pids, gint, i);
00647           if (known_pid == pid)
00648             return tree;
00649         }
00650 
00651       g_array_append_val (tree, pid);
00652 
00653       glibtop_proc_uid buf;
00654       glibtop_get_proc_uid (&buf, pid);
00655 
00656       pid = buf.ppid;
00657     }
00658   return tree;
00659 }
00660 
00661 static gboolean
00662 exec_string_should_be_processed (const char *exec)
00663 {
00664   if (!exec)
00665     return TRUE;
00666 
00667   return !g_str_has_prefix (exec, "ooffice") && !g_str_has_prefix (exec, "libreoffice");
00668 }
00669 
00670 static gboolean
00671 is_desktop_folder_item (const char *desktop_file_path, gssize max_len)
00672 {
00673   gsize len;
00674   const char *desktop_folder;
00675 
00676   g_return_val_if_fail (desktop_file_path, FALSE);
00677 
00678   if (max_len > 0)
00679     {
00680       len = max_len;
00681     }
00682   else
00683     {
00684       char *tmp;
00685       tmp = strrchr (desktop_file_path, G_DIR_SEPARATOR);
00686       g_return_val_if_fail (tmp, FALSE);
00687       len = tmp - desktop_file_path;
00688     }
00689 
00690   desktop_folder = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
00691 
00692   if (strncmp (desktop_folder, desktop_file_path, len) == 0)
00693     return TRUE;
00694 
00695   return FALSE;
00696 }
00697 
00698 static void
00699 insert_data_into_tables (BamfMatcher *self,
00700                          const char *data,
00701                          const char *exec,
00702                          const char *desktop_id,
00703                          GHashTable *desktop_file_table,
00704                          GHashTable *desktop_id_table)
00705 {
00706   GList *file_list, *id_list;
00707   char *datadup;
00708   
00709   g_return_if_fail (exec);
00710   g_return_if_fail (desktop_id);
00711 
00712   file_list = g_hash_table_lookup (desktop_file_table, exec);
00713   id_list   = g_hash_table_lookup (desktop_id_table, desktop_id);
00714 
00715   if (g_list_find_custom (file_list, data, (GCompareFunc) g_strcmp0) &&
00716       g_list_find_custom (id_list, data, (GCompareFunc) g_strcmp0))
00717     {
00718       return;
00719     }
00720 
00721   datadup = g_strdup (data);
00722 
00723   /* order so that items whose desktop_id == exec string are first in the list */
00724 
00725   if (g_strcmp0 (exec, desktop_id) == 0 || is_desktop_folder_item (datadup, -1))
00726     {
00727       GList *l, *last;
00728       last = NULL;
00729 
00730       for (l = file_list; l; l = l->next)
00731         {
00732           char *dpath;
00733           char *dname_start, *dname_end;
00734           size_t len;
00735 
00736           dpath = l->data;
00737           dname_start = strrchr (dpath, G_DIR_SEPARATOR);
00738           if (!dname_start)
00739             {
00740               continue;
00741             }
00742 
00743           dname_start++;
00744           dname_end = strrchr (dname_start, '.');
00745           len = dname_end - dname_start;
00746 
00747           if (!dname_end || len < 1)
00748             {
00749               continue;
00750             }
00751 
00752           if (strncmp (desktop_id, dname_start, len) != 0 &&
00753               !is_desktop_folder_item (dpath, (dname_start - dpath - 1)))
00754             {
00755               last = l;
00756               break;
00757             }
00758         }
00759 
00760       file_list = g_list_insert_before (file_list, last, datadup);
00761     }
00762   else
00763     {
00764       file_list = g_list_append (file_list, datadup);
00765     }
00766 
00767   id_list = g_list_append (id_list, datadup);   
00768 
00769   g_hash_table_insert (desktop_file_table, g_strdup (exec),       file_list);
00770   g_hash_table_insert (desktop_id_table,   g_strdup (desktop_id), id_list);
00771 }
00772 
00773 static void
00774 insert_desktop_file_class_into_table (BamfMatcher *self,
00775                                       const char *desktop_file,
00776                                       GHashTable *desktop_class_table)
00777 {
00778   GKeyFile *desktop_keyfile;
00779   char *class;
00780 
00781   g_return_if_fail (desktop_file);
00782 
00783   desktop_keyfile = g_key_file_new ();
00784 
00785   if (g_key_file_load_from_file (desktop_keyfile, desktop_file, G_KEY_FILE_NONE,
00786                                  NULL))
00787     {
00788       class = g_key_file_get_string (desktop_keyfile,
00789                                      G_KEY_FILE_DESKTOP_GROUP,
00790                                      G_KEY_FILE_DESKTOP_KEY_STARTUP_WM_CLASS,
00791                                      NULL);
00792       if (class)
00793         g_hash_table_insert (desktop_class_table, g_strdup (desktop_file), class);
00794 
00795       g_key_file_free (desktop_keyfile);
00796     }
00797 }
00798 
00799 static void
00800 load_desktop_file_to_table (BamfMatcher * self,
00801                             const char *file,
00802                             GHashTable *desktop_file_table,
00803                             GHashTable *desktop_id_table,
00804                             GHashTable *desktop_class_table)
00805 {
00806   GDesktopAppInfo *desktop_file;
00807   char *exec;
00808   char *path;
00809   GString *desktop_id; /* is ok... really */
00810 
00811   g_return_if_fail (BAMF_IS_MATCHER (self));
00812 
00813   desktop_file = g_desktop_app_info_new_from_filename (file);
00814 
00815   if (!G_IS_APP_INFO (desktop_file))
00816     {
00817       return;
00818     }
00819 
00820   if (!g_desktop_app_info_get_show_in (desktop_file, g_getenv ("XDG_CURRENT_DESKTOP")))
00821     {
00822       g_object_unref (desktop_file);
00823       return;
00824     }
00825 
00826   exec = g_strdup (g_app_info_get_commandline (G_APP_INFO (desktop_file)));
00827 
00828   if (!exec)
00829     {
00830       g_object_unref (desktop_file);
00831       return;
00832     }
00833 
00834   if (exec_string_should_be_processed (exec))
00835     {
00842       char *tmp = trim_exec_string (self, exec);
00843       g_free (exec);
00844       exec = tmp;
00845     }
00846 
00847   path = g_path_get_basename (file);
00848   desktop_id = g_string_new (path);
00849   g_free (path);
00850 
00851   desktop_id = g_string_truncate (desktop_id, desktop_id->len - 8); /* remove last 8 characters for .desktop */
00852 
00853   insert_data_into_tables (self, file, exec, desktop_id->str, desktop_file_table, desktop_id_table);
00854   insert_desktop_file_class_into_table (self, file, desktop_class_table);
00855 
00856   g_free (exec);
00857   g_string_free (desktop_id, TRUE);
00858   g_object_unref (desktop_file);
00859 }
00860 
00861 static void
00862 load_directory_to_table (BamfMatcher * self,
00863                          const char *directory,
00864                          GHashTable *desktop_file_table,
00865                          GHashTable *desktop_id_table,
00866                          GHashTable *desktop_class_table)
00867 {
00868   GFile *dir;
00869   GFileEnumerator *enumerator;
00870   GFileInfo *info;
00871   const char *name;
00872   char *path;
00873 
00874   dir = g_file_new_for_path (directory);
00875 
00876   enumerator = g_file_enumerate_children (dir,
00877                                           "standard::*",
00878                                           G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
00879                                           NULL,
00880                                           NULL);
00881 
00882   if (!enumerator)
00883     return;
00884 
00885   info = g_file_enumerator_next_file (enumerator, NULL, NULL);
00886   for (; info; info = g_file_enumerator_next_file (enumerator, NULL, NULL))
00887     {
00888       name = g_file_info_get_name (info);
00889       path = g_build_filename (directory, name, NULL);
00890 
00891       if (g_str_has_suffix (name, ".desktop"))
00892         load_desktop_file_to_table (self,
00893                                     path,
00894                                     desktop_file_table,
00895                                     desktop_id_table,
00896                                     desktop_class_table);
00897 
00898       g_free (path);
00899       g_object_unref (info);
00900     }
00901 
00902   g_object_unref (enumerator);
00903   g_object_unref (dir);
00904 }
00905 
00906 static void
00907 load_index_file_to_table (BamfMatcher * self,
00908                           const char *index_file,
00909                           GHashTable *desktop_file_table,
00910                           GHashTable *desktop_id_table,
00911                           GHashTable *desktop_class_table)
00912 {
00913   GFile *file;
00914   GFileInputStream *stream;
00915   GDataInputStream *input;
00916   char *line;
00917   char *directory;
00918   gsize length;
00919 
00920   file = g_file_new_for_path (index_file);
00921 
00922   g_return_if_fail (file);
00923 
00924   stream = g_file_read (file, NULL, NULL);
00925 
00926   if (!stream)
00927     {
00928       g_object_unref (file);
00929       return;
00930     }
00931 
00932   directory = g_path_get_dirname (index_file);
00933   input = g_data_input_stream_new (G_INPUT_STREAM (stream));
00934 
00935   while ((line = g_data_input_stream_read_line (input, &length, NULL, NULL)))
00936     {
00937       char *exec;
00938       char *filename;
00939       GString *desktop_id;
00940 
00941       gchar **parts = g_strsplit (line, "\t", 3);
00942       exec = parts[1];
00943 
00944       if (exec_string_should_be_processed (exec))
00945         {
00946           char *tmp = trim_exec_string (self, exec);
00947           g_free (parts[1]);
00948           parts[1] = tmp;
00949           exec = parts[1];
00950         }
00951 
00952       filename = g_build_filename (directory, parts[0], NULL);
00953 
00954       desktop_id = g_string_new (parts[0]);
00955       g_string_truncate (desktop_id, desktop_id->len - 8);
00956       
00957       insert_data_into_tables (self, filename, exec, desktop_id->str, desktop_file_table, desktop_id_table);
00958       insert_desktop_file_class_into_table (self, filename, desktop_class_table);
00959 
00960       g_string_free (desktop_id, TRUE);
00961       g_free (line);
00962       g_free (filename);
00963       g_strfreev (parts);
00964       length = 0;
00965     }
00966 
00967   g_object_unref (input);
00968   g_object_unref (stream);
00969   g_object_unref (file);
00970   g_free (directory);
00971 }
00972 
00973 static GList * get_directory_tree_list (GList *) G_GNUC_WARN_UNUSED_RESULT;
00974 
00975 static GList *
00976 get_directory_tree_list (GList *dirs)
00977 {
00978   GList *l;
00979   GFile *file;
00980   GFileEnumerator *enumerator;
00981   GFileInfo *info;
00982   gchar *path, *subpath;
00983 
00984   for (l = dirs; l; l = l->next)
00985     {
00986       path = l->data;
00987       
00988       file = g_file_new_for_path (path);
00989       
00990       if (!g_file_query_exists (file, NULL))
00991         {
00992           g_object_unref (file);
00993           continue;
00994         }
00995       
00996       enumerator = g_file_enumerate_children (file,
00997                                               "standard::*",
00998                                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
00999                                               NULL,
01000                                               NULL);
01001       
01002       if (!enumerator)
01003         continue;
01004 
01005 
01006       info = g_file_enumerator_next_file (enumerator, NULL, NULL);
01007 
01008       while (info)
01009         {
01010           if (g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY)
01011             {
01012               /* append after the current list item for non-recursive recursion love
01013                * and to keep the priorities (hierarchy) of the .desktop directories.
01014                */
01015               subpath = g_build_filename (path, g_file_info_get_name (info), NULL);
01016               dirs = g_list_insert_before (dirs, l->next, subpath);
01017             }
01018 
01019           g_object_unref (info);
01020           info = g_file_enumerator_next_file (enumerator, NULL, NULL);
01021         }
01022 
01023       g_object_unref (enumerator);
01024       g_object_unref (file);
01025     }
01026 
01027   return dirs;
01028 }
01029 
01030 static GList * get_desktop_file_env_directories (GList *, const gchar *) G_GNUC_WARN_UNUSED_RESULT;
01031 
01032 static GList *
01033 get_desktop_file_env_directories (GList *dirs, const gchar *varname)
01034 {
01035   g_return_val_if_fail (varname, dirs);
01036 
01037   const gchar *env;
01038   char *path;
01039   char **data_dirs = NULL;
01040   char **data;
01041 
01042   env = g_getenv (varname);
01043 
01044   if (env)
01045     {
01046       data_dirs = g_strsplit (env, ":", 0);
01047   
01048       for (data = data_dirs; *data; data++)
01049         {
01050           path = g_build_filename (*data, "applications", NULL);
01051           if (g_file_test (path, G_FILE_TEST_IS_DIR) &&
01052               !g_list_find_custom (dirs, path, (GCompareFunc) g_strcmp0))
01053             {
01054               dirs = g_list_prepend (dirs, path);
01055             }
01056           else
01057             {
01058               g_free (path);
01059             }
01060         }
01061 
01062       if (data_dirs)
01063         g_strfreev (data_dirs);
01064     }
01065 
01066   return dirs;
01067 }
01068 
01069 static GList *
01070 get_desktop_file_directories (BamfMatcher *self)
01071 {
01072   GList *dirs = NULL;
01073   char *path;
01074 
01075   dirs = get_desktop_file_env_directories(dirs, "XDG_DATA_DIRS");
01076 
01077   if (!g_list_find_custom (dirs, "/usr/share/applications", (GCompareFunc) g_strcmp0))
01078     dirs = g_list_prepend (dirs, g_strdup ("/usr/share/applications"));
01079   
01080   if (!g_list_find_custom (dirs, "/usr/local/share/applications", (GCompareFunc) g_strcmp0))
01081     dirs = g_list_prepend (dirs, g_strdup ("/usr/local/share/applications"));
01082   
01083   dirs = get_desktop_file_env_directories(dirs, "XDG_DATA_HOME");
01084 
01085   //If this doesn't exist, we need to track .local or the home itself!
01086   path = g_build_filename (g_get_home_dir (), ".local/share/applications", NULL);
01087 
01088   if (!g_list_find_custom (dirs, path, (GCompareFunc) g_strcmp0))
01089     dirs = g_list_prepend (dirs, path);
01090   else
01091     g_free (path);
01092 
01093   /* include subdirs */
01094   dirs = get_directory_tree_list (dirs);
01095 
01096   /* Include also the user desktop folder, but without its subfolders */
01097   dirs = g_list_prepend (dirs, g_strdup (g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP)));
01098 
01099   return dirs;
01100 }
01101 
01102 static gint
01103 compare_sub_values (gconstpointer desktop_path, gconstpointer desktop_file)
01104 {
01105   return !g_str_has_prefix (desktop_file, desktop_path);
01106 }
01107 
01108 static void
01109 hash_table_remove_sub_values (GHashTable *htable, GCompareFunc compare_func,
01110                               GFreeFunc free_func, gpointer target, gboolean search_all)
01111 {
01112   g_return_if_fail (htable);
01113   g_return_if_fail (compare_func);
01114 
01115   GHashTableIter iter;
01116   gpointer key;
01117   gpointer value;
01118 
01119   g_hash_table_iter_init (&iter, htable);
01120 
01121   while (g_hash_table_iter_next (&iter, &key, &value))
01122     {
01123       GList *list, *l;
01124       gboolean found;
01125 
01126       list = value;
01127       found = FALSE;
01128 
01129       l = list;
01130       while (l)
01131         {
01132           GList *next = l->next;
01133 
01134           if (compare_func (target, l->data) == 0)
01135             {
01136               found = TRUE;
01137 
01138               if (!l->prev && !l->next)
01139               {
01140                 if (free_func)
01141                   g_list_free_full (list, free_func);
01142                 else
01143                   g_list_free (list);
01144 
01145                 g_hash_table_iter_remove (&iter);
01146 
01147                 next = NULL;
01148                 break;
01149               }
01150             else
01151               {
01152                 if (free_func)
01153                   free_func (l->data);
01154 
01155                 /* If the target is the first element of the list (and thanks to
01156 -                * the previous check we're also sure that it's not the only one),
01157                  * simply switch it with its follower, not to change the first
01158                  * pointer and the hash table value for key
01159                  */
01160                 if (l == list)
01161                   {
01162                     l->data = next->data;
01163                     l = next;
01164                     next = list;
01165                   }
01166 
01167                 list = g_list_delete_link (list, l);
01168               }
01169 
01170               if (!search_all)
01171                 break;
01172             }
01173             l = next;
01174         }
01175 
01176       if (found && !search_all)
01177          break;
01178     }
01179 }
01180 
01181 static gboolean
01182 hash_table_compare_sub_values (gpointer desktop_path, gpointer desktop_class, gpointer target_path)
01183 {
01184   return !compare_sub_values (target_path, desktop_path);
01185 }
01186 
01187 static void fill_desktop_file_table (BamfMatcher *, GList *, GHashTable *, GHashTable *, GHashTable *);
01188 
01189 static void
01190 on_monitor_changed (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent type, BamfMatcher *self)
01191 {
01192   char *path;
01193   const char *monitored_dir;
01194   GFileType filetype;
01195 
01196   g_return_if_fail (G_IS_FILE_MONITOR (monitor));
01197   g_return_if_fail (G_IS_FILE (file));
01198   g_return_if_fail (BAMF_IS_MATCHER (self));
01199 
01200   if (type != G_FILE_MONITOR_EVENT_CREATED &&
01201       type != G_FILE_MONITOR_EVENT_DELETED &&
01202       type != G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
01203     return;
01204 
01205   path = g_file_get_path (file);
01206   filetype = g_file_query_file_type (file, G_FILE_QUERY_INFO_NONE, NULL);
01207   monitored_dir = g_object_get_data (G_OBJECT (monitor), "root");
01208 
01209   if (!g_str_has_suffix (path, ".desktop") &&
01210       filetype != G_FILE_TYPE_DIRECTORY &&
01211       type != G_FILE_MONITOR_EVENT_DELETED)
01212     {
01213       g_free(path);
01214       return;
01215     }
01216 
01217   if (type == G_FILE_MONITOR_EVENT_DELETED ||
01218       type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
01219     {
01220       if (g_str_has_suffix (path, ".desktop"))
01221         {
01222           /* Remove all the .desktop file referencies from the hash tables.
01223            * Free the string itself only on the 2nd pass (tables share the same
01224            * string instance)
01225            */
01226           hash_table_remove_sub_values (self->priv->desktop_id_table,
01227                                        (GCompareFunc) g_strcmp0, NULL, path, FALSE);
01228           hash_table_remove_sub_values (self->priv->desktop_file_table,
01229                                        (GCompareFunc) g_strcmp0, g_free, path, FALSE);
01230           g_hash_table_remove (self->priv->desktop_class_table, path);
01231         }
01232       else if (g_strcmp0 (monitored_dir, path) == 0)
01233         {
01234           /* Remove all the referencies to the .desktop files placed in subfolders
01235            * of the current path. Free the strings itself only on the 2nd pass
01236            * (as before, the tables share the same string instance)
01237            */
01238           char *prefix = g_strconcat (path, G_DIR_SEPARATOR_S, NULL);
01239 
01240           hash_table_remove_sub_values (self->priv->desktop_id_table,
01241                                         compare_sub_values, NULL, prefix, TRUE);
01242           hash_table_remove_sub_values (self->priv->desktop_file_table,
01243                                         compare_sub_values, g_free, prefix, TRUE);
01244           g_hash_table_foreach_remove (self->priv->desktop_class_table,
01245                                        hash_table_compare_sub_values, prefix);
01246 
01247           g_signal_handlers_disconnect_by_func (monitor, on_monitor_changed, self);
01248           self->priv->monitors = g_list_remove (self->priv->monitors, monitor);
01249           g_object_unref (monitor);
01250           g_free (prefix);
01251         }
01252     }
01253 
01254   if (type == G_FILE_MONITOR_EVENT_CREATED ||
01255       type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
01256     {
01257       if (filetype == G_FILE_TYPE_DIRECTORY)
01258         {
01259           const char *desktop_dir;
01260           desktop_dir = g_get_user_special_dir (G_USER_DIRECTORY_DESKTOP);
01261 
01262           if (g_strcmp0 (monitored_dir, desktop_dir) != 0)
01263             {
01264               GList *dirs = NULL;
01265               dirs = g_list_prepend (dirs, g_strdup (path));
01266               dirs = get_directory_tree_list (dirs);
01267               fill_desktop_file_table (self, dirs,
01268                                        self->priv->desktop_file_table,
01269                                        self->priv->desktop_id_table,
01270                                        self->priv->desktop_class_table);
01271       
01272               g_list_free_full (dirs, g_free);
01273             }
01274         }
01275       else if (filetype != G_FILE_TYPE_UNKNOWN)
01276         {
01277           bamf_matcher_load_desktop_file (self, path);
01278         }
01279     }
01280 
01281   g_free (path);
01282 }
01283 
01284 static void
01285 bamf_add_new_monitored_directory (BamfMatcher * self, const gchar *directory)
01286 {
01287   g_return_if_fail (BAMF_IS_MATCHER (self));
01288 
01289   GFile *file;
01290   GFileMonitor *monitor;
01291 
01292   file = g_file_new_for_path (directory);
01293   monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
01294   g_file_monitor_set_rate_limit (monitor, 1000);
01295   g_object_set_data_full (G_OBJECT (monitor), "root", g_strdup (directory), g_free);
01296   g_signal_connect (monitor, "changed", (GCallback) on_monitor_changed, self);
01297   self->priv->monitors = g_list_prepend (self->priv->monitors, monitor);
01298 
01299   g_object_unref (file);
01300 }
01301 
01302 static void
01303 fill_desktop_file_table (BamfMatcher * self,
01304                          GList *directories,
01305                          GHashTable *desktop_file_table,
01306                          GHashTable *desktop_id_table,
01307                          GHashTable *desktop_class_table)
01308 {
01309   g_return_if_fail (BAMF_IS_MATCHER (self));
01310 
01311   GList *l;
01312   char *directory;
01313   char *bamf_file;
01314   
01315   for (l = directories; l; l = l->next)
01316     {
01317       directory = l->data;
01318 
01319       if (!g_file_test (directory, G_FILE_TEST_IS_DIR))
01320         continue;
01321 
01322       bamf_add_new_monitored_directory (self, directory);
01323 
01324       bamf_file = g_build_filename (directory, "bamf.index", NULL);
01325 
01326       if (g_file_test (bamf_file, G_FILE_TEST_EXISTS))
01327         {
01328           load_index_file_to_table (self, bamf_file, desktop_file_table,
01329                                     desktop_id_table, desktop_class_table);
01330         }
01331       else
01332         {
01333           load_directory_to_table (self, directory, desktop_file_table,
01334                                    desktop_id_table, desktop_class_table);
01335         }
01336 
01337       g_free (bamf_file);
01338     }
01339 }
01340 
01341 static void
01342 create_desktop_file_table (BamfMatcher * self,
01343                            GHashTable **desktop_file_table,
01344                            GHashTable **desktop_id_table,
01345                            GHashTable **desktop_class_table)
01346 {
01347   g_return_if_fail (BAMF_IS_MATCHER (self));
01348 
01349   GList *directories;
01350 
01351   *desktop_file_table =
01352     g_hash_table_new_full ((GHashFunc) g_str_hash,
01353                            (GEqualFunc) g_str_equal,
01354                            (GDestroyNotify) g_free,
01355                            NULL);
01356 
01357   *desktop_id_table =
01358     g_hash_table_new_full ((GHashFunc) g_str_hash,
01359                            (GEqualFunc) g_str_equal,
01360                            (GDestroyNotify) g_free,
01361                            NULL);
01362 
01363   *desktop_class_table =
01364     g_hash_table_new_full ((GHashFunc) g_str_hash,
01365                            (GEqualFunc) g_str_equal,
01366                            (GDestroyNotify) g_free,
01367                            (GDestroyNotify) g_free);  
01368 
01369   directories = get_desktop_file_directories (self);
01370 
01371   fill_desktop_file_table (self, directories, *desktop_file_table,
01372                            *desktop_id_table, *desktop_class_table);
01373   
01374   g_list_free_full (directories, g_free);
01375 }
01376 
01377 static gboolean
01378 is_open_office_window (BamfMatcher * self, BamfLegacyWindow * window)
01379 {
01380   const char *class_name = bamf_legacy_window_get_class_name (window);
01381 
01382   if (!class_name)
01383     return FALSE;
01384 
01385   return (g_str_has_prefix (class_name, "LibreOffice") ||
01386           g_str_has_prefix (class_name, "libreoffice") ||
01387           g_str_has_prefix (class_name, "OpenOffice") ||
01388           g_str_has_prefix (class_name, "openoffice"));
01389 }
01390 
01391 static char *
01392 get_window_hint (BamfLegacyWindow *window, const char *atom_name)
01393 {
01394   g_return_val_if_fail (BAMF_IS_LEGACY_WINDOW (window), NULL);
01395 
01396   Window xid = bamf_legacy_window_get_xid (window);
01397   return bamf_xutils_get_window_hint (xid, atom_name, XA_STRING);
01398 }
01399 
01400 static void
01401 set_window_hint (BamfLegacyWindow * window, const char *atom_name, const char *data)
01402 {
01403   g_return_if_fail (BAMF_LEGACY_WINDOW (window));
01404 
01405   Window xid = bamf_legacy_window_get_xid (window);
01406   bamf_xutils_set_window_hint (xid, atom_name, XA_STRING, data);
01407 }
01408 
01409 static char *
01410 process_exec_string (gint pid)
01411 {
01412   gchar *result = NULL;
01413   gint i = 0;
01414   gchar **argv = NULL;
01415   GString *exec = NULL;
01416   glibtop_proc_args buffer;
01417 
01418   if (pid == 0)
01419     return NULL;
01420 
01421   argv = glibtop_get_proc_argv (&buffer, pid, 0);
01422   exec = g_string_new ("");
01423 
01424   while (argv[i] != NULL)
01425     {
01426       g_string_append (exec, argv[i]);
01427       if (argv[i + 1] != NULL)
01428         g_string_append (exec, " ");
01429       g_free (argv[i]);
01430       i++;
01431     }
01432 
01433   g_free (argv);
01434 
01435   result = g_strdup (exec->str);
01436   g_string_free (exec, TRUE);
01437   return result;
01438 }
01439 
01440 
01441 static char *
01442 process_name (gint pid)
01443 {
01444   char *stat_path;
01445   char *contents;
01446   char **lines;
01447   char **sections;
01448   char *result = NULL;
01449   
01450   if (pid <= 0)
01451     return NULL;
01452   
01453   stat_path = g_strdup_printf ("/proc/%i/status", pid);
01454   
01455   if (g_file_get_contents (stat_path, &contents, NULL, NULL))
01456     { 
01457       lines = g_strsplit (contents, "\n", 2);
01458       
01459       if (lines && g_strv_length (lines) > 0)
01460         {
01461           sections = g_strsplit (lines[0], "\t", 0);
01462           if (sections && g_strv_length (sections) > 1)
01463             {
01464               result = g_strdup (sections[1]);
01465               g_strfreev (sections);
01466             }
01467           g_strfreev (lines);
01468         }
01469       g_free (contents);
01470     }  
01471   g_free (stat_path);
01472   
01473   return result;
01474 }
01475 
01476 static GList *
01477 bamf_matcher_possible_applications_for_pid (BamfMatcher *self,
01478                                            gint pid)
01479 {
01480   BamfMatcherPrivate *priv;
01481   GList *result = NULL, *table_list, *l;
01482   char *proc_name;
01483   char *exec_string;
01484   char *trimmed;
01485   
01486   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
01487   
01488   priv = self->priv;
01489   
01490   exec_string  = process_exec_string (pid);
01491   
01492   if (exec_string)
01493     {
01494       trimmed = trim_exec_string (self, exec_string);
01495       
01496       if (trimmed)
01497         {
01498           if (trimmed[0] != '\0')
01499             {
01500               table_list = g_hash_table_lookup (priv->desktop_file_table, trimmed);
01501               
01502               for (l = table_list; l; l = l->next)
01503                 {
01504                   result = g_list_prepend (result, g_strdup (l->data));
01505                 }
01506             }
01507           g_free (trimmed);
01508         }
01509       
01510       g_free (exec_string);
01511     }
01512 
01513   if (result)
01514     {
01515       result = g_list_reverse (result);
01516       return result;
01517     }
01518     
01519   proc_name = process_name (pid);
01520   if (proc_name)
01521     {
01522       table_list = g_hash_table_lookup (priv->desktop_file_table, proc_name);
01523               
01524       for (l = table_list; l; l = l->next)
01525         {
01526           result = g_list_prepend (result, g_strdup (l->data)); 
01527         }
01528       g_free (proc_name);
01529     }
01530   
01531   result = g_list_reverse (result);
01532   return result;
01533 }
01534 
01535 static gboolean
01536 is_web_app_window (BamfMatcher *self, BamfLegacyWindow *window)
01537 {
01538   const char *window_class = bamf_legacy_window_get_class_name (window);
01539   const char *instance_name = bamf_legacy_window_get_class_instance_name (window);
01540 
01541   // Chrome/Chromium uses url wm_class strings to represent its web apps.
01542   // These apps will still have the same parent pid and hints as the main chrome
01543   // window, so we skip the hint check.
01544   // We can tell a window is a chrome web app window if its instance name is
01545   // not google-chrome but its window class is Google-chrome
01546   // We can tell a window is chromium web app window if its instance name is
01547   // not chromium-browser but its window class is Chromium Browser
01548 
01549   gboolean valid_app = FALSE;
01550 
01551   if (instance_name && window_class)
01552     {
01553       if (g_strcmp0 (window_class, "Google-chrome") == 0 &&
01554           g_strcmp0 (instance_name, "google-chrome") != 0 &&
01555           !g_str_has_prefix (instance_name, "Google-chrome"))
01556         {
01557           valid_app = TRUE;
01558         }
01559       else if (g_strcmp0 (window_class, "Chromium-browser") == 0 &&
01560                g_strcmp0 (instance_name, "chromium-browser") != 0 &&
01561                !g_str_has_prefix (instance_name, "Chromium-browser"))
01562         {
01563           valid_app = TRUE;
01564         }
01565     }
01566 
01567   return valid_app;
01568 }
01569 
01570 static gboolean
01571 bamf_matcher_window_skips_hint_set (BamfMatcher *self, BamfLegacyWindow *window)
01572 {
01573   gboolean skip_hint_set = FALSE;
01574   g_return_val_if_fail (BAMF_IS_MATCHER (self), TRUE);
01575 
01576   skip_hint_set = is_web_app_window (self, window);
01577 
01578   return skip_hint_set;
01579 }
01580 
01581 static GList *
01582 bamf_matcher_get_class_matching_desktop_files (BamfMatcher *self, const gchar *class_name)
01583 {
01584   GList* desktop_files = NULL;
01585   gpointer key;
01586   gpointer value;
01587   GHashTableIter iter;
01588 
01589   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
01590   g_hash_table_iter_init (&iter, self->priv->desktop_class_table);
01591 
01592   while (g_hash_table_iter_next (&iter, &key, &value))
01593     {
01594       gchar* desktop_file = g_strdup (key);
01595       gchar* desktop_class = value;
01596 
01597       if (g_strcmp0 (desktop_class, class_name) == 0)
01598         {
01599           desktop_files = g_list_prepend (desktop_files, desktop_file);
01600         }
01601     }
01602 
01603   return desktop_files;
01604 }
01605 
01606 static gboolean
01607 bamf_matcher_has_instance_class_desktop_file (BamfMatcher *self, const gchar *class_name)
01608 {
01609   gpointer key;
01610   gpointer value;
01611   GHashTableIter iter;
01612 
01613   g_return_val_if_fail (BAMF_IS_MATCHER (self), FALSE);
01614   g_hash_table_iter_init (&iter, self->priv->desktop_class_table);
01615 
01616   while (g_hash_table_iter_next (&iter, &key, &value))
01617     {
01618       gchar* desktop_class = value;
01619 
01620       if (g_strcmp0 (desktop_class, class_name) == 0)
01621         {
01622           return TRUE;
01623         }
01624     }
01625 
01626   return FALSE;
01627 }
01628 
01629 static GList *
01630 bamf_matcher_possible_applications_for_window (BamfMatcher *self,
01631                                                BamfWindow *bamf_window,
01632                                                const char **target_class_out)
01633 {
01634   BamfMatcherPrivate *priv;
01635   BamfLegacyWindow *window;
01636   GList *desktop_files = NULL, *l;
01637   char *desktop_file = NULL;
01638   const char *desktop_class = NULL;
01639   const char *class_name = NULL;
01640   const char *instance_name = NULL;
01641   const char *target_class = NULL;
01642   gboolean filter_by_wmclass = FALSE;
01643 
01644   g_return_val_if_fail (BAMF_IS_WINDOW (bamf_window), NULL);
01645   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
01646 
01647   priv = self->priv;
01648   window = bamf_window_get_window (bamf_window);
01649   desktop_file = get_window_hint (window, _NET_WM_DESKTOP_FILE);
01650   class_name = bamf_legacy_window_get_class_name (window);
01651   instance_name = bamf_legacy_window_get_class_instance_name (window);
01652 
01653   target_class = instance_name;
01654   filter_by_wmclass = bamf_matcher_has_instance_class_desktop_file (self, target_class);
01655 
01656   if (!filter_by_wmclass)
01657   {
01658     if (is_web_app_window (self, window))
01659       {
01660         // This ensures that a new application is created even for unknown webapps
01661         filter_by_wmclass = TRUE;
01662       }
01663     else
01664       {
01665         target_class = class_name;
01666         filter_by_wmclass = bamf_matcher_has_instance_class_desktop_file (self, target_class);
01667       }
01668   }
01669 
01670   if (desktop_file)
01671     {
01672       desktop_class = bamf_matcher_get_desktop_file_class (self, desktop_file);
01673 
01674       if ((!filter_by_wmclass && !desktop_class) || g_strcmp0 (desktop_class, target_class) == 0)
01675         {
01676           desktop_files = g_list_prepend (desktop_files, desktop_file);
01677         }
01678       else
01679         {
01680           g_free (desktop_file);
01681         }
01682     }
01683 
01684   desktop_file = NULL;
01685 
01686   if (!desktop_files)
01687     {
01688       if (class_name)
01689         {
01690           char *window_class_down = g_ascii_strdown (class_name, -1);
01691           l = g_hash_table_lookup (priv->desktop_id_table, window_class_down);
01692           g_free (window_class_down);
01693 
01694           for (; l; l = l->next)
01695             {
01696               desktop_file = l->data;
01697 
01698               if (desktop_file)
01699                 {
01700                   desktop_class = bamf_matcher_get_desktop_file_class (self, desktop_file);
01701 
01702                   if ((!filter_by_wmclass && !desktop_class) || g_strcmp0 (desktop_class, target_class) == 0)
01703                     {
01704                       if (!g_list_find_custom (desktop_files, desktop_file,
01705                                                (GCompareFunc) g_strcmp0))
01706                         {
01707                           desktop_files = g_list_prepend (desktop_files, g_strdup (desktop_file));
01708                         }
01709                     }
01710                 }
01711             }
01712 
01713           desktop_files = g_list_reverse (desktop_files);
01714         }
01715 
01716       gint pid = bamf_legacy_window_get_pid (window);
01717       GList *pid_list = bamf_matcher_possible_applications_for_pid (self, pid);
01718       
01719       /* Append these files to the end to give preference to class_name style picking.
01720          This style of matching is prefered and used by GNOME Shell however does not work
01721          very well in practice, thus requiring the fallback here */
01722       for (l = pid_list; l; l = l->next)
01723         {
01724           desktop_file = l->data;
01725           if (g_list_find_custom (desktop_files, desktop_file, (GCompareFunc) g_strcmp0))
01726             {
01727               g_free (desktop_file);
01728             }
01729           else
01730             {
01731               gboolean append = FALSE;
01732 
01733               if (target_class)
01734                 {
01735                   desktop_class = bamf_matcher_get_desktop_file_class (self, desktop_file);
01736                   if ((!filter_by_wmclass && !desktop_class) || g_strcmp0 (desktop_class, target_class) == 0)
01737                     {
01738                       append = TRUE;
01739                     }
01740                 }
01741               else
01742                 {
01743                   append = TRUE;
01744                 }
01745 
01746               if (append)
01747                 {
01748                   /* If we're adding a .desktop file stored in the desktop folder,
01749                      give it the priority it should have. */
01750                   GList *last = NULL;
01751 
01752                   if (is_desktop_folder_item (desktop_file, -1))
01753                     {
01754                       GList *ll;
01755 
01756                       for (ll = desktop_files; ll; ll = ll->next)
01757                         {
01758                           if (!is_desktop_folder_item (ll->data, -1))
01759                             {
01760                               last = ll;
01761                               break;
01762                             }
01763                         }
01764                     }
01765 
01766                   desktop_files = g_list_insert_before (desktop_files, last, desktop_file);
01767                 }
01768               else
01769                 {
01770                   g_free (desktop_file);
01771                 }
01772             }
01773         }
01774 
01775       g_list_free (pid_list);
01776     }
01777 
01778   if (!desktop_files && filter_by_wmclass)
01779     {
01780       desktop_files = bamf_matcher_get_class_matching_desktop_files (self, target_class);
01781     }
01782 
01783   if (target_class_out)
01784     {
01785       *target_class_out = target_class;
01786     }
01787 
01788   return desktop_files;
01789 }
01790 
01791 static BamfApplication *
01792 bamf_matcher_get_application_for_window (BamfMatcher *self,
01793                                          BamfWindow *bamf_window,
01794                                          gboolean *new_application)
01795 {
01796   GList *possible_apps, *l;
01797   BamfLegacyWindow *window;
01798   const gchar *win_class_name;
01799   const gchar *target_class = NULL;
01800   const gchar *app_class = NULL;
01801   const gchar *app_desktop = NULL;
01802   BamfApplication *app = NULL, *best = NULL;
01803 
01804   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
01805   g_return_val_if_fail (BAMF_IS_WINDOW (bamf_window), NULL);
01806 
01807   window = bamf_window_get_window (bamf_window);
01808   win_class_name = bamf_legacy_window_get_class_name (window);
01809 
01810   possible_apps = bamf_matcher_possible_applications_for_window (self, bamf_window, &target_class);
01811   app_class = target_class;
01812 
01813   /* Loop over every possible desktop file that could match the window, and try
01814    * to reuse an already-opened window that uses it.
01815    * Desktop files are ordered by priority, so we try to use the first possible,
01816    * wm_class matching applications have the priority, btw. */
01817   if (possible_apps)
01818     {
01819       /* primary matching */
01820       for (l = possible_apps; l; l = l->next)
01821         {
01822           const gchar *desktop_file = l->data;
01823           app = bamf_matcher_get_application_by_desktop_file (self, desktop_file);
01824 
01825           if (BAMF_IS_APPLICATION (app))
01826             {
01827               const gchar *app_desktop_class;
01828               app_desktop_class = bamf_application_get_wmclass (app);
01829 
01830               if (target_class && app_desktop_class && strcasecmp (target_class, app_desktop_class) == 0)
01831                 {
01832                   best = app;
01833                   break;
01834                 }
01835               else if (!best)
01836                 {
01837                   best = app;
01838                 }
01839             }
01840         }
01841 
01842       /* If a "best" application has been found, we should check again if the
01843        * desktop file that is going to be used is really the best one we have.
01844        * To do this, we compare the window class name with the desktop class
01845        * of both candidates to ensure that really is the best one.
01846        * This is important to avoid that very-similar (which differ only by
01847        * StartupWMClass) running desktop files, would be wrongly used to match
01848        * an incompatible window. */
01849       if (BAMF_IS_APPLICATION (best) && possible_apps)
01850         {
01851           const gchar *best_app_desktop = bamf_application_get_desktop_file (best);
01852           const gchar *best_desktop = possible_apps->data;
01853 
01854           if (win_class_name && g_strcmp0 (best_app_desktop, best_desktop) != 0)
01855             {
01856               const gchar *best_app_class;
01857               const gchar *best_desktop_class;
01858 
01859               best_app_class = bamf_application_get_wmclass (best);
01860               best_desktop_class = bamf_matcher_get_desktop_file_class (self, best_desktop);
01861 
01862               /* We compare the two classes using their "distance" from the
01863                * desidered class value */
01864               if (best_app_class && best_desktop_class)
01865                 {
01866                   int max_chars = strlen (win_class_name);
01867                   int app_diff = strncasecmp (win_class_name, best_app_class, max_chars);
01868                   int desktop_diff = strncasecmp (win_class_name, best_desktop_class, max_chars);
01869 
01870                   if (abs (desktop_diff) < abs (app_diff))
01871                     {
01872                       best = bamf_matcher_get_application_by_desktop_file (self, best_desktop);
01873                       app_desktop = best_desktop;
01874                     }
01875                 }
01876             }
01877         }
01878     }
01879   else
01880     {
01881       /* secondary matching */
01882       GList *a;
01883       BamfView *view;
01884       const gchar *app_desktop_class;
01885 
01886       for (a = self->priv->views; a; a = a->next)
01887         {
01888           view = a->data;
01889 
01890           if (!BAMF_IS_APPLICATION (view))
01891             continue;
01892 
01893           app = BAMF_APPLICATION (view);
01894           app_desktop_class = bamf_application_get_wmclass (app);
01895 
01896           if (bamf_application_contains_similar_to_window (app, bamf_window))
01897             {
01898               if (target_class && g_strcmp0 (target_class, app_desktop_class) == 0)
01899                 {
01900                   best = app;
01901                   break;
01902                 }
01903               else if (!best)
01904                 {
01905                   best = app;
01906                 }
01907             }
01908         }
01909     }
01910 
01911   if (!best)
01912     {
01913       if (app_desktop)
01914         {
01915           best = bamf_application_new_from_desktop_file (app_desktop);
01916         }
01917       else if (possible_apps)
01918         {
01919           best = bamf_application_new_from_desktop_files (possible_apps);
01920         }
01921       else
01922         {
01923           best = bamf_application_new ();
01924         }
01925 
01926       bamf_application_set_wmclass (best, app_class);
01927 
01928       if (new_application)
01929         *new_application = TRUE;
01930     }
01931   else
01932     {
01933       if (new_application)
01934         *new_application = FALSE;
01935     }
01936 
01937   g_list_free_full (possible_apps, g_free);
01938 
01939   return best;
01940 }
01941 
01942 /* Ensures that the window hint is set if a registered pid matches, and that set window hints
01943    are already known to bamfdaemon */
01944 static void
01945 ensure_window_hint_set (BamfMatcher *self,
01946                         BamfLegacyWindow *window)
01947 {
01948   GArray *pids;
01949   GHashTable *registered_pids;
01950   char *desktop_file_hint = NULL;
01951   gint i, pid;
01952   gpointer key;
01953 
01954   g_return_if_fail (BAMF_IS_MATCHER (self));
01955   g_return_if_fail (BAMF_IS_LEGACY_WINDOW (window));
01956 
01957   registered_pids = self->priv->registered_pids;
01958 
01959   /* Some windows such as web applications shares the pid with their parent
01960    * browser so, we have to ignore them */
01961   if (bamf_matcher_window_skips_hint_set (self, window))
01962     {
01963       return;
01964     }
01965 
01966   desktop_file_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
01967 
01968   if (desktop_file_hint)
01969     {
01970       /* already set, make sure we know about this
01971        * fact for future windows of this applications */
01972       pid = bamf_legacy_window_get_pid (window);
01973 
01974       if (pid > 0)
01975         {
01976           key = GINT_TO_POINTER (pid);
01977 
01978           if (!g_hash_table_lookup (registered_pids, key))
01979             {
01980               g_hash_table_insert (registered_pids, key, g_strdup (desktop_file_hint));
01981             }
01982         }
01983 
01984       g_free (desktop_file_hint);
01985       return;
01986     }
01987 
01988   pids = pid_parent_tree (self, bamf_legacy_window_get_pid (window));
01989 
01990   for (i = 0; i < pids->len; i++)
01991     {
01992       pid = g_array_index (pids, gint, i);
01993       key = GINT_TO_POINTER (pid);
01994 
01995       desktop_file_hint = g_hash_table_lookup (registered_pids, key);
01996       if (desktop_file_hint != NULL && desktop_file_hint[0] != '\0')
01997         break;
01998     }
01999 
02000   g_array_free (pids, TRUE);
02001 
02002   if (desktop_file_hint)
02003     set_window_hint (window, _NET_WM_DESKTOP_FILE, desktop_file_hint);
02004 }
02005 
02006 static void
02007 handle_raw_window (BamfMatcher *self, BamfLegacyWindow *window)
02008 {
02009   BamfWindow *bamfwindow;
02010   BamfApplication *bamfapplication;
02011 
02012   g_return_if_fail (BAMF_IS_MATCHER (self));
02013   g_return_if_fail (BAMF_IS_LEGACY_WINDOW (window));
02014 
02015   gint pid = bamf_legacy_window_get_pid (window);
02016   if (pid > 1)
02017     g_array_append_val (self->priv->known_pids, pid);
02018 
02019   ensure_window_hint_set (self, window);
02020 
02021   /* We need to make our objects for bus export, the quickest way to do this, though not
02022    * always the best is to simply go window by window creating new applications as needed.
02023    */
02024 
02025   bamfwindow = bamf_window_new (window);
02026   bamf_matcher_register_view_stealing_ref (self, BAMF_VIEW (bamfwindow));
02027 
02028   gboolean new_app = FALSE;
02029   bamfapplication = bamf_matcher_get_application_for_window (self, bamfwindow, &new_app);
02030 
02031   if (new_app)
02032     {
02033       bamf_matcher_register_view_stealing_ref (self, BAMF_VIEW (bamfapplication));
02034     }
02035 
02036   bamf_view_add_child (BAMF_VIEW (bamfapplication), BAMF_VIEW (bamfwindow));
02037 }
02038 
02039 static void
02040 on_open_office_window_name_changed (BamfLegacyWindow *window, BamfMatcher* self)
02041 {
02042   g_return_if_fail (BAMF_IS_MATCHER (self));
02043   g_return_if_fail (BAMF_IS_LEGACY_WINDOW (window));
02044 
02045   char *old_hint;
02046   const char *new_hint;
02047 
02048   old_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
02049   new_hint = get_open_office_window_hint (self, window);
02050 
02051   if (new_hint && g_strcmp0 (new_hint, old_hint) != 0)
02052     {
02053       bamf_legacy_window_reopen (window);
02054     }
02055 
02056   g_free (old_hint);
02057 }
02058 
02059 static void
02060 on_open_office_window_closed (BamfLegacyWindow *window, BamfMatcher* self)
02061 {
02062   g_signal_handlers_disconnect_by_func (window, on_open_office_window_name_changed, self);
02063   g_object_unref (window);
02064 }
02065 
02066 static char *
02067 get_gnome_control_center_window_hint (BamfMatcher * self, BamfLegacyWindow * window)
02068 {
02069   gchar *role;
02070   gchar *exec;
02071   GHashTable *desktopFileTable;
02072   GList *list;
02073 
02074   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
02075   g_return_val_if_fail (BAMF_IS_LEGACY_WINDOW (window), NULL);
02076 
02077   role = get_window_hint (window, WM_WINDOW_ROLE);
02078   exec = g_strconcat ("gnome-control-center", (role ? " " : NULL), role, NULL);
02079 
02080   desktopFileTable = self->priv->desktop_file_table;
02081   list = g_hash_table_lookup (desktopFileTable, exec);
02082 
02083   if (!list && g_strcmp0 ("gnome-control-center", exec) != 0)
02084     {
02085       list = g_hash_table_lookup (desktopFileTable, "gnome-control-center");
02086     }
02087 
02088   g_free (exec);
02089   g_free (role);
02090 
02091   return (list ? (char *) list->data : NULL);
02092 }
02093 
02094 static void
02095 on_gnome_control_center_window_name_changed (BamfLegacyWindow *window, BamfMatcher* self)
02096 {
02097   g_return_if_fail (BAMF_IS_MATCHER (self));
02098   g_return_if_fail (BAMF_IS_LEGACY_WINDOW (window));
02099 
02100   char *old_hint;
02101   const char *new_hint;
02102 
02103   old_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
02104   new_hint = get_gnome_control_center_window_hint (self, window);
02105 
02106   if (new_hint && g_strcmp0 (new_hint, old_hint) != 0)
02107     {
02108       bamf_legacy_window_reopen (window);
02109     }
02110 
02111   g_free (old_hint);
02112 }
02113 
02114 static void
02115 on_gnome_control_center_window_closed (BamfLegacyWindow *window, BamfMatcher* self)
02116 {
02117   g_signal_handlers_disconnect_by_func (window, on_gnome_control_center_window_name_changed, self);
02118   g_object_unref (window);
02119 }
02120 
02121 static void
02122 handle_window_opened (BamfLegacyScreen * screen, BamfLegacyWindow * window, BamfMatcher *self)
02123 {
02124   g_return_if_fail (BAMF_IS_MATCHER (self));
02125   g_return_if_fail (BAMF_IS_LEGACY_WINDOW (window));
02126 
02127   if (bamf_legacy_window_get_window_type (window) == BAMF_WINDOW_DESKTOP)
02128     {
02129       BamfWindow *bamfwindow = bamf_window_new (window);
02130       bamf_matcher_register_view_stealing_ref (self, BAMF_VIEW (bamfwindow));
02131 
02132       return;
02133     }
02134 
02135   if (is_open_office_window (self, window))
02136     {
02137       BamfWindowType win_type = bamf_legacy_window_get_window_type (window);
02138 
02139       if (win_type == BAMF_WINDOW_SPLASHSCREEN || win_type == BAMF_WINDOW_TOOLBAR)
02140         {
02141           return;
02142         }
02143 
02144       char *old_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
02145       const char *new_hint = get_open_office_window_hint (self, window);
02146 
02147       if (new_hint && g_strcmp0 (old_hint, new_hint) != 0)
02148         {
02149           set_window_hint (window, _NET_WM_DESKTOP_FILE, new_hint);
02150         }
02151 
02152       g_object_ref (window);
02153       g_signal_connect (window, "name-changed", (GCallback) on_open_office_window_name_changed, self);
02154       g_signal_connect (window, "closed", (GCallback) on_open_office_window_closed, self);
02155 
02156       g_free (old_hint);
02157     }
02158   else if (g_strcmp0 (bamf_legacy_window_get_class_name (window), "Gnome-control-center") == 0)
02159     {
02160       char *old_hint = get_window_hint (window, _NET_WM_DESKTOP_FILE);
02161       const char *new_hint = get_gnome_control_center_window_hint (self, window);
02162 
02163       if (new_hint && g_strcmp0 (old_hint, new_hint) != 0)
02164         {
02165           set_window_hint (window, _NET_WM_DESKTOP_FILE, new_hint);
02166         }
02167 
02168       g_object_ref (window);
02169       g_signal_connect (window, "name-changed", (GCallback) on_gnome_control_center_window_name_changed, self);
02170       g_signal_connect (window, "closed", (GCallback) on_gnome_control_center_window_closed, self);
02171 
02172       g_free (old_hint);
02173     }
02174 
02175   /* we have a window who is ready to be matched */
02176   handle_raw_window (self, window);
02177 }
02178 
02179 static void
02180 handle_stacking_changed (BamfLegacyScreen * screen, BamfMatcher *self)
02181 {
02182   g_signal_emit_by_name (self, "stacking-order-changed");
02183 }
02184 
02185 static void
02186 bamf_matcher_setup_indicator_state (BamfMatcher *self, BamfIndicator *indicator)
02187 {
02188   GList *possible_apps, *l;
02189   const char *desktop_file;
02190   BamfApplication *app = NULL, *best = NULL;
02191 
02192   g_return_if_fail (BAMF_IS_MATCHER (self));
02193   g_return_if_fail (BAMF_IS_INDICATOR (indicator));
02194 
02195   possible_apps = bamf_matcher_possible_applications_for_pid (self, bamf_indicator_get_pid (indicator));
02196 
02197   /* Loop over every application, inside that application see if its .desktop file
02198    * matches with any of our possible hits. If so we match it. If we have no possible hits
02199    * fall back to secondary matching. 
02200    */
02201   if (possible_apps)
02202     {
02203       for (l = possible_apps; l; l = l->next)
02204         {
02205           desktop_file = l->data;
02206           app = bamf_matcher_get_application_by_desktop_file (self, desktop_file);
02207 
02208           if (BAMF_IS_APPLICATION (app))
02209             {
02210               best = app;
02211               break;
02212             }
02213         }
02214     }
02215 
02216   if (!best)
02217     {
02218       desktop_file = NULL;
02219 
02220       if (possible_apps)
02221         desktop_file = possible_apps->data;
02222 
02223       if (desktop_file)
02224         {
02225           best = bamf_application_new_from_desktop_file (desktop_file);
02226           bamf_matcher_register_view_stealing_ref (self, BAMF_VIEW (best));
02227         }
02228     }
02229 
02230   g_list_free_full (possible_apps, g_free);
02231 
02232   if (best)
02233     bamf_view_add_child (BAMF_VIEW (best), BAMF_VIEW (indicator));
02234 }
02235 
02236 static void
02237 handle_indicator_opened (BamfIndicatorSource *approver, BamfIndicator *indicator, BamfMatcher *self)
02238 {
02239   g_return_if_fail (BAMF_IS_MATCHER (self));
02240   g_return_if_fail (BAMF_IS_INDICATOR (indicator));
02241   
02242   bamf_matcher_register_view_stealing_ref (self, BAMF_VIEW (indicator));
02243   bamf_matcher_setup_indicator_state (self, indicator);
02244 }
02245 
02246 void
02247 bamf_matcher_load_desktop_file (BamfMatcher * self,
02248                                 const char * desktop_file)
02249 {
02250   GList *vl, *wl;
02251 
02252   g_return_if_fail (BAMF_IS_MATCHER (self));
02253 
02254   load_desktop_file_to_table (self,
02255                               desktop_file,
02256                               self->priv->desktop_file_table,
02257                               self->priv->desktop_id_table,
02258                               self->priv->desktop_class_table);
02259 
02260   /* If an application with no .desktop file has windows that matches
02261    * the new added .desktop file, then we try to re-match them. */
02262   for (vl = self->priv->views; vl; vl = vl->next)
02263     {
02264       if (!BAMF_IS_APPLICATION (vl->data))
02265         continue;
02266 
02267       BamfApplication *app = BAMF_APPLICATION (vl->data);
02268 
02269       if (!bamf_application_get_desktop_file (app))
02270         {
02271           GList *children = bamf_view_get_children (BAMF_VIEW (app));
02272 
02273           for (wl = children; wl; wl = wl->next)
02274           {
02275             if (!BAMF_IS_WINDOW (wl->data))
02276               continue;
02277 
02278             BamfWindow *win = BAMF_WINDOW (wl->data);
02279             GList *desktops = bamf_matcher_possible_applications_for_window (self, win, NULL);
02280 
02281             if (g_list_find_custom (desktops, desktop_file, (GCompareFunc) g_strcmp0))
02282               {
02283                 BamfLegacyWindow *legacy_window = bamf_window_get_window (win);
02284                 bamf_legacy_window_reopen (legacy_window);
02285               }
02286           }
02287         }
02288     }
02289 }
02290 
02291 void
02292 bamf_matcher_register_desktop_file_for_pid (BamfMatcher * self,
02293                                             const char * desktopFile,
02294                                             gint pid)
02295 {
02296   gpointer key;
02297   BamfLegacyScreen *screen;
02298   GList *glist_item;
02299   GList *windows;
02300 
02301   g_return_if_fail (BAMF_IS_MATCHER (self));
02302   g_return_if_fail (desktopFile);
02303 
02304   key = GINT_TO_POINTER (pid);
02305   g_hash_table_insert (self->priv->registered_pids, key, g_strdup (desktopFile));
02306 
02307   /* fixme, this is a bit heavy */
02308 
02309   screen = bamf_legacy_screen_get_default ();
02310 
02311   g_return_if_fail (BAMF_IS_LEGACY_SCREEN (screen));
02312 
02313   windows = bamf_legacy_screen_get_windows (screen);
02314 
02315   for (glist_item = windows; glist_item != NULL;
02316        glist_item = glist_item->next)
02317     {
02318       ensure_window_hint_set (self, glist_item->data);
02319     }
02320 }
02321 
02322 const char *
02323 bamf_matcher_get_desktop_file_class (BamfMatcher * self, const char * desktop_file)
02324 {
02325   g_return_val_if_fail (BAMF_IS_MATCHER (self), NULL);
02326   g_return_val_if_fail (desktop_file, NULL);
02327 
02328   return g_hash_table_lookup (self->priv->desktop_class_table, desktop_file);
02329 }
02330 
02331 static int
02332 x_error_handler (Display *display, XErrorEvent *event)
02333 {
02334   /* We hardly care, this means one of our get or set property calls didn't work
02335    * while this kind of sucks, it wont effect the running of the program except to
02336    * perhaps reduce the efficiency of the application
02337    */
02338   return 0;
02339 }
02340 
02341 const char *
02342 bamf_matcher_get_active_application (BamfMatcher *matcher)
02343 {
02344   GList *l;
02345   BamfView *view;
02346   BamfMatcherPrivate *priv;
02347 
02348   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02349 
02350   priv = matcher->priv;
02351 
02352   for (l = priv->views; l; l = l->next)
02353     {
02354       view = l->data;
02355 
02356       if (!BAMF_IS_APPLICATION (view))
02357         continue;
02358 
02359       if (bamf_view_is_active (view))
02360         {
02361           return bamf_view_get_path (view);
02362         }
02363     }
02364 
02365   return "";
02366 }
02367 
02368 const char *
02369 bamf_matcher_get_active_window (BamfMatcher *matcher)
02370 {
02371   GList *l;
02372   BamfView *view;
02373   BamfMatcherPrivate *priv;
02374 
02375   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02376 
02377   priv = matcher->priv;
02378 
02379   for (l = priv->views; l; l = l->next)
02380     {
02381       view = l->data;
02382 
02383       if (!BAMF_IS_WINDOW (view))
02384         continue;
02385 
02386       if (bamf_view_is_active (view))
02387         {
02388           return bamf_view_get_path (view);
02389         }
02390     }
02391 
02392   return "";
02393 }
02394 
02395 const char *
02396 bamf_matcher_application_for_xid (BamfMatcher *matcher,
02397                                   guint32 xid)
02398 {
02399   GList *l;
02400   BamfView *view;
02401   BamfMatcherPrivate *priv;
02402 
02403   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02404 
02405   priv = matcher->priv;
02406 
02407   for (l = priv->views; l; l = l->next)
02408     {
02409       view = l->data;
02410 
02411       if (!BAMF_IS_APPLICATION (view))
02412         continue;
02413 
02414       if (bamf_application_manages_xid (BAMF_APPLICATION (view), xid))
02415         {
02416           return bamf_view_get_path (view);
02417         }
02418     }
02419 
02420   return "";
02421 }
02422 
02423 static gint
02424 compare_windows_by_stack_order (gconstpointer a, gconstpointer b)
02425 {
02426   g_return_val_if_fail (BAMF_IS_WINDOW (a), -1);
02427   g_return_val_if_fail (BAMF_IS_WINDOW (b), 1);
02428 
02429   gint idx_a = bamf_window_get_stack_position (BAMF_WINDOW (a));
02430   gint idx_b = bamf_window_get_stack_position (BAMF_WINDOW (b));
02431 
02432   return (idx_a < idx_b) ? -1 : 1;
02433 }
02434 
02435 GVariant *
02436 bamf_matcher_get_window_stack_for_monitor (BamfMatcher *matcher, gint monitor)
02437 {
02438   GList *l;
02439   GList *windows;
02440   BamfView *view;
02441   GVariantBuilder b;
02442 
02443   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02444 
02445   windows = NULL;
02446   for (l = matcher->priv->views; l; l = l->next)
02447     {
02448       if (BAMF_IS_WINDOW (l->data))
02449         {
02450           windows = g_list_insert_sorted (windows, l->data,
02451                                           compare_windows_by_stack_order);
02452         }
02453     }
02454 
02455   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
02456   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
02457 
02458   for (l = windows; l; l = l->next)
02459     {
02460       view = l->data;
02461 
02462       if (!BAMF_IS_WINDOW (view))
02463         continue;
02464 
02465       if ((monitor >= 0 && bamf_window_get_monitor (BAMF_WINDOW (view)) == monitor) ||
02466           monitor < 0)
02467       {
02468         g_variant_builder_add (&b, "s", bamf_view_get_path (view));
02469       }
02470     }
02471 
02472   g_list_free (windows);
02473   g_variant_builder_close (&b);
02474 
02475   return g_variant_builder_end (&b);
02476 }
02477 
02478 gboolean
02479 bamf_matcher_application_is_running (BamfMatcher *matcher,
02480                                      const char *application)
02481 {
02482   BamfApplication *app;
02483 
02484   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), FALSE);
02485 
02486   app = bamf_matcher_get_application_by_desktop_file (matcher, application);
02487 
02488   if (BAMF_IS_APPLICATION (app))
02489     {
02490       return bamf_view_is_running (BAMF_VIEW (app));
02491     }
02492 
02493   return FALSE;
02494 }
02495 
02496 GVariant *
02497 bamf_matcher_window_dbus_paths (BamfMatcher *matcher)
02498 {
02499   GList *l;
02500   BamfView *view;
02501   BamfMatcherPrivate *priv;
02502   GVariantBuilder b;
02503 
02504   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02505 
02506   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
02507   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
02508 
02509   priv = matcher->priv;
02510 
02511   for (l = priv->views; l; l = l->next)
02512     {
02513       view = l->data;
02514 
02515       if (!BAMF_IS_WINDOW (view))
02516         continue;
02517 
02518       g_variant_builder_add (&b, "s", bamf_view_get_path (view));
02519     }
02520 
02521   g_variant_builder_close (&b);
02522 
02523   return g_variant_builder_end (&b);
02524 }
02525 
02526 GVariant *
02527 bamf_matcher_application_dbus_paths (BamfMatcher *matcher)
02528 {
02529   GList *l;
02530   BamfView *view;
02531   BamfMatcherPrivate *priv;
02532   GVariantBuilder b;
02533 
02534   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02535 
02536   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
02537   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
02538 
02539   priv = matcher->priv;
02540 
02541   for (l = priv->views; l; l = l->next)
02542     {
02543       view = l->data;
02544 
02545       if (!BAMF_IS_APPLICATION (view))
02546         continue;
02547 
02548       g_variant_builder_add (&b, "s", bamf_view_get_path (view));
02549     }
02550 
02551   g_variant_builder_close (&b);
02552 
02553   return g_variant_builder_end (&b);
02554 }
02555 
02556 const char *
02557 bamf_matcher_dbus_path_for_application (BamfMatcher *matcher,
02558                                         const char *application)
02559 {
02560   const char * path = "";
02561   BamfApplication *app;
02562 
02563   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02564 
02565   app = bamf_matcher_get_application_by_desktop_file (matcher, application);
02566 
02567   if (BAMF_IS_APPLICATION (app))
02568     {
02569       return bamf_view_get_path (BAMF_VIEW (app));
02570     }
02571 
02572   return path;
02573 }
02574 
02575 GList *
02576 bamf_matcher_get_favorites (BamfMatcher *matcher)
02577 {
02578   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02579   
02580   return matcher->priv->favorites;
02581 }
02582 
02583 void
02584 bamf_matcher_register_favorites (BamfMatcher *matcher,
02585                                  const char **favorites)
02586 {
02587   const char *fav;
02588   const char **favs;
02589   BamfMatcherPrivate *priv;
02590 
02591   g_return_if_fail (BAMF_IS_MATCHER (matcher));
02592   g_return_if_fail (favorites);
02593   priv = matcher->priv;
02594   
02595   for (favs = favorites; *favs; favs++)
02596     {
02597       fav = *favs;
02598       /* ignore things already in the list */
02599       if (g_list_find_custom (priv->favorites, fav, (GCompareFunc) g_strcmp0))
02600         continue;
02601 
02602       bamf_matcher_load_desktop_file (matcher, fav);
02603       priv->favorites = g_list_prepend (priv->favorites, g_strdup (fav));
02604     }
02605 
02606   g_signal_emit (matcher, matcher_signals[FAVORITES_CHANGED], 0);
02607 }
02608 
02609 GVariant *
02610 bamf_matcher_running_application_paths (BamfMatcher *matcher)
02611 {
02612   GList *l;
02613   BamfView *view;
02614   BamfMatcherPrivate *priv;
02615   GVariantBuilder b;
02616 
02617   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02618 
02619   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
02620   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
02621 
02622   priv = matcher->priv;
02623 
02624   for (l = priv->views; l; l = l->next)
02625     {
02626       view = l->data;
02627 
02628       if (!BAMF_IS_APPLICATION (view) || !bamf_view_is_running (view))
02629         continue;
02630 
02631       g_variant_builder_add (&b, "s", bamf_view_get_path (view));
02632     }
02633 
02634   g_variant_builder_close (&b);
02635 
02636   return g_variant_builder_end (&b);
02637 }
02638 
02639 GVariant *
02640 bamf_matcher_running_applications_desktop_files (BamfMatcher *matcher)
02641 {
02642   GList *l;
02643   BamfView *view;
02644   BamfMatcherPrivate *priv;
02645   GSequence *paths;
02646   GSequenceIter *iter;
02647   const char *desktop_file;
02648   GVariantBuilder b;
02649 
02650   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02651 
02652   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
02653   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
02654 
02655   paths = g_sequence_new (NULL);
02656   
02657   priv = matcher->priv;
02658 
02659   for (l = priv->views; l; l = l->next)
02660     {
02661       view = l->data;
02662 
02663       if (!BAMF_IS_APPLICATION (view) || !bamf_view_is_running (view))
02664         continue;
02665 
02666       desktop_file = bamf_application_get_desktop_file (BAMF_APPLICATION (view));
02667       if (!desktop_file) continue;
02668       
02669       if (g_sequence_lookup (paths, (gpointer) desktop_file,
02670                              (GCompareDataFunc) g_strcmp0, NULL) == NULL)
02671         {
02672           g_sequence_insert_sorted (paths, (gpointer) desktop_file, 
02673                                     (GCompareDataFunc) g_strcmp0, NULL);
02674         }
02675     }
02676 
02677   iter = g_sequence_get_begin_iter (paths);
02678   while (!g_sequence_iter_is_end (iter))
02679     {
02680       g_variant_builder_add (&b, "s", g_sequence_get (iter));
02681 
02682       iter = g_sequence_iter_next (iter);
02683     }
02684 
02685   g_sequence_free (paths);
02686 
02687   g_variant_builder_close (&b);
02688 
02689   return g_variant_builder_end (&b);
02690 }
02691 
02692 GVariant *
02693 bamf_matcher_tab_dbus_paths (BamfMatcher *matcher)
02694 {
02695   GVariantBuilder b;
02696 
02697   g_variant_builder_init (&b, G_VARIANT_TYPE ("(as)"));
02698   g_variant_builder_open (&b, G_VARIANT_TYPE ("as"));
02699   g_variant_builder_close (&b);
02700   return g_variant_builder_end (&b);
02701 }
02702 
02703 GVariant *
02704 bamf_matcher_xids_for_application (BamfMatcher *matcher,
02705                                    const char *application)
02706 {
02707   GVariantBuilder b;
02708   GVariant *xids;
02709   BamfApplication *app;
02710 
02711   g_return_val_if_fail (BAMF_IS_MATCHER (matcher), NULL);
02712 
02713   xids = NULL;
02714   app = bamf_matcher_get_application_by_desktop_file (matcher, application);
02715 
02716   if (BAMF_IS_APPLICATION (app))
02717     {
02718       xids = bamf_application_get_xids (app);
02719     }
02720 
02721   if (!xids)
02722     {
02723       g_variant_builder_init (&b, G_VARIANT_TYPE ("(au)"));
02724       g_variant_builder_open (&b, G_VARIANT_TYPE ("au"));
02725       g_variant_builder_close (&b);
02726       xids = g_variant_builder_end (&b);
02727     }
02728 
02729   return xids;
02730 }
02731 
02732 static gboolean
02733 on_dbus_handle_xids_for_application (BamfDBusMatcher *interface,
02734                                      GDBusMethodInvocation *invocation,
02735                                      const gchar *application,
02736                                      BamfMatcher *self)
02737 {
02738   GVariant *xids = bamf_matcher_xids_for_application (self, application);
02739   g_dbus_method_invocation_return_value (invocation, xids);
02740 
02741   return TRUE;
02742 }
02743 
02744 static gboolean
02745 on_dbus_handle_tab_paths (BamfDBusMatcher *interface,
02746                           GDBusMethodInvocation *invocation,
02747                           BamfMatcher *self)
02748 {
02749   GVariant *tab_paths = bamf_matcher_tab_dbus_paths (self);
02750   g_dbus_method_invocation_return_value (invocation, tab_paths);
02751 
02752   return TRUE;
02753 }
02754 
02755 static gboolean
02756 on_dbus_handle_application_paths (BamfDBusMatcher *interface,
02757                                   GDBusMethodInvocation *invocation,
02758                                   BamfMatcher *self)
02759 {
02760   GVariant *app_paths = bamf_matcher_application_dbus_paths (self);
02761   g_dbus_method_invocation_return_value (invocation, app_paths);
02762 
02763   return TRUE;
02764 }
02765 
02766 
02767 static gboolean
02768 on_dbus_handle_window_paths (BamfDBusMatcher *interface,
02769                              GDBusMethodInvocation *invocation,
02770                              BamfMatcher *self)
02771 {
02772   GVariant *win_paths = bamf_matcher_window_dbus_paths (self);
02773   g_dbus_method_invocation_return_value (invocation, win_paths);
02774 
02775   return TRUE;
02776 }
02777 
02778 static gboolean
02779 on_dbus_handle_running_applications (BamfDBusMatcher *interface,
02780                                      GDBusMethodInvocation *invocation,
02781                                      BamfMatcher *self)
02782 {
02783   GVariant *running_apps = bamf_matcher_running_application_paths (self);
02784   g_dbus_method_invocation_return_value (invocation, running_apps);
02785 
02786   return TRUE;
02787 }
02788 
02789 static gboolean
02790 on_dbus_handle_running_applications_desktop_files (BamfDBusMatcher *interface,
02791                                                    GDBusMethodInvocation *invocation,
02792                                                    BamfMatcher *self)
02793 {
02794   GVariant *paths = bamf_matcher_running_applications_desktop_files (self);
02795   g_dbus_method_invocation_return_value (invocation, paths);
02796 
02797   return TRUE;
02798 }
02799 
02800 static gboolean
02801 on_dbus_handle_active_application (BamfDBusMatcher *interface,
02802                                    GDBusMethodInvocation *invocation,
02803                                    BamfMatcher *self)
02804 {
02805   const gchar *active_app = bamf_matcher_get_active_application (self);
02806 
02807   g_dbus_method_invocation_return_value (invocation,
02808                                          g_variant_new ("(s)", active_app));
02809   return TRUE;
02810 }
02811 
02812 static gboolean
02813 on_dbus_handle_active_window (BamfDBusMatcher *interface,
02814                               GDBusMethodInvocation *invocation,
02815                               BamfMatcher *self)
02816 {
02817   const gchar *active_win = bamf_matcher_get_active_window (self);
02818 
02819   g_dbus_method_invocation_return_value (invocation,
02820                                          g_variant_new ("(s)", active_win));
02821   return TRUE;
02822 }
02823 
02824 static gboolean
02825 on_dbus_handle_application_is_running (BamfDBusMatcher *interface,
02826                                        GDBusMethodInvocation *invocation,
02827                                        const gchar *application,
02828                                        BamfMatcher *self)
02829 {
02830   gboolean is_running = bamf_matcher_application_is_running (self, application);
02831 
02832   g_dbus_method_invocation_return_value (invocation,
02833                                          g_variant_new ("(b)", is_running));
02834   return TRUE;
02835 }
02836 
02837 static gboolean
02838 on_dbus_handle_register_favorites (BamfDBusMatcher *interface,
02839                                    GDBusMethodInvocation *invocation,
02840                                    const char **favorites,
02841                                    BamfMatcher *self)
02842 {
02843   bamf_matcher_register_favorites (self, favorites);
02844   g_dbus_method_invocation_return_value (invocation, NULL);
02845 
02846   return TRUE;
02847 }
02848 
02849 static gboolean
02850 on_dbus_handle_path_for_application (BamfDBusMatcher *interface,
02851                                      GDBusMethodInvocation *invocation,
02852                                      const gchar *application,
02853                                      BamfMatcher *self)
02854 {
02855   const gchar *app_path = bamf_matcher_dbus_path_for_application (self, application);
02856 
02857   g_dbus_method_invocation_return_value (invocation,
02858                                          g_variant_new ("(s)", app_path));
02859 
02860   return TRUE;
02861 }
02862 
02863 static gboolean
02864 on_dbus_handle_application_for_xid (BamfDBusMatcher *interface,
02865                                     GDBusMethodInvocation *invocation,
02866                                     guint xid,
02867                                     BamfMatcher *self)
02868 {
02869   const gchar *app_path = bamf_matcher_application_for_xid (self, xid);
02870 
02871   g_dbus_method_invocation_return_value (invocation,
02872                                          g_variant_new ("(s)", app_path));
02873 
02874   return TRUE;
02875 }
02876 
02877 static gboolean
02878 on_dbus_handle_window_stack_for_monitor (BamfDBusMatcher *interface,
02879                                          GDBusMethodInvocation *invocation,
02880                                          gint monitor,
02881                                          BamfMatcher *self)
02882 {
02883   GVariant *windows = bamf_matcher_get_window_stack_for_monitor (self, monitor);
02884 
02885   g_dbus_method_invocation_return_value (invocation, windows);
02886 
02887   return TRUE;
02888 }
02889 
02890 static void
02891 bamf_matcher_init (BamfMatcher * self)
02892 {
02893   BamfMatcherPrivate *priv;
02894   BamfLegacyScreen *screen;
02895   BamfIndicatorSource *approver;
02896   GArray *prefixstrings;
02897   int i;
02898 
02899   priv = self->priv = BAMF_MATCHER_GET_PRIVATE (self);
02900 
02901   priv->known_pids = g_array_new (FALSE, TRUE, sizeof (gint));
02902   priv->bad_prefixes = g_array_new (FALSE, TRUE, sizeof (GRegex *));
02903   priv->good_prefixes = g_array_new (FALSE, TRUE, sizeof (GRegex *));
02904   priv->registered_pids = g_hash_table_new_full (g_direct_hash, g_direct_equal,
02905                                                  NULL, g_free);
02906 
02907   prefixstrings = bad_prefix_strings ();
02908   for (i = 0; i < prefixstrings->len; i++)
02909     {
02910       char *str = g_array_index (prefixstrings, char *, i);
02911       GRegex *regex = g_regex_new (str, G_REGEX_OPTIMIZE, 0, NULL);
02912       g_array_append_val (priv->bad_prefixes, regex);
02913     }
02914   g_array_free (prefixstrings, TRUE);
02915 
02916   prefixstrings = good_prefix_strings ();
02917   for (i = 0; i < prefixstrings->len; i++)
02918     {
02919       char *str = g_array_index (prefixstrings, char *, i);
02920       GRegex *regex = g_regex_new (str, G_REGEX_OPTIMIZE, 0, NULL);
02921       g_array_append_val (priv->good_prefixes, regex);
02922     }
02923   g_array_free (prefixstrings, TRUE);
02924 
02925   create_desktop_file_table (self, &(priv->desktop_file_table),
02926                              &(priv->desktop_id_table),
02927                              &(priv->desktop_class_table));
02928 
02929   screen = bamf_legacy_screen_get_default ();
02930   g_signal_connect (G_OBJECT (screen), BAMF_LEGACY_SCREEN_SIGNAL_WINDOW_OPENED,
02931                     (GCallback) handle_window_opened, self);
02932 
02933   g_signal_connect (G_OBJECT (screen), BAMF_LEGACY_SCREEN_SIGNAL_STACKING_CHANGED,
02934                     (GCallback) handle_stacking_changed, self);
02935 
02936   approver = bamf_indicator_source_get_default ();
02937   g_signal_connect (G_OBJECT (approver), "indicator-opened",
02938                     (GCallback) handle_indicator_opened, self);
02939 
02940   XSetErrorHandler (x_error_handler);
02941 
02942   /* Registering signal callbacks to reply to dbus method calls */
02943   g_signal_connect (self, "handle-xids-for-application",
02944                     G_CALLBACK (on_dbus_handle_xids_for_application), self);
02945 
02946   g_signal_connect (self, "handle-tab-paths",
02947                     G_CALLBACK (on_dbus_handle_tab_paths), self);
02948 
02949   g_signal_connect (self, "handle-application-paths",
02950                     G_CALLBACK (on_dbus_handle_application_paths), self);
02951 
02952   g_signal_connect (self, "handle-window-paths",
02953                     G_CALLBACK (on_dbus_handle_window_paths), self);
02954 
02955   g_signal_connect (self, "handle-running-applications",
02956                     G_CALLBACK (on_dbus_handle_running_applications), self);
02957 
02958   g_signal_connect (self, "handle-running-applications-desktop-files",
02959                     G_CALLBACK (on_dbus_handle_running_applications_desktop_files), self);
02960   
02961   g_signal_connect (self, "handle-active-window",
02962                     G_CALLBACK (on_dbus_handle_active_window), self);
02963 
02964   g_signal_connect (self, "handle-active-application",
02965                     G_CALLBACK (on_dbus_handle_active_application), self);
02966 
02967   g_signal_connect (self, "handle-application-is-running",
02968                     G_CALLBACK (on_dbus_handle_application_is_running), self);
02969 
02970   g_signal_connect (self, "handle-register-favorites",
02971                     G_CALLBACK (on_dbus_handle_register_favorites), self);
02972 
02973   g_signal_connect (self, "handle-path-for-application",
02974                     G_CALLBACK (on_dbus_handle_path_for_application), self);
02975 
02976   g_signal_connect (self, "handle-application-for-xid",
02977                     G_CALLBACK (on_dbus_handle_application_for_xid), self);
02978 
02979   g_signal_connect (self, "handle-window-stack-for-monitor",
02980                     G_CALLBACK (on_dbus_handle_window_stack_for_monitor), self);
02981 }
02982 
02983 static void
02984 bamf_matcher_dispose (GObject *object)
02985 {
02986   BamfMatcher *self = (BamfMatcher *) object;
02987   BamfMatcherPrivate *priv = self->priv;
02988 
02989   while (priv->views)
02990     {
02991       bamf_matcher_unregister_view (self, priv->views->data);
02992     }
02993 
02994   G_OBJECT_CLASS (bamf_matcher_parent_class)->dispose (object);
02995 }
02996 
02997 static void
02998 bamf_matcher_finalize (GObject *object)
02999 {
03000   BamfMatcher *self = (BamfMatcher *) object;
03001   BamfMatcherPrivate *priv = self->priv;
03002   BamfLegacyScreen *screen = bamf_legacy_screen_get_default ();
03003   GList *l;
03004   int i;
03005 
03006   for (i = 0; i < priv->bad_prefixes->len; i++)
03007     {
03008       GRegex *regex = g_array_index (priv->bad_prefixes, GRegex *, i);
03009       g_regex_unref (regex);
03010     }
03011 
03012   for (i = 0; i < priv->good_prefixes->len; i++)
03013     {
03014       GRegex *regex = g_array_index (priv->good_prefixes, GRegex *, i);
03015       g_regex_unref (regex);
03016     }
03017 
03018   g_array_free (priv->bad_prefixes, TRUE);
03019   g_array_free (priv->good_prefixes, TRUE);
03020   g_array_free (priv->known_pids, TRUE);
03021   g_hash_table_destroy (priv->desktop_id_table);
03022   g_hash_table_destroy (priv->desktop_file_table);
03023   g_hash_table_destroy (priv->desktop_class_table);
03024   g_hash_table_destroy (priv->registered_pids);
03025 
03026   if (priv->opened_closed_paths_table)
03027     {
03028       g_hash_table_destroy (priv->opened_closed_paths_table);
03029     }
03030 
03031   if (priv->dispatch_changes_id != 0)
03032     {
03033       g_source_remove (priv->dispatch_changes_id);
03034       priv->dispatch_changes_id = 0;
03035     }
03036 
03037   g_list_free_full (priv->views, g_object_unref);
03038 
03039   g_signal_handlers_disconnect_by_func (screen, handle_window_opened, self);
03040   g_signal_handlers_disconnect_by_func (screen, handle_stacking_changed, self);
03041 
03042   for (l = priv->monitors; l; l = l->next)
03043     {
03044       g_signal_handlers_disconnect_by_func (G_OBJECT (l->data), 
03045                                             (GCallback) on_monitor_changed, self);
03046     }
03047   g_list_free_full (priv->monitors, g_object_unref);
03048   priv->monitors = NULL;
03049 
03050   g_list_free_full (priv->favorites, g_free);
03051   priv->favorites = NULL;
03052 
03053   priv->active_app = NULL;
03054   priv->active_win = NULL;
03055 
03056   static_matcher = NULL;
03057 
03058   G_OBJECT_CLASS (bamf_matcher_parent_class)->finalize (object);
03059 }
03060 
03061 static void
03062 bamf_matcher_class_init (BamfMatcherClass * klass)
03063 {
03064   GObjectClass *object_class = G_OBJECT_CLASS (klass);
03065 
03066   g_type_class_add_private (klass, sizeof (BamfMatcherPrivate));
03067   object_class->dispose = bamf_matcher_dispose;
03068   object_class->finalize = bamf_matcher_finalize;
03069 
03070   matcher_signals [FAVORITES_CHANGED] =
03071     g_signal_new ("favorites-changed",
03072                   G_OBJECT_CLASS_TYPE (klass),
03073                   0,
03074                   0, NULL, NULL,
03075                   g_cclosure_marshal_VOID__VOID,
03076                   G_TYPE_NONE, 0);
03077 }
03078 
03079 BamfMatcher *
03080 bamf_matcher_get_default (void)
03081 {
03082   if (!BAMF_IS_MATCHER (static_matcher))
03083     {
03084       static_matcher = g_object_new (BAMF_TYPE_MATCHER, NULL);
03085     }
03086 
03087   return static_matcher;
03088 }