~ubuntu-branches/ubuntu/natty/unity-2d/natty-updates

« back to all changes in this revision

Viewing changes to .pc/debian-changes-0.1-0ubuntu3/launcher/UnityApplications/launcherapplication.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Oliver Grawert
  • Date: 2011-01-21 13:11:45 UTC
  • Revision ID: james.westby@ubuntu.com-20110121131145-tn95f1z7mxi4n1bx
Tags: 0.1-0ubuntu4
* add Vcs-Bzr location to debian/control
* update branch location in debian/copyright
* pull in some upstream fixes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2010 Canonical, Ltd.
3
 
 *
4
 
 * This program is free software; you can redistribute it and/or modify
5
 
 * it under the terms of the GNU General Public License as published by
6
 
 * the Free Software Foundation; version 3.
7
 
 *
8
 
 * This program is distributed in the hope that it will be useful,
9
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
 * GNU General Public License for more details.
12
 
 *
13
 
 * You should have received a copy of the GNU General Public License
14
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 */
16
 
 
17
 
/* Those have to be included before any QObject-style header to avoid
18
 
   compilation errors. */
19
 
#include <gdk/gdk.h>
20
 
 
21
 
/* Required otherwise using wnck_set_client_type breaks linking with error:
22
 
   undefined reference to `wnck_set_client_type(WnckClientType)'
23
 
*/
24
 
extern "C" {
25
 
#include <libwnck/libwnck.h>
26
 
#include <libwnck/util.h>
27
 
}
28
 
 
29
 
#include "launcherapplication.h"
30
 
#include "launchermenu.h"
31
 
#include "bamf-matcher.h"
32
 
 
33
 
#include <X11/X.h>
34
 
 
35
 
#include <QDebug>
36
 
#include <QAction>
37
 
#include <QDBusInterface>
38
 
 
39
 
LauncherApplication::LauncherApplication() :
40
 
    m_application(NULL), m_appInfo(NULL), m_sticky(false), m_has_visible_window(false)
41
 
{
42
 
    /* Make sure wnck_set_client_type is called only once */
43
 
    static bool client_type_set = false;
44
 
    if(!client_type_set)
45
 
    {
46
 
        /* Critically important to set the client type to pager because wnck
47
 
           will pass that type over to the window manager through XEvents.
48
 
           Window managers tend to respect orders from pagers to the letter by
49
 
           for example bypassing focus stealing prevention.
50
 
           Compiz does exactly that in src/event.c:handleEvent(...) in the
51
 
           ClientMessage case (line 1702).
52
 
           Metacity has a similar policy in src/core/window.c:window_activate(...)
53
 
           (line 2951).
54
 
        */
55
 
        wnck_set_client_type(WNCK_CLIENT_TYPE_PAGER);
56
 
        client_type_set = true;
57
 
    }
58
 
 
59
 
    QObject::connect(&m_launching_timer, SIGNAL(timeout()), this, SLOT(onLaunchingTimeouted()));
60
 
}
61
 
 
62
 
LauncherApplication::LauncherApplication(const LauncherApplication& other) :
63
 
    m_application(NULL), m_appInfo(NULL)
64
 
{
65
 
    /* FIXME: a number of members are not copied over */
66
 
    QObject::connect(&m_launching_timer, SIGNAL(timeout()), this, SLOT(onLaunchingTimeouted()));
67
 
    if (other.m_application != NULL)
68
 
        setBamfApplication(other.m_application);
69
 
    m_priority = other.m_priority;
70
 
}
71
 
 
72
 
LauncherApplication::~LauncherApplication()
73
 
{
74
 
    if(m_application != NULL)
75
 
    {
76
 
        m_application = NULL;
77
 
    }
78
 
    if(m_appInfo != NULL)
79
 
    {
80
 
        m_appInfo = NULL;
81
 
    }
82
 
}
83
 
 
84
 
bool
85
 
LauncherApplication::active() const
86
 
{
87
 
    if(m_application != NULL)
88
 
        return m_application->active();
89
 
 
90
 
    return false;
91
 
}
92
 
 
93
 
bool
94
 
LauncherApplication::running() const
95
 
{
96
 
    if(m_application != NULL)
97
 
        return m_application->running();
98
 
 
99
 
    return false;
100
 
}
101
 
 
102
 
bool
103
 
LauncherApplication::urgent() const
104
 
{
105
 
    if(m_application != NULL)
106
 
        return m_application->urgent();
107
 
 
108
 
    return false;
109
 
}
110
 
 
111
 
bool
112
 
LauncherApplication::sticky() const
113
 
{
114
 
    return m_sticky;
115
 
}
116
 
 
117
 
QString
118
 
LauncherApplication::name() const
119
 
{
120
 
    if(m_application != NULL)
121
 
        return m_application->name();
122
 
 
123
 
    if(m_appInfo != NULL)
124
 
        return QString::fromUtf8(g_app_info_get_name((GAppInfo*)m_appInfo));
125
 
 
126
 
    return QString("");
127
 
}
128
 
 
129
 
QString
130
 
LauncherApplication::icon() const
131
 
{
132
 
    if(m_application != NULL)
133
 
        return m_application->icon();
134
 
 
135
 
    if(m_appInfo != NULL)
136
 
        return QString::fromUtf8(g_icon_to_string(g_app_info_get_icon((GAppInfo*)m_appInfo)));
137
 
 
138
 
    return QString("");
139
 
}
140
 
 
141
 
QString
142
 
LauncherApplication::application_type() const
143
 
{
144
 
    if(m_application != NULL)
145
 
        return m_application->application_type();
146
 
 
147
 
    return QString("");
148
 
}
149
 
 
150
 
QString
151
 
LauncherApplication::desktop_file() const
152
 
{
153
 
    if(m_application != NULL)
154
 
        return m_application->desktop_file();
155
 
 
156
 
    if(m_appInfo != NULL)
157
 
        return QString::fromUtf8(g_desktop_app_info_get_filename(m_appInfo));
158
 
 
159
 
    return QString("");
160
 
}
161
 
 
162
 
void
163
 
LauncherApplication::setSticky(bool sticky)
164
 
{
165
 
    if (sticky == m_sticky)
166
 
        return;
167
 
 
168
 
    m_sticky = sticky;
169
 
    emit stickyChanged(sticky);
170
 
}
171
 
 
172
 
void
173
 
LauncherApplication::setDesktopFile(QString desktop_file)
174
 
{
175
 
    QByteArray byte_array = desktop_file.toUtf8();
176
 
    gchar *file = byte_array.data();
177
 
 
178
 
    if(desktop_file.startsWith("/"))
179
 
    {
180
 
        /* It looks like a full path to a desktop file */
181
 
        m_appInfo = g_desktop_app_info_new_from_filename(file);
182
 
    }
183
 
    else
184
 
    {
185
 
        /* It might just be a desktop file name; let GIO look for the actual
186
 
           desktop file for us */
187
 
        m_appInfo = g_desktop_app_info_new(file);
188
 
    }
189
 
 
190
 
    /* Emit the Changed signal on all properties that can depend on m_appInfo
191
 
       m_application's properties take precedence over m_appInfo's
192
 
    */
193
 
    if(m_application == NULL && m_appInfo != NULL)
194
 
    {
195
 
        emit desktopFileChanged(desktop_file);
196
 
        emit nameChanged(name());
197
 
        emit iconChanged(icon());
198
 
    }
199
 
}
200
 
 
201
 
void
202
 
LauncherApplication::setBamfApplication(BamfApplication *application)
203
 
{
204
 
    if (application == NULL) {
205
 
        return;
206
 
    }
207
 
 
208
 
    m_application = application;
209
 
    setDesktopFile(application->desktop_file());
210
 
 
211
 
    QObject::connect(application, SIGNAL(ActiveChanged(bool)), this, SIGNAL(activeChanged(bool)));
212
 
 
213
 
    /* FIXME: a bug somewhere makes connecting to the Closed() signal not work even though
214
 
              the emit Closed() in bamf-view.cpp is reached. */
215
 
    /* Connect first the onBamfApplicationClosed slot, then the runningChanged
216
 
       signal, to avoid a race condition when an application is closed.
217
 
       See https://launchpad.net/bugs/634057 */
218
 
    QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SLOT(onBamfApplicationClosed(bool)));
219
 
    QObject::connect(application, SIGNAL(RunningChanged(bool)), this, SIGNAL(runningChanged(bool)));
220
 
    QObject::connect(application, SIGNAL(UrgentChanged(bool)), this, SIGNAL(urgentChanged(bool)));
221
 
    QObject::connect(application, SIGNAL(WindowAdded(BamfWindow*)), this, SLOT(updateHasVisibleWindow()));
222
 
    QObject::connect(application, SIGNAL(WindowRemoved(BamfWindow*)), this, SLOT(updateHasVisibleWindow()));
223
 
 
224
 
    connect(application, SIGNAL(WindowAdded(BamfWindow*)), SLOT(onWindowAdded(BamfWindow*)));
225
 
 
226
 
    updateBamfApplicationDependentProperties();
227
 
}
228
 
 
229
 
void
230
 
LauncherApplication::updateBamfApplicationDependentProperties()
231
 
{
232
 
    emit activeChanged(active());
233
 
    emit runningChanged(running());
234
 
    emit urgentChanged(urgent());
235
 
    emit nameChanged(name());
236
 
    emit iconChanged(icon());
237
 
    emit applicationTypeChanged(application_type());
238
 
    emit desktopFileChanged(desktop_file());
239
 
    m_launching_timer.stop();
240
 
    emit launchingChanged(launching());
241
 
    updateHasVisibleWindow();
242
 
}
243
 
 
244
 
void
245
 
LauncherApplication::onBamfApplicationClosed(bool running)
246
 
{
247
 
    if(running)
248
 
       return;
249
 
 
250
 
    m_application->disconnect(this);
251
 
    m_application = NULL;
252
 
    updateBamfApplicationDependentProperties();
253
 
    emit closed();
254
 
}
255
 
 
256
 
void
257
 
LauncherApplication::setIconGeometry(int x, int y, int width, int height, uint xid)
258
 
{
259
 
    if (m_application == NULL) {
260
 
        return;
261
 
    }
262
 
 
263
 
    QScopedPointer<BamfUintList> xids;
264
 
    if (xid == 0) {
265
 
        xids.reset(m_application->xids());
266
 
    } else {
267
 
        QList<uint> list;
268
 
        list.append(xid);
269
 
        xids.reset(new BamfUintList(list));
270
 
    }
271
 
    int size = xids->size();
272
 
    if (size < 1) {
273
 
        return;
274
 
    }
275
 
 
276
 
    WnckScreen* screen = wnck_screen_get_default();
277
 
    wnck_screen_force_update(screen);
278
 
 
279
 
    for (int i = 0; i < size; ++i) {
280
 
        WnckWindow* window = wnck_window_get(xids->at(i));
281
 
        wnck_window_set_icon_geometry(window, x, y, width, height);
282
 
    }
283
 
}
284
 
 
285
 
void
286
 
LauncherApplication::onWindowAdded(BamfWindow* window)
287
 
{
288
 
    windowAdded(window->xid());
289
 
}
290
 
 
291
 
int
292
 
LauncherApplication::priority() const
293
 
{
294
 
    return m_priority;
295
 
}
296
 
 
297
 
bool
298
 
LauncherApplication::launching() const
299
 
{
300
 
    return m_launching_timer.isActive();
301
 
}
302
 
 
303
 
void
304
 
LauncherApplication::updateHasVisibleWindow()
305
 
{
306
 
    bool prev = m_has_visible_window;
307
 
 
308
 
    if (m_application != NULL) {
309
 
        m_has_visible_window = QScopedPointer<BamfUintList>(m_application->xids())->size() > 0;
310
 
    } else {
311
 
        m_has_visible_window = false;
312
 
    }
313
 
    if (m_has_visible_window != prev)
314
 
        emit hasVisibleWindowChanged(m_has_visible_window);
315
 
}
316
 
 
317
 
bool
318
 
LauncherApplication::has_visible_window() const
319
 
{
320
 
    return m_has_visible_window;
321
 
}
322
 
 
323
 
void
324
 
LauncherApplication::activate()
325
 
{
326
 
    if (active())
327
 
    {
328
 
        spread();
329
 
    }
330
 
    else if (running() && has_visible_window())
331
 
    {
332
 
        show();
333
 
    }
334
 
    else
335
 
    {
336
 
        launch();
337
 
    }
338
 
}
339
 
 
340
 
bool
341
 
LauncherApplication::launch()
342
 
{
343
 
    if(m_appInfo == NULL) return false;
344
 
 
345
 
    GError* error = NULL;
346
 
    GdkAppLaunchContext *context;
347
 
    GTimeVal timeval;
348
 
 
349
 
    g_get_current_time (&timeval);
350
 
    context = gdk_app_launch_context_new();
351
 
    /* Using GDK_CURRENT_TIME doesn’t seem to work, launched windows
352
 
       sometimes don’t get focus (see https://launchpad.net/bugs/643616). */
353
 
    gdk_app_launch_context_set_timestamp(context, timeval.tv_sec);
354
 
 
355
 
    g_app_info_launch((GAppInfo*)m_appInfo, NULL, (GAppLaunchContext*)context, &error);
356
 
    g_object_unref(context);
357
 
 
358
 
    if (error != NULL)
359
 
    {
360
 
        qWarning() << "Failed to launch application:" << error->message;
361
 
        g_error_free(error);
362
 
        return false;
363
 
    }
364
 
 
365
 
    /* 'launching' property becomes true for a maximum of 8 seconds and becomes
366
 
       false as soon as the application is launched */
367
 
    m_launching_timer.setSingleShot(true);
368
 
    m_launching_timer.start(8000);
369
 
    emit launchingChanged(true);
370
 
 
371
 
    return true;
372
 
}
373
 
 
374
 
void
375
 
LauncherApplication::onLaunchingTimeouted()
376
 
{
377
 
    emit launchingChanged(false);
378
 
}
379
 
 
380
 
void
381
 
LauncherApplication::close()
382
 
{
383
 
    if (m_application == NULL)
384
 
        return;
385
 
 
386
 
    QScopedPointer<BamfUintList> xids(m_application->xids());
387
 
    int size = xids->size();
388
 
    if (size < 1)
389
 
        return;
390
 
 
391
 
    WnckScreen* screen = wnck_screen_get_default();
392
 
    wnck_screen_force_update(screen);
393
 
 
394
 
    for (int i = 0; i < size; ++i) {
395
 
        WnckWindow* window = wnck_window_get(xids->at(i));
396
 
        wnck_window_close(window, CurrentTime);
397
 
    }
398
 
}
399
 
 
400
 
void
401
 
LauncherApplication::show()
402
 
{
403
 
    if(m_application == NULL) {
404
 
        return;
405
 
    }
406
 
 
407
 
    QScopedPointer<BamfWindowList> windows(m_application->windows());
408
 
    int size = windows->size();
409
 
    if (size < 1) {
410
 
        return;
411
 
    }
412
 
 
413
 
    /* Pick the most important window.
414
 
       The primary criterion to determine the most important window is urgency.
415
 
       The secondary criterion is the last_active timestamp (the last time the
416
 
       window was activated). */
417
 
    BamfWindow* important = windows->at(0);
418
 
    for (int i = 0; i < size; ++i) {
419
 
        BamfWindow* current = windows->at(i);
420
 
        if (current->urgent() && !important->urgent()) {
421
 
            important = current;
422
 
        }
423
 
        else if (current->urgent() || !important->urgent()) {
424
 
            if (current->last_active() > important->last_active()) {
425
 
                important = current;
426
 
            }
427
 
        }
428
 
    }
429
 
 
430
 
    WnckScreen* screen = wnck_screen_get_default();
431
 
    wnck_screen_force_update(screen);
432
 
 
433
 
    WnckWindow* window = wnck_window_get(important->xid());
434
 
    showWindow(window);
435
 
}
436
 
 
437
 
void
438
 
LauncherApplication::showWindow(WnckWindow* window)
439
 
{
440
 
    WnckWorkspace* workspace = wnck_window_get_workspace(window);
441
 
 
442
 
    /* Using X.h's CurrentTime (= 0) */
443
 
    wnck_workspace_activate(workspace, CurrentTime);
444
 
 
445
 
    /* If the workspace contains a viewport then move the viewport so
446
 
       that the window is visible.
447
 
       Compiz for example uses only one workspace with a desktop larger
448
 
       than the screen size which means that a viewport is used to
449
 
       determine what part of the desktop is visible.
450
 
 
451
 
       Reference:
452
 
       http://standards.freedesktop.org/wm-spec/wm-spec-latest.html#largedesks
453
 
    */
454
 
    if (wnck_workspace_is_virtual(workspace)) {
455
 
        moveViewportToWindow(window);
456
 
    }
457
 
 
458
 
    /* Using X.h's CurrentTime (= 0) */
459
 
    wnck_window_activate(window, CurrentTime);
460
 
}
461
 
 
462
 
void
463
 
LauncherApplication::moveViewportToWindow(WnckWindow* window)
464
 
{
465
 
    WnckWorkspace* workspace = wnck_window_get_workspace(window);
466
 
    WnckScreen* screen = wnck_window_get_screen(window);
467
 
 
468
 
    int screen_width = wnck_screen_get_width(screen);
469
 
    int screen_height = wnck_screen_get_height(screen);
470
 
    int viewport_x = wnck_workspace_get_viewport_x(workspace);
471
 
    int viewport_y = wnck_workspace_get_viewport_y(workspace);
472
 
 
473
 
    int window_x, window_y, window_width, window_height;
474
 
    wnck_window_get_geometry(window, &window_x, &window_y,
475
 
                                     &window_width, &window_height);
476
 
 
477
 
    /* Compute the row and column of the "virtual workspace" that contains
478
 
       the window. A "virtual workspace" is a portion of the desktop of the
479
 
       size of the screen.
480
 
    */
481
 
    int viewport_column = (viewport_x + window_x) / screen_width;
482
 
    int viewport_row = (viewport_y + window_y) / screen_height;
483
 
 
484
 
    wnck_screen_move_viewport(screen, viewport_column * screen_width,
485
 
                                      viewport_row * screen_height);
486
 
}
487
 
 
488
 
void
489
 
LauncherApplication::spread()
490
 
{
491
 
    qDebug() << "Triggering spread via DBUS";
492
 
    QDBusInterface iface("com.canonical.Unity2d.Spread", "/Spread",
493
 
                         "com.canonical.Unity2d.Spread");
494
 
    iface.call("SpreadApplicationWindows", m_application->xids()->at(0));
495
 
}
496
 
 
497
 
void
498
 
LauncherApplication::createMenuActions()
499
 
{
500
 
    bool is_running = running();
501
 
 
502
 
    /* Only applications with a corresponding desktop file can be kept in the launcher */
503
 
    if (!desktop_file().isEmpty()) {
504
 
        QAction* keep = new QAction(m_menu);
505
 
        keep->setCheckable(is_running);
506
 
        keep->setChecked(sticky());
507
 
        keep->setText(is_running ? tr("Keep In Launcher") : tr("Remove From Launcher"));
508
 
        m_menu->addAction(keep);
509
 
        QObject::connect(keep, SIGNAL(triggered()), this, SLOT(onKeepTriggered()));
510
 
    }
511
 
 
512
 
    if (is_running)
513
 
    {
514
 
        QAction* quit = new QAction(m_menu);
515
 
        quit->setText(tr("Quit"));
516
 
        m_menu->addAction(quit);
517
 
        QObject::connect(quit, SIGNAL(triggered()), this, SLOT(onQuitTriggered()));
518
 
    }
519
 
}
520
 
 
521
 
void
522
 
LauncherApplication::onKeepTriggered()
523
 
{
524
 
    QAction* keep = static_cast<QAction*>(sender());
525
 
    bool sticky = keep->isChecked();
526
 
    m_menu->hide();
527
 
    setSticky(sticky);
528
 
}
529
 
 
530
 
void
531
 
LauncherApplication::onQuitTriggered()
532
 
{
533
 
    m_menu->hide();
534
 
    close();
535
 
}
536