~ubuntu-branches/ubuntu/trusty/gnome-shell/trusty-proposed

« back to all changes in this revision

Viewing changes to src/shell-app.c

Tags: upstream-3.3.90
ImportĀ upstreamĀ versionĀ 3.3.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
#include "shell-app-system-private.h"
16
16
#include "shell-window-tracker-private.h"
17
17
#include "st.h"
 
18
#include "gactionmuxer.h"
18
19
 
19
20
typedef enum {
20
21
  MATCH_NONE,
30
31
typedef struct {
31
32
  guint refcount;
32
33
 
33
 
  /* Last time the user interacted with any of this application's windows */
34
 
  guint32 last_user_time;
35
 
 
36
34
  /* Signal connection to dirty window sort list on workspace changes */
37
35
  guint workspace_switch_id;
38
36
 
40
38
 
41
39
  /* Whether or not we need to resort the windows; this is done on demand */
42
40
  gboolean window_sort_stale : 1;
 
41
 
 
42
  /* See GApplication documentation */
 
43
  GDBusMenuModel   *remote_menu;
 
44
  GActionMuxer     *muxer;
43
45
} ShellAppRunningState;
44
46
 
45
47
/**
73
75
  char *name_collation_key;
74
76
  char *casefolded_description;
75
77
  char *casefolded_exec;
 
78
  char **casefolded_keywords;
76
79
};
77
80
 
78
 
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT);
79
 
 
80
81
enum {
81
82
  PROP_0,
82
 
  PROP_STATE
 
83
  PROP_STATE,
 
84
  PROP_ID,
 
85
  PROP_DBUS_ID,
 
86
  PROP_ACTION_GROUP,
 
87
  PROP_MENU
83
88
};
84
89
 
85
90
enum {
90
95
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
91
96
 
92
97
static void create_running_state (ShellApp *app);
 
98
static void setup_running_state (ShellApp *app, MetaWindow *window);
93
99
static void unref_running_state (ShellAppRunningState *state);
94
100
 
 
101
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT)
 
102
 
95
103
static void
96
104
shell_app_get_property (GObject    *gobject,
97
105
                        guint       prop_id,
105
113
    case PROP_STATE:
106
114
      g_value_set_enum (value, app->state);
107
115
      break;
 
116
    case PROP_ID:
 
117
      g_value_set_string (value, shell_app_get_id (app));
 
118
      break;
 
119
    case PROP_ACTION_GROUP:
 
120
      if (app->running_state)
 
121
        g_value_set_object (value, app->running_state->muxer);
 
122
      break;
 
123
    case PROP_MENU:
 
124
      if (app->running_state)
 
125
        g_value_set_object (value, app->running_state->remote_menu);
 
126
      break;
108
127
    default:
109
128
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
110
129
      break;
509
528
        window = most_recent_transient;
510
529
 
511
530
 
512
 
      if (!shell_window_tracker_is_window_interesting (window))
513
 
        {
514
 
          /* We won't get notify::user-time signals for uninteresting windows,
515
 
           * which means that an app's last_user_time won't get updated.
516
 
           * Update it here instead.
517
 
           */
518
 
          app->running_state->last_user_time = timestamp;
519
 
        }
520
 
 
521
531
      if (active != workspace)
522
532
        meta_workspace_activate_with_focus (workspace, window, timestamp);
523
533
      else
525
535
    }
526
536
}
527
537
 
 
538
 
 
539
void
 
540
shell_app_update_window_actions (ShellApp *app, MetaWindow *window)
 
541
{
 
542
  const char *object_path;
 
543
 
 
544
  object_path = meta_window_get_gtk_window_object_path (window);
 
545
  if (object_path != NULL)
 
546
    {
 
547
      GActionGroup *actions;
 
548
 
 
549
      actions = g_object_get_data (G_OBJECT (window), "actions");
 
550
      if (actions == NULL)
 
551
        {
 
552
          actions = G_ACTION_GROUP (g_dbus_action_group_get (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
 
553
                                                             meta_window_get_gtk_unique_bus_name (window),
 
554
                                                             object_path));
 
555
          g_object_set_data_full (G_OBJECT (window), "actions", actions, g_object_unref);
 
556
        }
 
557
 
 
558
      if (!app->running_state->muxer)
 
559
        app->running_state->muxer = g_action_muxer_new ();
 
560
 
 
561
      g_action_muxer_insert (app->running_state->muxer, "win", actions);
 
562
      g_object_notify (G_OBJECT (app), "action-group");
 
563
    }
 
564
}
 
565
 
528
566
/**
529
567
 * shell_app_activate:
530
568
 * @app: a #ShellApp
752
790
  return FALSE;
753
791
}
754
792
 
 
793
static int
 
794
shell_app_get_last_user_time (ShellApp *app)
 
795
{
 
796
  GSList *iter;
 
797
  int last_user_time;
 
798
 
 
799
  last_user_time = 0;
 
800
 
 
801
  if (app->running_state != NULL)
 
802
    {
 
803
      for (iter = app->running_state->windows; iter; iter = iter->next)
 
804
        last_user_time = MAX (last_user_time, meta_window_get_user_time (iter->data));
 
805
    }
 
806
 
 
807
  return last_user_time;
 
808
}
 
809
 
755
810
/**
756
811
 * shell_app_compare:
757
812
 * @app:
791
846
        return -1;
792
847
      else if (!app->running_state->windows && other->running_state->windows)
793
848
        return 1;
794
 
      return other->running_state->last_user_time - app->running_state->last_user_time;
 
849
 
 
850
      return shell_app_get_last_user_time (other) - shell_app_get_last_user_time (app);
795
851
    }
796
852
 
797
853
  return 0;
871
927
{
872
928
  g_assert (app->running_state != NULL);
873
929
 
874
 
  app->running_state->last_user_time = meta_window_get_user_time (window);
875
 
 
876
930
  /* Ideally we don't want to emit windows-changed if the sort order
877
931
   * isn't actually changing. This check catches most of those.
878
932
   */
903
957
_shell_app_add_window (ShellApp        *app,
904
958
                       MetaWindow      *window)
905
959
{
906
 
  guint32 user_time;
907
 
 
908
960
  if (app->running_state && g_slist_find (app->running_state->windows, window))
909
961
    return;
910
962
 
918
970
  g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
919
971
  g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app);
920
972
 
921
 
  user_time = meta_window_get_user_time (window);
922
 
  if (user_time > app->running_state->last_user_time)
923
 
    app->running_state->last_user_time = user_time;
 
973
  setup_running_state (app, window);
924
974
 
925
975
  if (app->state != SHELL_APP_STATE_STARTING)
926
976
    shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
1081
1131
  gboolean ret;
1082
1132
  ShellGlobal *global;
1083
1133
  MetaScreen *screen;
 
1134
  GdkDisplay *gdisplay;
1084
1135
 
1085
1136
  if (startup_id)
1086
1137
    *startup_id = NULL;
1099
1150
 
1100
1151
  global = shell_global_get ();
1101
1152
  screen = shell_global_get_screen (global);
 
1153
  gdisplay = gdk_screen_get_display (shell_global_get_gdk_screen (global));
1102
1154
 
1103
1155
  if (timestamp == 0)
1104
1156
    timestamp = shell_global_get_current_time (global);
1106
1158
  if (workspace < 0)
1107
1159
    workspace = meta_screen_get_active_workspace_index (screen);
1108
1160
 
1109
 
  context = gdk_app_launch_context_new ();
 
1161
  context = gdk_display_get_app_launch_context (gdisplay);
1110
1162
  gdk_app_launch_context_set_timestamp (context, timestamp);
1111
1163
  gdk_app_launch_context_set_desktop (context, workspace);
1112
1164
 
1160
1212
  app->running_state->refcount = 1;
1161
1213
  app->running_state->workspace_switch_id =
1162
1214
    g_signal_connect (screen, "workspace-switched", G_CALLBACK(shell_app_on_ws_switch), app);
 
1215
 
 
1216
  app->running_state->muxer = g_action_muxer_new ();
 
1217
}
 
1218
 
 
1219
static void
 
1220
setup_running_state (ShellApp   *app,
 
1221
                     MetaWindow *window)
 
1222
{
 
1223
  /* We assume that 'gtk-unique-bus-name', gtk-application-object-path'
 
1224
   * and 'gtk-app-menu-object-path' are the same for all windows which
 
1225
   * have it set.
 
1226
   *
 
1227
   * It could be possible, however, that the first window we see
 
1228
   * belonging to the app didn't have them set.  For this reason, we
 
1229
   * take the values from the first window that has them set and ignore
 
1230
   * all the rest (until the app is stopped and restarted).
 
1231
   */
 
1232
 
 
1233
  if (app->running_state->remote_menu == NULL)
 
1234
    {
 
1235
      const gchar *application_object_path;
 
1236
      const gchar *app_menu_object_path;
 
1237
      const gchar *unique_bus_name;
 
1238
      GDBusConnection *session;
 
1239
      GDBusActionGroup *actions;
 
1240
 
 
1241
      application_object_path = meta_window_get_gtk_application_object_path (window);
 
1242
      app_menu_object_path = meta_window_get_gtk_app_menu_object_path (window);
 
1243
      unique_bus_name = meta_window_get_gtk_unique_bus_name (window);
 
1244
 
 
1245
      if (application_object_path == NULL || app_menu_object_path == NULL || unique_bus_name == NULL)
 
1246
        return;
 
1247
 
 
1248
      session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
 
1249
      g_assert (session != NULL);
 
1250
      app->running_state->remote_menu = g_dbus_menu_model_get (session, unique_bus_name, app_menu_object_path);
 
1251
      actions = g_dbus_action_group_get (session, unique_bus_name, application_object_path);
 
1252
      g_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions));
 
1253
      g_object_unref (actions);
 
1254
      g_object_unref (session);
 
1255
    }
1163
1256
}
1164
1257
 
1165
1258
static void
1167
1260
{
1168
1261
  MetaScreen *screen;
1169
1262
 
 
1263
  g_assert (state->refcount > 0);
 
1264
 
1170
1265
  state->refcount--;
1171
1266
  if (state->refcount > 0)
1172
1267
    return;
1173
1268
 
1174
1269
  screen = shell_global_get_screen (shell_global_get ());
1175
 
 
1176
1270
  g_signal_handler_disconnect (screen, state->workspace_switch_id);
 
1271
 
 
1272
  g_clear_object (&state->remote_menu);
 
1273
  g_clear_object (&state->muxer);
 
1274
 
1177
1275
  g_slice_free (ShellAppRunningState, state);
1178
1276
}
1179
1277
 
1202
1300
  const char *name;
1203
1301
  const char *exec;
1204
1302
  const char *comment;
 
1303
  const char * const *keywords;
1205
1304
  char *normalized_exec;
1206
1305
  GDesktopAppInfo *appinfo;
1207
1306
 
1216
1315
  normalized_exec = shell_util_normalize_and_casefold (exec);
1217
1316
  app->casefolded_exec = trim_exec_line (normalized_exec);
1218
1317
  g_free (normalized_exec);
 
1318
 
 
1319
  keywords = g_desktop_app_info_get_keywords (appinfo);
 
1320
 
 
1321
  if (keywords)
 
1322
    {
 
1323
      int i;
 
1324
 
 
1325
      app->casefolded_keywords = g_new0 (char*, g_strv_length ((char **)keywords) + 1);
 
1326
 
 
1327
      i = 0;
 
1328
      while (keywords[i])
 
1329
        {
 
1330
          app->casefolded_keywords[i] = shell_util_normalize_and_casefold (keywords[i]);
 
1331
          ++i;
 
1332
        }
 
1333
      app->casefolded_keywords[i] = NULL;
 
1334
    }
 
1335
  else
 
1336
    app->casefolded_keywords = NULL;
1219
1337
}
1220
1338
 
1221
1339
/**
1222
1340
 * shell_app_compare_by_name:
1223
 
 * @app:
1224
 
 * @other:
 
1341
 * @app: One app
 
1342
 * @other: The other app
1225
1343
 *
1226
1344
 * Order two applications by name.
1227
1345
 *
1228
 
 * Returns: -1, 0, or 1; suitable for use as a comparison function for e.g. g_slist_sort()
 
1346
 * Returns: -1, 0, or 1; suitable for use as a comparison function
 
1347
 * for e.g. g_slist_sort()
1229
1348
 */
1230
1349
int
1231
1350
shell_app_compare_by_name (ShellApp *app, ShellApp *other)
1283
1402
            current_match = MATCH_SUBSTRING;
1284
1403
        }
1285
1404
 
 
1405
      if (app->casefolded_keywords)
 
1406
        {
 
1407
          int i = 0;
 
1408
          while (app->casefolded_keywords[i] && current_match < MATCH_PREFIX)
 
1409
            {
 
1410
              p = strstr (app->casefolded_keywords[i], term);
 
1411
              if (p != NULL)
 
1412
                {
 
1413
                  if (p == app->casefolded_keywords[i])
 
1414
                    current_match = MATCH_PREFIX;
 
1415
                  else
 
1416
                    current_match = MATCH_SUBSTRING;
 
1417
                }
 
1418
              ++i;
 
1419
            }
 
1420
        }
 
1421
 
1286
1422
      if (current_match == MATCH_NONE)
1287
1423
        return current_match;
1288
1424
 
1348
1484
      while (app->running_state->windows)
1349
1485
        _shell_app_remove_window (app, app->running_state->windows->data);
1350
1486
    }
 
1487
  /* We should have been transitioned when we removed all of our windows */
 
1488
  g_assert (app->state == SHELL_APP_STATE_STOPPED);
 
1489
  g_assert (app->running_state == NULL);
1351
1490
 
1352
1491
  G_OBJECT_CLASS(shell_app_parent_class)->dispose (object);
1353
1492
}
1363
1502
  g_free (app->name_collation_key);
1364
1503
  g_free (app->casefolded_description);
1365
1504
  g_free (app->casefolded_exec);
 
1505
  g_strfreev (app->casefolded_keywords);
1366
1506
 
1367
1507
  G_OBJECT_CLASS(shell_app_parent_class)->finalize (object);
1368
1508
}
1380
1520
                                     SHELL_TYPE_APP,
1381
1521
                                     G_SIGNAL_RUN_LAST,
1382
1522
                                     0,
1383
 
                                     NULL, NULL,
1384
 
                                     g_cclosure_marshal_VOID__VOID,
 
1523
                                     NULL, NULL, NULL,
1385
1524
                                     G_TYPE_NONE, 0);
1386
1525
 
1387
1526
  /**
1398
1537
                                                      SHELL_TYPE_APP_STATE,
1399
1538
                                                      SHELL_APP_STATE_STOPPED,
1400
1539
                                                      G_PARAM_READABLE));
 
1540
 
 
1541
  /**
 
1542
   * ShellApp:id:
 
1543
   *
 
1544
   * The id of this application (a desktop filename, or a special string
 
1545
   * like window:0xabcd1234)
 
1546
   */
 
1547
  g_object_class_install_property (gobject_class,
 
1548
                                   PROP_ID,
 
1549
                                   g_param_spec_string ("id",
 
1550
                                                        "Application id",
 
1551
                                                        "The desktop file id of this ShellApp",
 
1552
                                                        NULL,
 
1553
                                                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
1554
 
 
1555
  /**
 
1556
   * ShellApp:action-group:
 
1557
   *
 
1558
   * The #GDBusActionGroup associated with this ShellApp, if any. See the
 
1559
   * documentation of #GApplication and #GActionGroup for details.
 
1560
   */
 
1561
  g_object_class_install_property (gobject_class,
 
1562
                                   PROP_ACTION_GROUP,
 
1563
                                   g_param_spec_object ("action-group",
 
1564
                                                        "Application Action Group",
 
1565
                                                        "The action group exported by the remote application",
 
1566
                                                        G_TYPE_ACTION_GROUP,
 
1567
                                                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
1568
  /**
 
1569
   * ShellApp:menu:
 
1570
   *
 
1571
   * The #GMenuProxy associated with this ShellApp, if any. See the
 
1572
   * documentation of #GMenuModel for details.
 
1573
   */
 
1574
  g_object_class_install_property (gobject_class,
 
1575
                                   PROP_MENU,
 
1576
                                   g_param_spec_object ("menu",
 
1577
                                                        "Application Menu",
 
1578
                                                        "The primary menu exported by the remote application",
 
1579
                                                        G_TYPE_MENU_MODEL,
 
1580
                                                        G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
1581
 
1401
1582
}