~kroq-gar78/ubuntu/precise/gnome-control-center/fix-885947

« back to all changes in this revision

Viewing changes to libslab/app-shell.c

  • Committer: Bazaar Package Importer
  • Author(s): Rodrigo Moya
  • Date: 2011-05-17 10:47:27 UTC
  • mfrom: (0.1.11 experimental) (1.1.45 upstream)
  • Revision ID: james.westby@ubuntu.com-20110517104727-lqel6m8vhfw5jby1
Tags: 1:3.0.1.1-1ubuntu1
* Rebase on Debian, remaining Ubuntu changes:
* debian/control:
  - Build-Depend on hardening-wrapper, dpkg-dev and dh-autoreconf
  - Add dependency on ubuntu-system-service
  - Remove dependency on gnome-icon-theme-symbolic
  - Move dependency on apg, gnome-icon-theme-symbolic and accountsservice to
    be a Recommends: until we get them in main
* debian/rules:
  - Use autoreconf
  - Add binary-post-install rule for gnome-control-center-data
  - Run dh-autoreconf
* debian/gnome-control-center.dirs:
* debian/gnome-control-center.links:
  - Add a link to the control center shell for indicators
* debian/patches/00_disable-nm.patch:
  - Temporary patch to disable building with NetworkManager until we get
    the new one in the archive
* debian/patches/01_git_remove_gettext_calls.patch:
  - Remove calls to AM_GNU_GETTEXT, IT_PROG_INTLTOOL should be enough
* debian/patches/01_git_kill_warning.patch:
  - Kill warning
* debian/patches/50_ubuntu_systemwide_prefs.patch:
  - Ubuntu specific proxy preferences
* debian/patches/51_ubuntu_system_keyboard.patch:
  - Implement the global keyboard spec at https://wiki.ubuntu.com/DefaultKeyboardSettings

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * This file is part of libslab.
3
 
 *
4
 
 * Copyright (c) 2006 Novell, Inc.
5
 
 *
6
 
 * Libslab is free software; you can redistribute it and/or modify it under the
7
 
 * terms of the GNU Lesser General Public License as published by the Free
8
 
 * Software Foundation; either version 2 of the License, or (at your option)
9
 
 * any later version.
10
 
 *
11
 
 * Libslab is distributed in the hope that it will be useful, but WITHOUT ANY
12
 
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
14
 
 * more details.
15
 
 *
16
 
 * You should have received a copy of the GNU Lesser General Public License
17
 
 * along with libslab; if not, write to the Free Software Foundation, Inc., 51
18
 
 * Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
 
 */
20
 
 
21
 
#ifdef HAVE_CONFIG_H
22
 
#include <config.h>
23
 
#endif
24
 
 
25
 
#include <libgnome/gnome-desktop-item.h>
26
 
#include <gio/gio.h>
27
 
#include <gdk/gdkkeysyms.h>
28
 
#include <sys/types.h>
29
 
#include <sys/stat.h>
30
 
#include <string.h>
31
 
#include <stdlib.h>
32
 
 
33
 
#include <glib/gi18n-lib.h>
34
 
 
35
 
#include "app-shell.h"
36
 
#include "shell-window.h"
37
 
#include "app-resizer.h"
38
 
#include "slab-section.h"
39
 
#include "slab-gnome-util.h"
40
 
#include "search-bar.h"
41
 
 
42
 
#include "application-tile.h"
43
 
#include "themed-icon.h"
44
 
 
45
 
#define TILE_EXEC_NAME "Tile_desktop_exec_name"
46
 
#define SECONDS_IN_DAY 86400
47
 
#define EXIT_SHELL_ON_ACTION_START "exit_shell_on_action_start"
48
 
#define EXIT_SHELL_ON_ACTION_HELP "exit_shell_on_action_help"
49
 
#define EXIT_SHELL_ON_ACTION_ADD_REMOVE "exit_shell_on_action_add_remove"
50
 
#define EXIT_SHELL_ON_ACTION_UPGRADE_UNINSTALL "exit_shell_on_action_upgrade_uninstall"
51
 
#define NEW_APPS_FILE_KEY "new_apps_file_key"
52
 
 
53
 
static void create_application_category_sections (AppShellData * app_data);
54
 
static GtkWidget *create_filter_section (AppShellData * app_data, const gchar * title);
55
 
static GtkWidget *create_groups_section (AppShellData * app_data, const gchar * title);
56
 
static GtkWidget *create_actions_section (AppShellData * app_data, const gchar * title,
57
 
        void (*actions_handler) (Tile *, TileEvent *, gpointer));
58
 
 
59
 
static void generate_category (const char * category, GMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive);
60
 
static void generate_launchers (GMenuTreeDirectory * root_dir, AppShellData * app_data,
61
 
        CategoryData * cat_data, gboolean recursive);
62
 
static void generate_new_apps (AppShellData * app_data);
63
 
static void insert_launcher_into_category (CategoryData * cat_data, GnomeDesktopItem * desktop_item,
64
 
        AppShellData * app_data);
65
 
 
66
 
static gboolean main_keypress_callback (GtkWidget * widget, GdkEventKey * event,
67
 
        AppShellData * app_data);
68
 
static gboolean main_delete_callback (GtkWidget * widget, GdkEvent * event,
69
 
        AppShellData * app_data);
70
 
static void application_launcher_clear_search_bar (AppShellData * app_data);
71
 
static void launch_selected_app (AppShellData * app_data);
72
 
static void generate_potential_apps (gpointer catdata, gpointer user_data);
73
 
 
74
 
static void relayout_shell (AppShellData * app_data);
75
 
static gboolean handle_filter_changed (NldSearchBar * search_bar, int context, const char *text,
76
 
        gpointer user_data);
77
 
static void handle_group_clicked (Tile * tile, TileEvent * event, gpointer user_data);
78
 
static void set_state (AppShellData * app_data, GtkWidget * widget);
79
 
static void populate_groups_section (AppShellData * app_data);
80
 
static void generate_filtered_lists (gpointer catdata, gpointer user_data);
81
 
static void show_no_results_message (AppShellData * app_data, GtkWidget * containing_vbox);
82
 
static void populate_application_category_sections (AppShellData * app_data,
83
 
        GtkWidget * containing_vbox);
84
 
static void populate_application_category_section (AppShellData * app_data, SlabSection * section,
85
 
        GList * launcher_list);
86
 
static void tile_activated_cb (Tile * tile, TileEvent * event, gpointer user_data);
87
 
static void handle_launcher_single_clicked (Tile * launcher, gpointer data);
88
 
static void handle_menu_action_performed (Tile * launcher, TileEvent * event, TileAction * action,
89
 
        gpointer data);
90
 
static gint application_launcher_compare (gconstpointer a, gconstpointer b);
91
 
static void gmenu_tree_changed_callback (GMenuTree * tree, gpointer user_data);
92
 
gboolean regenerate_categories (AppShellData * app_data);
93
 
 
94
 
void
95
 
hide_shell (AppShellData * app_data)
96
 
{
97
 
        gtk_window_get_position (GTK_WINDOW (app_data->main_app),
98
 
                &app_data->main_app_window_x, &app_data->main_app_window_y);
99
 
        /* printf("x:%d, y:%d\n", app_data->main_app_window_x, app_data->main_app_window_y); */
100
 
        /* clear the search bar now so reshowing is fast and flicker free - BNC#283186 */
101
 
        application_launcher_clear_search_bar (app_data);
102
 
        gtk_widget_hide (app_data->main_app);
103
 
}
104
 
 
105
 
void
106
 
show_shell (AppShellData * app_data)
107
 
{
108
 
        gtk_widget_show_all (app_data->main_app);
109
 
        if (!app_data->static_actions)
110
 
                gtk_widget_hide_all (app_data->actions_section);  /* don't show unless a launcher is selected */
111
 
 
112
 
        if (app_data->main_app_window_shown_once)
113
 
                gtk_window_move (GTK_WINDOW (app_data->main_app),
114
 
                        app_data->main_app_window_x, app_data->main_app_window_y);
115
 
 
116
 
        /* if this is the first time shown, need to clear this handler */
117
 
        else
118
 
                shell_window_clear_resize_handler (SHELL_WINDOW (app_data->shell));
119
 
        app_data->main_app_window_shown_once = TRUE;
120
 
}
121
 
 
122
 
gboolean
123
 
create_main_window (AppShellData * app_data, const gchar * app_name, const gchar * title,
124
 
        const gchar * window_icon, gint width, gint height, gboolean hidden)
125
 
{
126
 
        GtkWidget *main_app = gtk_window_new (GTK_WINDOW_TOPLEVEL);
127
 
        app_data->main_app = main_app;
128
 
        gtk_widget_set_name (main_app, app_name);
129
 
        gtk_window_set_title (GTK_WINDOW (main_app), title);
130
 
        /* gtk_window_set_default_size(GTK_WINDOW(main_app), width, height); */
131
 
        gtk_window_set_icon_name (GTK_WINDOW (main_app), window_icon);
132
 
        gtk_container_add (GTK_CONTAINER (main_app), app_data->shell);
133
 
 
134
 
        g_signal_connect (main_app, "delete-event", G_CALLBACK (main_delete_callback), app_data);
135
 
        g_signal_connect (main_app, "key-press-event", G_CALLBACK (main_keypress_callback),
136
 
                app_data);
137
 
 
138
 
        gtk_window_set_position (GTK_WINDOW (app_data->main_app), GTK_WIN_POS_CENTER);
139
 
        if (!hidden)
140
 
                show_shell (app_data);
141
 
 
142
 
        return TRUE;
143
 
}
144
 
 
145
 
static void
146
 
generate_potential_apps (gpointer catdata, gpointer user_data)
147
 
{
148
 
        GHashTable *app_hash = (GHashTable *) user_data;
149
 
        CategoryData *data = (CategoryData *) catdata;
150
 
        gchar *uri;
151
 
 
152
 
        GList *launcher_list = data->filtered_launcher_list;
153
 
 
154
 
        while (launcher_list)
155
 
        {
156
 
                g_object_get (launcher_list->data, "tile-uri", &uri, NULL);
157
 
                /* eliminate dups of same app in multiple categories */
158
 
                if (!g_hash_table_lookup (app_hash, uri))
159
 
                        g_hash_table_insert (app_hash, uri, launcher_list->data);
160
 
                else
161
 
                        g_free (uri);
162
 
                launcher_list = g_list_next (launcher_list);
163
 
        }
164
 
}
165
 
 
166
 
static gboolean
167
 
return_first_entry (gpointer key, gpointer value, gpointer unused)
168
 
{
169
 
        return TRUE;    /*better way to pull an entry out ? */
170
 
}
171
 
 
172
 
static void
173
 
launch_selected_app (AppShellData * app_data)
174
 
{
175
 
        GHashTable *app_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
176
 
        guint num_apps;
177
 
 
178
 
        g_list_foreach (app_data->categories_list, generate_potential_apps, app_hash);
179
 
        num_apps = g_hash_table_size (app_hash);
180
 
        if (num_apps == 1)
181
 
        {
182
 
                ApplicationTile *launcher =
183
 
                        APPLICATION_TILE (g_hash_table_find (app_hash, return_first_entry, NULL));
184
 
                g_hash_table_destroy (app_hash);
185
 
                handle_launcher_single_clicked (TILE (launcher), app_data);
186
 
                return;
187
 
        }
188
 
 
189
 
        g_hash_table_destroy (app_hash);
190
 
}
191
 
 
192
 
static gboolean
193
 
main_keypress_callback (GtkWidget * widget, GdkEventKey * event, AppShellData * app_data)
194
 
{
195
 
        if (event->keyval == GDK_Return)
196
 
        {
197
 
                SlabSection *section = SLAB_SECTION (app_data->filter_section);
198
 
                NldSearchBar *search_bar;
199
 
 
200
 
                /* Make sure our implementation has not changed */
201
 
                g_assert (NLD_IS_SEARCH_BAR (section->contents));
202
 
                search_bar = NLD_SEARCH_BAR (section->contents);
203
 
                if (nld_search_bar_has_focus (search_bar))
204
 
                {
205
 
                        launch_selected_app (app_data);
206
 
                        return TRUE;
207
 
                }
208
 
        }
209
 
 
210
 
        /* quit on ESC or Ctl-W or Ctl-Q */
211
 
        if (event->keyval == GDK_Escape ||
212
 
                ((event->keyval == GDK_w || event->keyval == GDK_W)     && (event->state & GDK_CONTROL_MASK)) ||
213
 
                ((event->keyval == GDK_q || event->keyval == GDK_Q) && (event->state & GDK_CONTROL_MASK)))
214
 
        {
215
 
                if (app_data->exit_on_close)
216
 
                        gtk_main_quit ();
217
 
                else
218
 
                        hide_shell (app_data);
219
 
                return TRUE;
220
 
        }
221
 
        return FALSE;
222
 
}
223
 
 
224
 
static gboolean
225
 
main_delete_callback (GtkWidget * widget, GdkEvent * event, AppShellData * app_data)
226
 
{
227
 
        if (app_data->exit_on_close)
228
 
        {
229
 
                gtk_main_quit ();
230
 
                return FALSE;
231
 
        }
232
 
 
233
 
        hide_shell (app_data);
234
 
        return TRUE;            /* stop the processing of this event */
235
 
}
236
 
 
237
 
void
238
 
layout_shell (AppShellData * app_data, const gchar * filter_title, const gchar * groups_title,
239
 
        const gchar * actions_title, GSList * actions,
240
 
        void (*actions_handler) (Tile *, TileEvent *, gpointer))
241
 
{
242
 
        GtkWidget *filter_section;
243
 
        GtkWidget *groups_section;
244
 
        GtkWidget *actions_section;
245
 
 
246
 
        GtkWidget *left_vbox;
247
 
        GtkWidget *right_vbox;
248
 
        gint num_cols;
249
 
 
250
 
        GtkWidget *sw;
251
 
        GtkAdjustment *adjustment;
252
 
 
253
 
        app_data->shell = shell_window_new (app_data);
254
 
        app_data->static_actions = actions;
255
 
 
256
 
        right_vbox = gtk_vbox_new (FALSE, CATEGORY_SPACING);
257
 
 
258
 
        num_cols = SIZING_SCREEN_WIDTH_LARGE_NUMCOLS;
259
 
        if (gdk_screen_width () <= SIZING_SCREEN_WIDTH_LARGE)
260
 
        {
261
 
                if (gdk_screen_width () <= SIZING_SCREEN_WIDTH_MEDIUM)
262
 
                        num_cols = SIZING_SCREEN_WIDTH_SMALL_NUMCOLS;
263
 
                else
264
 
                        num_cols = SIZING_SCREEN_WIDTH_MEDIUM_NUMCOLS;
265
 
        }
266
 
        app_data->category_layout =
267
 
                app_resizer_new (GTK_VBOX (right_vbox), num_cols, TRUE, app_data);
268
 
 
269
 
        sw = gtk_scrolled_window_new (NULL, NULL);
270
 
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC,
271
 
                GTK_POLICY_AUTOMATIC);
272
 
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_IN);
273
 
        gtk_container_add (GTK_CONTAINER (sw), app_data->category_layout);
274
 
        adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw));
275
 
        g_object_set (adjustment, "step-increment", (double) 20, NULL);
276
 
 
277
 
        create_application_category_sections (app_data);
278
 
        populate_application_category_sections (app_data, right_vbox);
279
 
        app_resizer_set_table_cache (APP_RESIZER (app_data->category_layout),
280
 
                app_data->cached_tables_list);
281
 
 
282
 
        gtk_container_set_focus_vadjustment (GTK_CONTAINER (right_vbox),
283
 
                gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (sw)));
284
 
 
285
 
        left_vbox = gtk_vbox_new (FALSE, 15);
286
 
 
287
 
        filter_section = create_filter_section (app_data, filter_title);
288
 
        app_data->filter_section = filter_section;
289
 
        gtk_box_pack_start (GTK_BOX (left_vbox), filter_section, FALSE, FALSE, 0);
290
 
 
291
 
        groups_section = create_groups_section (app_data, groups_title);
292
 
        app_data->groups_section = groups_section;
293
 
        populate_groups_section (app_data);
294
 
        gtk_box_pack_start (GTK_BOX (left_vbox), groups_section, FALSE, FALSE, 0);
295
 
 
296
 
        actions_section = create_actions_section (app_data, actions_title, actions_handler);
297
 
        app_data->actions_section = actions_section;
298
 
        gtk_box_pack_start (GTK_BOX (left_vbox), actions_section, FALSE, FALSE, 0);
299
 
 
300
 
        shell_window_set_contents (SHELL_WINDOW (app_data->shell), left_vbox, sw);
301
 
}
302
 
 
303
 
static gboolean
304
 
relayout_shell_partial (gpointer user_data)
305
 
{
306
 
        AppShellData *app_data = (AppShellData *) user_data;
307
 
        GtkVBox *vbox = APP_RESIZER (app_data->category_layout)->child;
308
 
        CategoryData *data;
309
 
 
310
 
        if (app_data->stop_incremental_relayout)
311
 
                return FALSE;
312
 
 
313
 
        if (app_data->incremental_relayout_cat_list != NULL)
314
 
        {
315
 
                /* There are still categories to layout */
316
 
                data = (CategoryData *) app_data->incremental_relayout_cat_list->data;
317
 
                if (data->filtered_launcher_list != NULL)
318
 
                {
319
 
                        populate_application_category_section (app_data, data->section,
320
 
                                data->filtered_launcher_list);
321
 
                        gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (data->section), TRUE, TRUE,
322
 
                                0);
323
 
                        app_data->filtered_out_everything = FALSE;
324
 
                }
325
 
 
326
 
                app_data->incremental_relayout_cat_list =
327
 
                        g_list_next (app_data->incremental_relayout_cat_list);
328
 
                return TRUE;
329
 
        }
330
 
 
331
 
        /* We're done laying out the categories; finish up */
332
 
        if (app_data->filtered_out_everything)
333
 
                show_no_results_message (app_data, GTK_WIDGET (vbox));
334
 
 
335
 
        app_resizer_set_table_cache (APP_RESIZER (app_data->category_layout),
336
 
                app_data->cached_tables_list);
337
 
        populate_groups_section (app_data);
338
 
 
339
 
        gtk_widget_show_all (app_data->category_layout);
340
 
        gdk_window_set_cursor (app_data->shell->window, NULL);
341
 
 
342
 
        app_data->stop_incremental_relayout = TRUE;
343
 
        return FALSE;
344
 
}
345
 
 
346
 
static void
347
 
relayout_shell_incremental (AppShellData * app_data)
348
 
{
349
 
        GtkVBox *vbox = APP_RESIZER (app_data->category_layout)->child;
350
 
 
351
 
        app_data->stop_incremental_relayout = FALSE;
352
 
        app_data->filtered_out_everything = TRUE;
353
 
        app_data->incremental_relayout_cat_list = app_data->categories_list;
354
 
 
355
 
        if (app_data->cached_tables_list)
356
 
                g_list_free (app_data->cached_tables_list);
357
 
        app_data->cached_tables_list = NULL;
358
 
 
359
 
        remove_container_entries (GTK_CONTAINER (vbox));
360
 
 
361
 
        g_idle_add ((GSourceFunc) relayout_shell_partial, app_data);
362
 
}
363
 
 
364
 
static void
365
 
relayout_shell (AppShellData * app_data)
366
 
{
367
 
        GtkWidget *shell = app_data->shell;
368
 
        GtkVBox *vbox = APP_RESIZER (app_data->category_layout)->child;
369
 
 
370
 
        populate_application_category_sections (app_data, GTK_WIDGET (vbox));
371
 
        app_resizer_set_table_cache (APP_RESIZER (app_data->category_layout),
372
 
                app_data->cached_tables_list);
373
 
        populate_groups_section (app_data);
374
 
 
375
 
        gtk_widget_show_all (shell);
376
 
        if (!app_data->static_actions && !app_data->last_clicked_launcher)
377
 
                gtk_widget_hide_all (app_data->actions_section);  /* don't show unless a launcher is selected */
378
 
}
379
 
 
380
 
static GtkWidget *
381
 
create_actions_section (AppShellData * app_data, const gchar * title,
382
 
        void (*actions_handler) (Tile *, TileEvent *, gpointer))
383
 
{
384
 
        GtkWidget *section, *launcher;
385
 
        GtkWidget *vbox;
386
 
        GSList *actions;
387
 
        AppAction *action;
388
 
        AtkObject *a11y_cat;
389
 
 
390
 
        g_assert (app_data != NULL);
391
 
 
392
 
        section = slab_section_new (title, Style1);
393
 
        g_object_ref (section);
394
 
 
395
 
        vbox = gtk_vbox_new (FALSE, 0);
396
 
        slab_section_set_contents (SLAB_SECTION (section), vbox);
397
 
 
398
 
        if (app_data->static_actions)
399
 
        {
400
 
                for (actions = app_data->static_actions; actions; actions = actions->next)
401
 
                {
402
 
                        GtkWidget *header;
403
 
 
404
 
                        action = (AppAction *) actions->data;
405
 
                        header = gtk_label_new (action->name);
406
 
                        gtk_misc_set_alignment (GTK_MISC (header), 0, 0.5);
407
 
                        launcher = nameplate_tile_new (NULL, NULL, header, NULL);
408
 
 
409
 
                        g_object_set_data (G_OBJECT (launcher), APP_ACTION_KEY, action->item);
410
 
                        g_signal_connect (launcher, "tile-activated", G_CALLBACK (actions_handler),
411
 
                                app_data);
412
 
                        gtk_box_pack_start (GTK_BOX (vbox), launcher, FALSE, FALSE, 0);
413
 
 
414
 
                        a11y_cat = gtk_widget_get_accessible (GTK_WIDGET (launcher));
415
 
                        atk_object_set_name (a11y_cat, action->name);
416
 
                }
417
 
        }
418
 
 
419
 
        return section;
420
 
}
421
 
 
422
 
static GtkWidget *
423
 
create_groups_section (AppShellData * app_data, const gchar * title)
424
 
{
425
 
        GtkWidget *section;
426
 
        GtkWidget *vbox;
427
 
 
428
 
        g_assert (app_data != NULL);
429
 
 
430
 
        section = slab_section_new (title, Style1);
431
 
        g_object_ref (section);
432
 
 
433
 
        vbox = gtk_vbox_new (FALSE, 0);
434
 
        slab_section_set_contents (SLAB_SECTION (section), vbox);
435
 
 
436
 
        return section;
437
 
}
438
 
 
439
 
static void
440
 
populate_groups_section (AppShellData * app_data)
441
 
{
442
 
        SlabSection *section = SLAB_SECTION (app_data->groups_section);
443
 
        GtkVBox *vbox;
444
 
        GList *cat_list;
445
 
 
446
 
        /* Make sure our implementation has not changed and it's still a GtkVBox */
447
 
        g_assert (GTK_IS_VBOX (section->contents));
448
 
 
449
 
        vbox = GTK_VBOX (section->contents);
450
 
        remove_container_entries (GTK_CONTAINER (vbox));
451
 
 
452
 
        cat_list = app_data->categories_list;
453
 
        do
454
 
        {
455
 
                CategoryData *data = (CategoryData *) cat_list->data;
456
 
                if (NULL != data->filtered_launcher_list)
457
 
                {
458
 
                        gtk_widget_set_state (GTK_WIDGET (data->group_launcher), GTK_STATE_NORMAL);
459
 
                        gtk_box_pack_start (GTK_BOX (vbox), GTK_WIDGET (data->group_launcher),
460
 
                                FALSE, FALSE, 0);
461
 
                }
462
 
        }
463
 
        while (NULL != (cat_list = g_list_next (cat_list)));
464
 
}
465
 
 
466
 
static void
467
 
handle_group_clicked (Tile * tile, TileEvent * event, gpointer user_data)
468
 
{
469
 
        AppShellData *app_data = (AppShellData *) user_data;
470
 
        GtkWidget *section = NULL;
471
 
 
472
 
        gint clicked_pos =
473
 
                GPOINTER_TO_INT (g_object_get_data (G_OBJECT (tile), GROUP_POSITION_NUMBER_KEY));
474
 
 
475
 
        GList *cat_list = app_data->categories_list;
476
 
 
477
 
        gint total = 0;
478
 
        do
479
 
        {
480
 
                CategoryData *cat_data = (CategoryData *) cat_list->data;
481
 
                gint pos =
482
 
                        GPOINTER_TO_INT (g_object_get_data (G_OBJECT (cat_data->group_launcher),
483
 
                                GROUP_POSITION_NUMBER_KEY));
484
 
                if (pos == clicked_pos)
485
 
                {
486
 
                        section = GTK_WIDGET (cat_data->section);
487
 
                        break;
488
 
                }
489
 
 
490
 
                if (NULL != cat_data->filtered_launcher_list)
491
 
                {
492
 
                        total += GTK_WIDGET (cat_data->section)->allocation.height +
493
 
                                CATEGORY_SPACING;
494
 
                }
495
 
        }
496
 
        while (NULL != (cat_list = g_list_next (cat_list)));
497
 
 
498
 
        g_assert (section != NULL);
499
 
        set_state (app_data, section);
500
 
 
501
 
        app_resizer_set_vadjustment_value (app_data->category_layout, total);
502
 
}
503
 
 
504
 
static void
505
 
set_state (AppShellData * app_data, GtkWidget * widget)
506
 
{
507
 
        if (app_data->selected_group)
508
 
        {
509
 
                slab_section_set_selected (app_data->selected_group, FALSE);
510
 
                app_data->selected_group = NULL;
511
 
        }
512
 
 
513
 
        if (widget)
514
 
        {
515
 
                app_data->selected_group = SLAB_SECTION (widget);
516
 
                slab_section_set_selected (SLAB_SECTION (widget), TRUE);
517
 
        }
518
 
        gtk_widget_queue_draw (app_data->shell);
519
 
}
520
 
 
521
 
static GtkWidget *
522
 
create_filter_section (AppShellData * app_data, const gchar * title)
523
 
{
524
 
        GtkWidget *section;
525
 
 
526
 
        GtkWidget *search_bar;
527
 
 
528
 
        section = slab_section_new (title, Style1);
529
 
        g_object_ref (section);
530
 
 
531
 
        search_bar = nld_search_bar_new ();
532
 
        nld_search_bar_set_search_timeout (NLD_SEARCH_BAR (search_bar), 0);
533
 
        slab_section_set_contents (SLAB_SECTION (section), search_bar);
534
 
 
535
 
        g_signal_connect (G_OBJECT (search_bar), "search", G_CALLBACK (handle_filter_changed),
536
 
                app_data);
537
 
 
538
 
        return section;
539
 
}
540
 
 
541
 
static gboolean
542
 
handle_filter_changed_delayed (gpointer user_data)
543
 
{
544
 
        AppShellData *app_data = (AppShellData *) user_data;
545
 
 
546
 
        g_list_foreach (app_data->categories_list, generate_filtered_lists,
547
 
                (gpointer) app_data->filter_string);
548
 
        app_data->last_clicked_launcher = NULL;
549
 
 
550
 
        /*  showing the updates incremtally is very visually distracting. Much worse than just blanking until
551
 
           the incremental work is done and then doing one show. It would be nice to optimize this though
552
 
           somehow and not even show any change but the cursor change until all the work is done. But since
553
 
           we do the work incrementally in an idle loop I don't know how else besides hiding to not show
554
 
           incremental updates
555
 
         */
556
 
        /* gdk_window_freeze_updates(app_data->category_layout->window); */
557
 
        gtk_widget_hide (app_data->category_layout);
558
 
        app_data->busy_cursor =
559
 
                gdk_cursor_new_for_display (gtk_widget_get_display (app_data->shell), GDK_WATCH);
560
 
        gdk_window_set_cursor (app_data->shell->window, app_data->busy_cursor);
561
 
        gdk_cursor_unref (app_data->busy_cursor);
562
 
 
563
 
        set_state (app_data, NULL);
564
 
        app_resizer_set_vadjustment_value (app_data->category_layout, 0);
565
 
 
566
 
        relayout_shell_incremental (app_data);
567
 
 
568
 
        app_data->filter_changed_timeout = 0;
569
 
        return FALSE;
570
 
}
571
 
 
572
 
static gboolean
573
 
handle_filter_changed (NldSearchBar * search_bar, int context, const char *text, gpointer data)
574
 
{
575
 
        AppShellData *app_data;
576
 
 
577
 
        app_data = (AppShellData *) data;
578
 
 
579
 
        if (app_data->filter_string)
580
 
                g_free (app_data->filter_string);
581
 
        app_data->filter_string = g_strdup (text);
582
 
 
583
 
        if (app_data->filter_changed_timeout)
584
 
                g_source_remove (app_data->filter_changed_timeout);
585
 
 
586
 
        app_data->filter_changed_timeout =
587
 
                g_timeout_add (75, handle_filter_changed_delayed, app_data);
588
 
        app_data->stop_incremental_relayout = TRUE;
589
 
 
590
 
        return FALSE;
591
 
}
592
 
 
593
 
static void
594
 
generate_filtered_lists (gpointer catdata, gpointer user_data)
595
 
{
596
 
        CategoryData *data = (CategoryData *) catdata;
597
 
 
598
 
        /* Fixme - everywhere you use ascii you need to fix up for multibyte */
599
 
        gchar *filter_string = g_ascii_strdown (user_data, -1);
600
 
        gchar *temp1, *temp2;
601
 
        GList *launcher_list = data->launcher_list;
602
 
 
603
 
        g_list_free (data->filtered_launcher_list);
604
 
        data->filtered_launcher_list = NULL;
605
 
 
606
 
        do
607
 
        {
608
 
                ApplicationTile *launcher = APPLICATION_TILE (launcher_list->data);
609
 
                const gchar *filename;
610
 
 
611
 
                temp1 = NULL;
612
 
                temp2 = NULL;
613
 
 
614
 
                /* Since the filter may remove this entry from the
615
 
                   container it will not get a mouse out event */
616
 
                gtk_widget_set_state (GTK_WIDGET (launcher), GTK_STATE_NORMAL);
617
 
                filename = g_object_get_data (G_OBJECT (launcher), TILE_EXEC_NAME); /* do I need to free this */
618
 
 
619
 
                temp1 = g_ascii_strdown (launcher->name, -1);
620
 
                if (launcher->description)
621
 
                        temp2 = g_ascii_strdown (launcher->description, -1);
622
 
                if (g_strrstr (temp1, filter_string) || (launcher->description
623
 
                                && g_strrstr (temp2, filter_string))
624
 
                        || g_strrstr (filename, filter_string))
625
 
                {
626
 
                        data->filtered_launcher_list =
627
 
                                g_list_append (data->filtered_launcher_list, launcher);
628
 
                }
629
 
                if (temp1)
630
 
                        g_free (temp1);
631
 
                if (temp2)
632
 
                        g_free (temp2);
633
 
        }
634
 
        while (NULL != (launcher_list = g_list_next (launcher_list)));
635
 
        g_free (filter_string);
636
 
}
637
 
 
638
 
static void
639
 
delete_old_data (AppShellData * app_data)
640
 
{
641
 
        GList *temp;
642
 
        GList *cat_list;
643
 
 
644
 
        g_assert (app_data != NULL);
645
 
        g_assert (app_data->categories_list != NULL);
646
 
 
647
 
        cat_list = app_data->categories_list;
648
 
 
649
 
        do
650
 
        {
651
 
                CategoryData *data = (CategoryData *) cat_list->data;
652
 
                gtk_widget_destroy (GTK_WIDGET (data->section));
653
 
                gtk_widget_destroy (GTK_WIDGET (data->group_launcher));
654
 
                g_object_unref (data->section);
655
 
                g_object_unref (data->group_launcher);
656
 
                g_free (data->category);
657
 
 
658
 
                for (temp = data->launcher_list; temp; temp = g_list_next (temp))
659
 
                {
660
 
                        g_free (g_object_get_data (G_OBJECT (temp->data), TILE_EXEC_NAME));
661
 
                        g_object_unref (temp->data);
662
 
                }
663
 
 
664
 
                g_list_free (data->launcher_list);
665
 
                g_list_free (data->filtered_launcher_list);
666
 
                g_free (data);
667
 
        }
668
 
        while (NULL != (cat_list = g_list_next (cat_list)));
669
 
 
670
 
        g_list_free (app_data->categories_list);
671
 
        app_data->categories_list = NULL;
672
 
        app_data->selected_group = NULL;
673
 
}
674
 
 
675
 
static void
676
 
create_application_category_sections (AppShellData * app_data)
677
 
{
678
 
        GList *cat_list;
679
 
        AtkObject *a11y_cat;
680
 
        gint pos = 0;
681
 
 
682
 
        g_assert (app_data != NULL);
683
 
        g_assert (app_data->categories_list != NULL);   /* Fixme - pop up a dialog box and then close */
684
 
 
685
 
        cat_list = app_data->categories_list;
686
 
 
687
 
        do
688
 
        {
689
 
                CategoryData *data = (CategoryData *) cat_list->data;
690
 
                GtkWidget *header = gtk_label_new (data->category);
691
 
                gchar *markup;
692
 
                GtkWidget *hbox;
693
 
                GtkWidget *table;
694
 
 
695
 
                gtk_misc_set_alignment (GTK_MISC (header), 0, 0.5);
696
 
                data->group_launcher = TILE (nameplate_tile_new (NULL, NULL, header, NULL));
697
 
                g_object_ref (data->group_launcher);
698
 
 
699
 
                g_object_set_data (G_OBJECT (data->group_launcher), GROUP_POSITION_NUMBER_KEY,
700
 
                        GINT_TO_POINTER (pos));
701
 
                pos++;
702
 
                g_signal_connect (data->group_launcher, "tile-activated",
703
 
                        G_CALLBACK (handle_group_clicked), app_data);
704
 
                a11y_cat = gtk_widget_get_accessible (GTK_WIDGET (data->group_launcher));
705
 
                atk_object_set_name (a11y_cat, data->category);
706
 
 
707
 
                markup = g_markup_printf_escaped ("<span size=\"x-large\" weight=\"bold\">%s</span>",
708
 
                        data->category);
709
 
                data->section = SLAB_SECTION (slab_section_new_with_markup (markup, Style2));
710
 
 
711
 
                /* as we filter these will be added/removed from parent container and we dont want them destroyed */
712
 
                g_object_ref (data->section);
713
 
                g_free (markup);
714
 
 
715
 
                hbox = gtk_hbox_new (FALSE, 0);
716
 
                table = gtk_table_new (0, 0, TRUE);
717
 
                gtk_table_set_col_spacings (GTK_TABLE (table), 5);
718
 
                gtk_table_set_row_spacings (GTK_TABLE (table), 5);
719
 
                gtk_box_pack_start (GTK_BOX (hbox), table, FALSE, FALSE, 15);
720
 
                slab_section_set_contents (SLAB_SECTION (data->section), hbox);
721
 
        }
722
 
        while (NULL != (cat_list = g_list_next (cat_list)));
723
 
}
724
 
 
725
 
static void
726
 
show_no_results_message (AppShellData * app_data, GtkWidget * containing_vbox)
727
 
{
728
 
        gchar *markup;
729
 
        gchar *str1;
730
 
        gchar *str2;
731
 
 
732
 
        if (!app_data->filtered_out_everything_widget)
733
 
        {
734
 
                GtkWidget *hbox;
735
 
                GtkWidget *image;
736
 
                GtkWidget *label;
737
 
 
738
 
                app_data->filtered_out_everything_widget = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
739
 
                g_object_ref (app_data->filtered_out_everything_widget);
740
 
 
741
 
                hbox = gtk_hbox_new (FALSE, 0);
742
 
                image = themed_icon_new ("face-surprise", GTK_ICON_SIZE_DIALOG);
743
 
                gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, FALSE, 0);
744
 
 
745
 
                label = gtk_label_new (NULL);
746
 
                gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
747
 
                gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 15);
748
 
                app_data->filtered_out_everything_widget_label = GTK_LABEL (label);
749
 
 
750
 
                gtk_container_add (GTK_CONTAINER (app_data->filtered_out_everything_widget), hbox);
751
 
        }
752
 
 
753
 
        str1 = g_markup_printf_escaped ("<b>%s</b>", app_data->filter_string);
754
 
        str2 = g_strdup_printf (_("Your filter \"%s\" does not match any items."), str1);
755
 
        markup = g_strdup_printf ("<span size=\"large\"><b>%s</b></span>\n\n%s",
756
 
                _("No matches found."), str2);
757
 
        gtk_label_set_text (app_data->filtered_out_everything_widget_label, markup);
758
 
        gtk_label_set_use_markup (app_data->filtered_out_everything_widget_label, TRUE);
759
 
        gtk_box_pack_start (GTK_BOX (containing_vbox), app_data->filtered_out_everything_widget,
760
 
                TRUE, TRUE, 0);
761
 
        g_free (str1);
762
 
        g_free (str2);
763
 
        g_free (markup);
764
 
}
765
 
 
766
 
static void
767
 
populate_application_category_sections (AppShellData * app_data, GtkWidget * containing_vbox)
768
 
{
769
 
        GList *cat_list = app_data->categories_list;
770
 
        gboolean filtered_out_everything = TRUE;
771
 
        if (app_data->cached_tables_list)
772
 
                g_list_free (app_data->cached_tables_list);
773
 
        app_data->cached_tables_list = NULL;
774
 
 
775
 
        remove_container_entries (GTK_CONTAINER (containing_vbox));
776
 
        do
777
 
        {
778
 
                CategoryData *data = (CategoryData *) cat_list->data;
779
 
                if (NULL != data->filtered_launcher_list)
780
 
                {
781
 
                        populate_application_category_section (app_data, data->section,
782
 
                                data->filtered_launcher_list);
783
 
                        gtk_box_pack_start (GTK_BOX (containing_vbox), GTK_WIDGET (data->section),
784
 
                                TRUE, TRUE, 0);
785
 
                        filtered_out_everything = FALSE;
786
 
                }
787
 
        }
788
 
        while (NULL != (cat_list = g_list_next (cat_list)));
789
 
 
790
 
        if (TRUE == filtered_out_everything)
791
 
                show_no_results_message (app_data, containing_vbox);
792
 
}
793
 
 
794
 
static void
795
 
populate_application_category_section (AppShellData * app_data, SlabSection * section,
796
 
        GList * launcher_list)
797
 
{
798
 
        GtkWidget *hbox;
799
 
        GtkTable *table;
800
 
        GList *children;
801
 
 
802
 
        g_assert (GTK_IS_HBOX (section->contents));
803
 
        hbox = GTK_WIDGET (section->contents);
804
 
 
805
 
        children = gtk_container_get_children (GTK_CONTAINER (hbox));
806
 
        table = children->data;
807
 
        g_list_free (children);
808
 
 
809
 
        /* Make sure our implementation has not changed and it's still a GtkTable */
810
 
        g_assert (GTK_IS_TABLE (table));
811
 
 
812
 
        app_data->cached_tables_list = g_list_append (app_data->cached_tables_list, table);
813
 
 
814
 
        app_resizer_layout_table_default (APP_RESIZER (app_data->category_layout), table,
815
 
                launcher_list);
816
 
 
817
 
}
818
 
 
819
 
gboolean
820
 
regenerate_categories (AppShellData * app_data)
821
 
{
822
 
        delete_old_data (app_data);
823
 
        generate_categories (app_data);
824
 
        create_application_category_sections (app_data);
825
 
        relayout_shell (app_data);
826
 
 
827
 
        return FALSE;   /* remove this function from the list */
828
 
}
829
 
 
830
 
static void
831
 
gmenu_tree_changed_callback (GMenuTree * old_tree, gpointer user_data)
832
 
{
833
 
        /*
834
 
        This method only gets called on the first change (gmenu appears to ignore subsequent) until
835
 
        we reget the root dir which we can't do in this method because if we do for some reason this
836
 
        method then gets called multiple times for one actual change. This actually is okay because
837
 
        it's probably a good idea to wait a couple seconds to regenerate the categories in case there
838
 
        are multiple quick changes being made, no sense regenerating multiple times.
839
 
        */
840
 
        g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE, 3000, (GSourceFunc) regenerate_categories,
841
 
                user_data, NULL);
842
 
}
843
 
 
844
 
AppShellData *
845
 
appshelldata_new (const gchar * menu_name, NewAppConfig * new_apps, const gchar * gconf_keys_prefix,
846
 
        GtkIconSize icon_size, gboolean show_tile_generic_name, gboolean exit_on_close)
847
 
{
848
 
        AppShellData *app_data = g_new0 (AppShellData, 1);
849
 
        app_data->gconf_prefix = gconf_keys_prefix;
850
 
        app_data->new_apps = new_apps;
851
 
        app_data->menu_name = menu_name;
852
 
        app_data->icon_size = icon_size;
853
 
        app_data->stop_incremental_relayout = TRUE;
854
 
        app_data->show_tile_generic_name = show_tile_generic_name;
855
 
        app_data->exit_on_close = exit_on_close;
856
 
        return app_data;
857
 
}
858
 
 
859
 
void
860
 
generate_categories (AppShellData * app_data)
861
 
{
862
 
        GMenuTreeDirectory *root_dir;
863
 
        GSList *contents, *l;
864
 
        gboolean need_misc = FALSE;
865
 
 
866
 
        if (!app_data->tree)
867
 
        {
868
 
                app_data->tree = gmenu_tree_lookup (app_data->menu_name, GMENU_TREE_FLAGS_NONE);
869
 
                gmenu_tree_add_monitor (app_data->tree, gmenu_tree_changed_callback, app_data);
870
 
        }
871
 
        root_dir = gmenu_tree_get_root_directory (app_data->tree);
872
 
        if (root_dir)
873
 
                contents = gmenu_tree_directory_get_contents (root_dir);
874
 
        else
875
 
                contents = NULL;
876
 
        if (!root_dir || !contents)
877
 
        {
878
 
                GtkWidget *dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT,
879
 
                        GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Failure loading - %s",
880
 
                        app_data->menu_name);
881
 
                gtk_dialog_run (GTK_DIALOG (dialog));
882
 
                gtk_widget_destroy (dialog);
883
 
                exit (1);       /* Fixme - is there a GNOME/GTK way to do this. */
884
 
        }
885
 
 
886
 
        for (l = contents; l; l = l->next)
887
 
        {
888
 
                const char *category;
889
 
                GMenuTreeItem *item = l->data;
890
 
 
891
 
                switch (gmenu_tree_item_get_type (item))
892
 
                {
893
 
                case GMENU_TREE_ITEM_DIRECTORY:
894
 
                        category = gmenu_tree_directory_get_name ((GMenuTreeDirectory*)item);
895
 
                        generate_category(category, (GMenuTreeDirectory*)item, app_data, TRUE);
896
 
                        break;
897
 
                case GMENU_TREE_ITEM_ENTRY:
898
 
                        need_misc = TRUE;
899
 
                        break;
900
 
                default:
901
 
                        break;
902
 
                }
903
 
 
904
 
                gmenu_tree_item_unref (item);
905
 
        }
906
 
        g_slist_free (contents);
907
 
 
908
 
        if (need_misc)
909
 
                generate_category (_("Other"), root_dir, app_data, FALSE);
910
 
 
911
 
        if (app_data->hash)
912
 
        {
913
 
                g_hash_table_destroy (app_data->hash);
914
 
                app_data->hash = NULL;
915
 
        }
916
 
 
917
 
        gmenu_tree_item_unref (root_dir);
918
 
 
919
 
        if (app_data->new_apps && (app_data->new_apps->max_items > 0))
920
 
                generate_new_apps (app_data);
921
 
}
922
 
 
923
 
static void
924
 
generate_category (const char * category, GMenuTreeDirectory * root_dir, AppShellData * app_data, gboolean recursive)
925
 
{
926
 
        CategoryData *data;
927
 
        /* This is not needed. GMenu already returns an ordered, non duplicate list
928
 
        GList *list_entry;
929
 
        list_entry =
930
 
                g_list_find_custom (app_data->categories_list, category,
931
 
                category_name_compare);
932
 
        if (!list_entry)
933
 
        {
934
 
        */
935
 
                data = g_new0 (CategoryData, 1);
936
 
                data->category = g_strdup (category);
937
 
                app_data->categories_list =
938
 
                        /* use the gmenu order instead of alphabetical */
939
 
                        g_list_append (app_data->categories_list, data);
940
 
                        /* g_list_insert_sorted (app_data->categories_list, data, category_data_compare); */
941
 
        /*
942
 
        }
943
 
        else
944
 
        {
945
 
                data = list_entry->data;
946
 
        }
947
 
        */
948
 
 
949
 
        if (app_data->hash)     /* used to eliminate dups on a per category basis. */
950
 
                g_hash_table_destroy (app_data->hash);
951
 
        app_data->hash = g_hash_table_new (g_str_hash, g_str_equal);
952
 
        generate_launchers (root_dir, app_data, data, recursive);
953
 
}
954
 
 
955
 
static gboolean
956
 
check_specific_apps_hack (GnomeDesktopItem * item)
957
 
{
958
 
        static const gchar *COMMAND_LINE_LOCKDOWN_GCONF_KEY =
959
 
                "/desktop/gnome/lockdown/disable_command_line";
960
 
        static const gchar *COMMAND_LINE_LOCKDOWN_DESKTOP_CATEGORY = "TerminalEmulator";
961
 
        static gboolean got_lockdown_value = FALSE;
962
 
        static gboolean command_line_lockdown;
963
 
 
964
 
        gchar *path;
965
 
        const char *exec;
966
 
 
967
 
        if (!got_lockdown_value)
968
 
        {
969
 
                got_lockdown_value = TRUE;
970
 
                command_line_lockdown = get_slab_gconf_bool (COMMAND_LINE_LOCKDOWN_GCONF_KEY);
971
 
        }
972
 
 
973
 
        /* This seems like an ugly hack but it's the way it's currently done in the old control center */
974
 
        exec = gnome_desktop_item_get_string (item, GNOME_DESKTOP_ITEM_EXEC);
975
 
 
976
 
        /* discard xscreensaver if gnome-screensaver is installed */
977
 
        if ((exec && !strcmp (exec, "xscreensaver-demo"))
978
 
                && (path = g_find_program_in_path ("gnome-screensaver-preferences")))
979
 
        {
980
 
                g_free (path);
981
 
                return TRUE;
982
 
        }
983
 
 
984
 
        /* discard gnome-keyring-manager if CASA is installed */
985
 
        if ((exec && !strcmp (exec, "gnome-keyring-manager"))
986
 
                && (path = g_find_program_in_path ("CASAManager.sh")))
987
 
        {
988
 
                g_free (path);
989
 
                return TRUE;
990
 
        }
991
 
 
992
 
        /* discard terminals if lockdown key is set */
993
 
        if (command_line_lockdown)
994
 
        {
995
 
                const gchar *categories =
996
 
                        gnome_desktop_item_get_string (item, GNOME_DESKTOP_ITEM_CATEGORIES);
997
 
                if (g_strrstr (categories, COMMAND_LINE_LOCKDOWN_DESKTOP_CATEGORY))
998
 
                {
999
 
                        /* printf ("eliminating %s\n", gnome_desktop_item_get_location (item)); */
1000
 
                        return TRUE;
1001
 
                }
1002
 
        }
1003
 
 
1004
 
        return FALSE;
1005
 
}
1006
 
 
1007
 
static void
1008
 
generate_launchers (GMenuTreeDirectory * root_dir, AppShellData * app_data, CategoryData * cat_data, gboolean recursive)
1009
 
{
1010
 
        GnomeDesktopItem *desktop_item;
1011
 
        const gchar *desktop_file;
1012
 
        GSList *contents, *l;
1013
 
 
1014
 
        contents = gmenu_tree_directory_get_contents (root_dir);
1015
 
        for (l = contents; l; l = l->next)
1016
 
        {
1017
 
                switch (gmenu_tree_item_get_type (l->data))
1018
 
                {
1019
 
                case GMENU_TREE_ITEM_DIRECTORY:
1020
 
                        /* g_message ("Found sub-category %s", gmenu_tree_directory_get_name (l->data)); */
1021
 
                        if (recursive)
1022
 
                                generate_launchers (l->data, app_data, cat_data, TRUE);
1023
 
                        break;
1024
 
                case GMENU_TREE_ITEM_ENTRY:
1025
 
                        /* g_message ("Found item name is:%s", gmenu_tree_entry_get_name (l->data)); */
1026
 
                        desktop_file = gmenu_tree_entry_get_desktop_file_path (l->data);
1027
 
                        if (desktop_file)
1028
 
                        {
1029
 
                                if (g_hash_table_lookup (app_data->hash, desktop_file))
1030
 
                                {
1031
 
                                        break;  /* duplicate */
1032
 
                                }
1033
 
                                /* Fixme - make sure it's safe to store this without duping it. As far as I can tell it is
1034
 
                                   safe as long as I don't hang on to this anylonger than I hang on to the GMenuTreeEntry*
1035
 
                                   which brings up another point - am I supposed to free these or does freeing the top level recurse
1036
 
                                */
1037
 
                                g_hash_table_insert (app_data->hash, (gpointer) desktop_file,
1038
 
                                        (gpointer) desktop_file);
1039
 
                        }
1040
 
                        desktop_item = gnome_desktop_item_new_from_file (desktop_file, 0, NULL);
1041
 
                        if (!desktop_item)
1042
 
                        {
1043
 
                                g_critical ("Failure - gnome_desktop_item_new_from_file(%s)",
1044
 
                                            desktop_file);
1045
 
                                break;
1046
 
                        }
1047
 
                        if (!check_specific_apps_hack (desktop_item))
1048
 
                                insert_launcher_into_category (cat_data, desktop_item, app_data);
1049
 
                        gnome_desktop_item_unref (desktop_item);
1050
 
                        break;
1051
 
                default:
1052
 
                        break;
1053
 
                }
1054
 
 
1055
 
                gmenu_tree_item_unref (l->data);
1056
 
        }
1057
 
        g_slist_free (contents);
1058
 
}
1059
 
 
1060
 
static void
1061
 
generate_new_apps (AppShellData * app_data)
1062
 
{
1063
 
        GHashTable *all_apps_cache = NULL;
1064
 
        gchar *all_apps;
1065
 
        GError *error = NULL;
1066
 
        gchar *separator = "\n";
1067
 
        gchar *gconf_key;
1068
 
 
1069
 
        gchar *basename;
1070
 
        gchar *all_apps_file_name;
1071
 
        gchar **all_apps_split;
1072
 
        gint x;
1073
 
        gboolean got_new_apps;
1074
 
        CategoryData *new_apps_category = NULL;
1075
 
        GList *categories, *launchers;
1076
 
        GHashTable *new_apps_dups;
1077
 
 
1078
 
        gconf_key = g_strdup_printf ("%s%s", app_data->gconf_prefix, NEW_APPS_FILE_KEY);
1079
 
        basename = get_slab_gconf_string (gconf_key);
1080
 
        g_free (gconf_key);
1081
 
        if (!basename)
1082
 
        {
1083
 
                g_warning ("Failure getting gconf key NEW_APPS_FILE_KEY");
1084
 
                return;
1085
 
        }
1086
 
 
1087
 
        all_apps_file_name = g_build_filename (g_get_home_dir (), basename, NULL);
1088
 
        g_free (basename);
1089
 
 
1090
 
        if (!g_file_get_contents (all_apps_file_name, &all_apps, NULL, &error))
1091
 
        {
1092
 
                /* If file does not exist, this is the first time this user has run this, create the baseline file */
1093
 
                GList *categories, *launchers;
1094
 
                GString *gstr;
1095
 
                gchar *dirname;
1096
 
 
1097
 
                g_error_free (error);
1098
 
                error = NULL;
1099
 
 
1100
 
                /* best initial size determined by running on a couple different platforms */
1101
 
                gstr = g_string_sized_new (10000);
1102
 
 
1103
 
                for (categories = app_data->categories_list; categories; categories = categories->next)
1104
 
                {
1105
 
                        CategoryData *data = categories->data;
1106
 
                        for (launchers = data->launcher_list; launchers; launchers = launchers->next)
1107
 
                        {
1108
 
                                Tile *tile = TILE (launchers->data);
1109
 
                                GnomeDesktopItem *item =
1110
 
                                        application_tile_get_desktop_item (APPLICATION_TILE (tile));
1111
 
                                const gchar *uri = gnome_desktop_item_get_location (item);
1112
 
                                g_string_append (gstr, uri);
1113
 
                                g_string_append (gstr, separator);
1114
 
                        }
1115
 
                }
1116
 
 
1117
 
                dirname = g_path_get_dirname (all_apps_file_name);
1118
 
                g_mkdir_with_parents (dirname, 0700);   /* creates if does not exist */
1119
 
                g_free (dirname);
1120
 
 
1121
 
                if (!g_file_set_contents (all_apps_file_name, gstr->str, -1, &error))
1122
 
                        g_warning ("Error setting all apps file:%s\n", error->message);
1123
 
 
1124
 
                g_string_free (gstr, TRUE);
1125
 
                g_free (all_apps_file_name);
1126
 
                return;
1127
 
        }
1128
 
 
1129
 
        all_apps_cache = g_hash_table_new (g_str_hash, g_str_equal);
1130
 
        all_apps_split = g_strsplit (all_apps, separator, -1);
1131
 
        for (x = 0; all_apps_split[x]; x++)
1132
 
        {
1133
 
                g_hash_table_insert (all_apps_cache, all_apps_split[x], all_apps_split[x]);
1134
 
        }
1135
 
 
1136
 
        got_new_apps = FALSE;
1137
 
        new_apps_dups = g_hash_table_new (g_str_hash, g_str_equal);
1138
 
        for (categories = app_data->categories_list; categories; categories = categories->next)
1139
 
        {
1140
 
                CategoryData *cat_data = categories->data;
1141
 
                for (launchers = cat_data->launcher_list; launchers; launchers = launchers->next)
1142
 
                {
1143
 
                        Tile *tile = TILE (launchers->data);
1144
 
                        GnomeDesktopItem *item =
1145
 
                                application_tile_get_desktop_item (APPLICATION_TILE (tile));
1146
 
                        const gchar *uri = gnome_desktop_item_get_location (item);
1147
 
                        if (!g_hash_table_lookup (all_apps_cache, uri))
1148
 
                        {
1149
 
                                GFile *file;
1150
 
                                GFileInfo *info;
1151
 
                                long filetime;
1152
 
 
1153
 
                                if (g_hash_table_lookup (new_apps_dups, uri))
1154
 
                                {
1155
 
                                        /* if a desktop file is in 2 or more top level categories, only show it once */
1156
 
                                        /* printf("Discarding Newapp duplicate:%s\n", uri); */
1157
 
                                        break;
1158
 
                                }
1159
 
                                g_hash_table_insert (new_apps_dups, (gpointer) uri, (gpointer) uri);
1160
 
 
1161
 
                                if (!got_new_apps)
1162
 
                                {
1163
 
                                        new_apps_category = g_new0 (CategoryData, 1);
1164
 
                                        new_apps_category->category =
1165
 
                                                g_strdup (app_data->new_apps->name);
1166
 
                                        app_data->new_apps->garray =
1167
 
                                                g_array_sized_new (FALSE, TRUE,
1168
 
                                                sizeof (NewAppData *),
1169
 
                                                app_data->new_apps->max_items);
1170
 
 
1171
 
                                        /* should not need this, but a bug in glib does not actually clear the elements until you call this method */
1172
 
                                        g_array_set_size (app_data->new_apps->garray, app_data->new_apps->max_items);
1173
 
                                        got_new_apps = TRUE;
1174
 
                                }
1175
 
 
1176
 
                                file = g_file_new_for_uri (uri);
1177
 
                                info = g_file_query_info (file,
1178
 
                                                          G_FILE_ATTRIBUTE_TIME_MODIFIED,
1179
 
                                                          0, NULL, NULL);
1180
 
 
1181
 
                                if (!info)
1182
 
                                {
1183
 
                                        g_object_unref (file);
1184
 
                                        g_warning ("Cant get vfs info for %s\n", uri);
1185
 
                                        return;
1186
 
                                }
1187
 
                                filetime = (long) g_file_info_get_attribute_uint64 (info,
1188
 
                                                                                    G_FILE_ATTRIBUTE_TIME_MODIFIED);
1189
 
                                g_object_unref (info);
1190
 
                                g_object_unref (file);
1191
 
 
1192
 
                                for (x = 0; x < app_data->new_apps->max_items; x++)
1193
 
                                {
1194
 
                                        NewAppData *temp_data = (NewAppData *)
1195
 
                                                g_array_index (app_data->new_apps->garray, NewAppData *, x);
1196
 
                                        if (!temp_data || filetime > temp_data->time)   /* if this slot is empty or we are newer than this slot */
1197
 
                                        {
1198
 
                                                NewAppData *temp = g_new0 (NewAppData, 1);
1199
 
                                                temp->time = filetime;
1200
 
                                                temp->item = item;
1201
 
                                                g_array_insert_val (app_data->new_apps->garray, x,
1202
 
                                                        temp);
1203
 
                                                break;
1204
 
                                        }
1205
 
                                }
1206
 
                        }
1207
 
                }
1208
 
        }
1209
 
        g_hash_table_destroy (new_apps_dups);
1210
 
        g_hash_table_destroy (all_apps_cache);
1211
 
 
1212
 
        if (got_new_apps)
1213
 
        {
1214
 
                for (x = 0; x < app_data->new_apps->max_items; x++)
1215
 
                {
1216
 
                        NewAppData *data =
1217
 
                                (NewAppData *) g_array_index (app_data->new_apps->garray,
1218
 
                                NewAppData *, x);
1219
 
                        if (data)
1220
 
                        {
1221
 
                                insert_launcher_into_category (new_apps_category, data->item,
1222
 
                                        app_data);
1223
 
                                g_free (data);
1224
 
                        }
1225
 
                        else
1226
 
                                break;
1227
 
                }
1228
 
                app_data->categories_list =
1229
 
                        g_list_prepend (app_data->categories_list, new_apps_category);
1230
 
 
1231
 
                g_array_free (app_data->new_apps->garray, TRUE);
1232
 
        }
1233
 
        g_free (all_apps);
1234
 
        g_free (all_apps_file_name);
1235
 
        g_strfreev (all_apps_split);
1236
 
}
1237
 
 
1238
 
static void
1239
 
insert_launcher_into_category (CategoryData * cat_data, GnomeDesktopItem * desktop_item,
1240
 
        AppShellData * app_data)
1241
 
{
1242
 
        GtkWidget *launcher;
1243
 
        static GtkSizeGroup *icon_group = NULL;
1244
 
 
1245
 
        gchar *filepath;
1246
 
        gchar *filename;
1247
 
        GtkWidget *tile_icon;
1248
 
 
1249
 
        if (!icon_group)
1250
 
                icon_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
1251
 
 
1252
 
        launcher =
1253
 
                application_tile_new_full (gnome_desktop_item_get_location (desktop_item),
1254
 
                app_data->icon_size, app_data->show_tile_generic_name, app_data->gconf_prefix);
1255
 
        gtk_widget_set_size_request (launcher, SIZING_TILE_WIDTH, -1);
1256
 
 
1257
 
        filepath =
1258
 
                g_strdup (gnome_desktop_item_get_string (desktop_item, GNOME_DESKTOP_ITEM_EXEC));
1259
 
        g_strdelimit (filepath, " ", '\0');     /* just want the file name - no args or replacements */
1260
 
        filename = g_strrstr (filepath, "/");
1261
 
        if (filename)
1262
 
                g_stpcpy (filepath, filename + 1);
1263
 
        filename = g_ascii_strdown (filepath, -1);
1264
 
        g_free (filepath);
1265
 
        g_object_set_data (G_OBJECT (launcher), TILE_EXEC_NAME, filename);
1266
 
 
1267
 
        tile_icon = NAMEPLATE_TILE (launcher)->image;
1268
 
        gtk_size_group_add_widget (icon_group, tile_icon);
1269
 
 
1270
 
        g_signal_connect (launcher, "tile-activated", G_CALLBACK (tile_activated_cb), app_data);
1271
 
 
1272
 
        /* Note that this will handle the case of the action being launched via the side panel as
1273
 
           well as directly from the context menu of an individual launcher, because they both
1274
 
           funnel through tile_button_action_activate.
1275
 
        */
1276
 
        g_signal_connect (launcher, "tile-action-triggered",
1277
 
                G_CALLBACK (handle_menu_action_performed), app_data);
1278
 
 
1279
 
        /* These will be inserted/removed from tables as the filter changes and we dont want them */
1280
 
        /* destroyed when they are removed */
1281
 
        g_object_ref (launcher);
1282
 
 
1283
 
        /* use alphabetical order instead of the gmenu order. We group all sub items in each top level
1284
 
        category together, ignoring sub menus, so we also ignore sub menu layout hints */
1285
 
        cat_data->launcher_list =
1286
 
                /* g_list_insert (cat_data->launcher_list, launcher, -1); */
1287
 
                g_list_insert_sorted (cat_data->launcher_list, launcher, application_launcher_compare);
1288
 
        cat_data->filtered_launcher_list =
1289
 
                /* g_list_insert (cat_data->filtered_launcher_list, launcher, -1); */
1290
 
                g_list_insert_sorted (cat_data->filtered_launcher_list, launcher, application_launcher_compare);
1291
 
}
1292
 
 
1293
 
static gint
1294
 
application_launcher_compare (gconstpointer a, gconstpointer b)
1295
 
{
1296
 
        ApplicationTile *launcher1 = APPLICATION_TILE (a);
1297
 
        ApplicationTile *launcher2 = APPLICATION_TILE (b);
1298
 
 
1299
 
        gchar *val1 = launcher1->name;
1300
 
        gchar *val2 = launcher2->name;
1301
 
 
1302
 
        if (val1 == NULL || val2 == NULL)
1303
 
        {
1304
 
                g_assert_not_reached ();
1305
 
        }
1306
 
        return g_ascii_strcasecmp (val1, val2);
1307
 
}
1308
 
 
1309
 
static void
1310
 
application_launcher_clear_search_bar (AppShellData * app_data)
1311
 
{
1312
 
        SlabSection *section = SLAB_SECTION (app_data->filter_section);
1313
 
        NldSearchBar *search_bar;
1314
 
        g_assert (NLD_IS_SEARCH_BAR (section->contents));
1315
 
        search_bar = NLD_SEARCH_BAR (section->contents);
1316
 
        nld_search_bar_set_text (search_bar, "", TRUE);
1317
 
}
1318
 
 
1319
 
/*
1320
 
static gint
1321
 
category_name_compare (gconstpointer a, gconstpointer b)
1322
 
{
1323
 
        CategoryData *data = (CategoryData *) a;
1324
 
        const gchar *category = b;
1325
 
 
1326
 
        if (category == NULL || data->category == NULL)
1327
 
        {
1328
 
                g_assert_not_reached ();
1329
 
        }
1330
 
        return g_ascii_strcasecmp (category, data->category);
1331
 
}
1332
 
*/
1333
 
 
1334
 
static void
1335
 
tile_activated_cb (Tile * tile, TileEvent * event, gpointer user_data)
1336
 
{
1337
 
        switch (event->type)
1338
 
        {
1339
 
        case TILE_EVENT_ACTIVATED_SINGLE_CLICK:
1340
 
        case TILE_EVENT_ACTIVATED_KEYBOARD:
1341
 
                handle_launcher_single_clicked (tile, user_data);
1342
 
                break;
1343
 
        default:
1344
 
                break;
1345
 
        }
1346
 
 
1347
 
}
1348
 
 
1349
 
static void
1350
 
handle_launcher_single_clicked (Tile * launcher, gpointer data)
1351
 
{
1352
 
        AppShellData *app_data = (AppShellData *) data;
1353
 
        gchar *gconf_key;
1354
 
 
1355
 
        tile_trigger_action (launcher, launcher->actions[APPLICATION_TILE_ACTION_START]);
1356
 
 
1357
 
        gconf_key = g_strdup_printf ("%s%s", app_data->gconf_prefix, EXIT_SHELL_ON_ACTION_START);
1358
 
        if (get_slab_gconf_bool (gconf_key))
1359
 
        {
1360
 
                if (app_data->exit_on_close)
1361
 
                        gtk_main_quit ();
1362
 
                else
1363
 
                        hide_shell (app_data);
1364
 
        }
1365
 
        g_free (gconf_key);
1366
 
}
1367
 
 
1368
 
static void
1369
 
handle_menu_action_performed (Tile * launcher, TileEvent * event, TileAction * action,
1370
 
        gpointer data)
1371
 
{
1372
 
        AppShellData *app_data = (AppShellData *) data;
1373
 
        gchar *temp;
1374
 
 
1375
 
        temp = NULL;
1376
 
        if (action == launcher->actions[APPLICATION_TILE_ACTION_START])
1377
 
        {
1378
 
                temp = g_strdup_printf ("%s%s", app_data->gconf_prefix, EXIT_SHELL_ON_ACTION_START);
1379
 
        }
1380
 
 
1381
 
        else if (action == launcher->actions[APPLICATION_TILE_ACTION_HELP])
1382
 
        {
1383
 
                temp = g_strdup_printf ("%s%s", app_data->gconf_prefix, EXIT_SHELL_ON_ACTION_HELP);
1384
 
        }
1385
 
 
1386
 
        else if (action == launcher->actions[APPLICATION_TILE_ACTION_UPDATE_MAIN_MENU]
1387
 
                || action == launcher->actions[APPLICATION_TILE_ACTION_UPDATE_STARTUP])
1388
 
        {
1389
 
                temp = g_strdup_printf ("%s%s", app_data->gconf_prefix,
1390
 
                        EXIT_SHELL_ON_ACTION_ADD_REMOVE);
1391
 
        }
1392
 
 
1393
 
        else if (action == launcher->actions[APPLICATION_TILE_ACTION_UPGRADE_PACKAGE]
1394
 
                || action == launcher->actions[APPLICATION_TILE_ACTION_UNINSTALL_PACKAGE])
1395
 
        {
1396
 
                temp = g_strdup_printf ("%s%s", app_data->gconf_prefix,
1397
 
                        EXIT_SHELL_ON_ACTION_UPGRADE_UNINSTALL);
1398
 
        }
1399
 
 
1400
 
        if (temp)
1401
 
        {
1402
 
                if (get_slab_gconf_bool (temp))
1403
 
                {
1404
 
                        if (app_data->exit_on_close)
1405
 
                                gtk_main_quit ();
1406
 
                        else
1407
 
                                hide_shell (app_data);
1408
 
                }
1409
 
                g_free (temp);
1410
 
        }
1411
 
        else
1412
 
                g_warning ("Unknown Action");
1413
 
}