~unity-team/unity/trusty-1374785

« back to all changes in this revision

Viewing changes to src/quicklauncher/application_view.vala

  • Committer: Neil Jagdish Patel
  • Date: 2009-12-14 22:44:07 UTC
  • mfrom: (43.1.10 trunk.fix-make)
  • Revision ID: neil.patel@canonical.com-20091214224407-ez2goxv2nf4ipmr4
[build] IT MAKE DISTS!

removed:
  src/places/Makefile.am
  src/quicklauncher/Makefile.am
  src/quicklauncher/application_view.vala
added:
  tests/test-const.vapi
renamed:
  src/places/bar_model.vala => src/places/bar-model.vala
  src/places/bar_view.vala => src/places/bar-view.vala
  src/places/default_model.vala => src/places/default-model.vala
  src/places/default_view.vala => src/places/default-view.vala
  src/quicklauncher/application_store.vala => src/quicklauncher/application-store.vala
  src/quicklauncher/launcher_store.vala => src/quicklauncher/launcher-store.vala
  src/quicklauncher/launcher_view.vala => src/quicklauncher/launcher-view.vala
  src/quicklauncher/prism_handler.vala => src/quicklauncher/prism-handler.vala
  src/quicklauncher/quicklauncher_manager.vala => src/quicklauncher/quicklauncher-manager.vala
modified:
  .bzrignore
  AUTHORS
  Makefile.am
  configure.ac
  libunity/Makefile.am
  src/Makefile.am
  src/quicklauncher/unity-scroller.vala
  tests/Makefile.am
  tests/test-unity.vala
  src/quicklauncher/application-store.vala
unknown:
  unity-0.1.5.tar.gz
pending merges:
  Neil Jagdish Patel 2009-12-14 [build] Update authors
    Neil Jagdish Patel 2009-12-14 [tests] Make sure EXTRA_DIST is set and......
    Neil Jagdish Patel 2009-12-14 [tests] Fix Quicklauncher test. Fix path t...
    Neil Jagdish Patel 2009-12-14 [build] Actuall fix make distcheck
    Neil Jagdish Patel 2009-12-14 [build] Enable localinstall for testing du...
    Neil Jagdish Patel 2009-12-14 [build] Some more fixes for distcheck
    Neil Jagdish Patel 2009-12-14 [build] Fix up remaining bits, make distch...
    Neil Jagdish Patel 2009-12-14 [ignores] Update
    Neil Jagdish Patel 2009-12-14 [build] Clean up build and make make dist ...
    Neil Jagdish Patel 2009-12-14 Fix warnings

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
2
 
/*
3
 
 * Copyright (C) 2009 Canonical Ltd
4
 
 *
5
 
 * This program is free software: you can redistribute it and/or modify
6
 
 * it under the terms of the GNU General Public License version 3 as
7
 
 * published by the Free Software Foundation.
8
 
 *
9
 
 * This program is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 
 * GNU General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU General Public License
15
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 
 *
17
 
 * Authored by Gordon Allott <gord.allott@canonical.com>
18
 
 *
19
 
 */
20
 
 
21
 
namespace Unity.Quicklauncher
22
 
{  
23
 
  const string THROBBER_FILE = Unity.PKGDATADIR 
24
 
    + "/quicklauncher_spinner.png";
25
 
  const string FOCUSED_FILE  = Unity.PKGDATADIR 
26
 
    + "/quicklauncher_focused_indicator.svg";
27
 
  const string RUNNING_FILE  = Unity.PKGDATADIR 
28
 
    + "/quicklauncher_running_indicator.svg";
29
 
  const string HONEYCOMB_MASK_FILE = Unity.PKGDATADIR 
30
 
    + "/honeycomb-mask.png";
31
 
 
32
 
  public class ApplicationView : Ctk.Bin
33
 
  {
34
 
    
35
 
    public Launcher.Application app;
36
 
    private Ctk.Image icon;
37
 
    private Ctk.EffectGlow icon_glow_effect;
38
 
    private Ctk.EffectDropShadow icon_dropshadow_effect;
39
 
    private bool _is_sticky;
40
 
    private Clutter.Group container;
41
 
    private Ctk.Image throbber;
42
 
    private Ctk.Image focused_indicator;
43
 
    private Ctk.Image running_indicator;
44
 
    private Gdk.Pixbuf honeycomb_mask;
45
 
    public Unity.TooltipManager.Tooltip  tooltip;
46
 
    
47
 
    private uint32 last_pressed_time;
48
 
 
49
 
    private bool _busy;
50
 
    private bool is_starting {
51
 
      get { return _busy; }
52
 
      set {
53
 
        if (value)
54
 
        {
55
 
          if (!_busy)
56
 
            throbber_start ();
57
 
          } else {
58
 
            throbber_hide ();
59
 
          }
60
 
        _busy = value;
61
 
      }
62
 
    }
63
 
 
64
 
    private Clutter.Animation anim_throbber;
65
 
 
66
 
    private void throbber_start ()
67
 
    {
68
 
      if (anim_throbber != null)
69
 
        anim_throbber.completed ();
70
 
 
71
 
      this.throbber.opacity = 255;
72
 
      this.throbber.set_z_rotation_from_gravity (0.0f, Clutter.Gravity.CENTER);
73
 
      this.anim_throbber = this.throbber.animate (
74
 
        Clutter.AnimationMode.LINEAR, 1200,
75
 
        "rotation-angle-z", 360.0f
76
 
        );
77
 
 
78
 
      this.anim_throbber.loop = true;
79
 
      GLib.Timeout.add_seconds (15, on_launch_timeout);
80
 
    }
81
 
    
82
 
    private void throbber_fadeout ()
83
 
    {
84
 
      if (this.throbber.opacity == 0)
85
 
        return;
86
 
      
87
 
      if (anim_throbber != null)
88
 
        anim_throbber.completed ();
89
 
      
90
 
      this.anim_throbber =
91
 
      this.throbber.animate (Clutter.AnimationMode.EASE_IN_QUAD,
92
 
                             200, 
93
 
                             "opacity", 
94
 
                             0);
95
 
      this.anim_throbber.loop = false;
96
 
    }
97
 
    
98
 
    private void throbber_hide ()
99
 
    {
100
 
      if (this.throbber.opacity > 0)
101
 
        throbber_fadeout ();
102
 
    }
103
 
    
104
 
    /* animation support */
105
 
    private Clutter.Animation _anim;
106
 
    public Clutter.Animation anim { 
107
 
      get { return _anim; }
108
 
      set {
109
 
        if (_anim != null) { 
110
 
          assert (_anim is Clutter.Animation);
111
 
          _anim.completed();
112
 
        }
113
 
        _anim = value;
114
 
      }
115
 
    }
116
 
    
117
 
    private Clutter.Animation hover_anim;
118
 
    
119
 
    /* if we are not sticky anymore and we are not running, request remove */
120
 
    public bool is_sticky {
121
 
      get { return _is_sticky; }
122
 
      set {
123
 
        if (value == false && !is_running) 
124
 
          this.request_remove (this);
125
 
        _is_sticky = value;
126
 
      }
127
 
    }
128
 
    
129
 
    public bool is_running {
130
 
      get { return app.running; }
131
 
    }
132
 
    
133
 
    public bool is_hovering = false;
134
 
    
135
 
    /**
136
 
     * signal is called when the application is not marked as sticky and 
137
 
     * it is not running
138
 
     */
139
 
    public signal void request_remove (ApplicationView app);
140
 
    public signal void request_attention (ApplicationView app);
141
 
    public signal void clicked (ApplicationView app);
142
 
 
143
 
    public ApplicationView (Launcher.Application app)
144
 
    {
145
 
      /* This is a 'view' for a launcher application object
146
 
       * it will (hopefully) be able to launch, track when this application 
147
 
       * opens and closes and be able to get desktop file info
148
 
       */
149
 
      this.app = app;
150
 
      generate_view_from_app ();
151
 
      this.app.opened.connect(this.on_app_opened);
152
 
      this.app.closed.connect(this.on_app_closed);
153
 
 
154
 
      this.tooltip = Unity.TooltipManager.get_default().create (this.app.name);
155
 
 
156
 
      this.app.notify["running"].connect (this.notify_on_is_running);
157
 
      this.app.notify["focused"].connect (this.notify_on_is_focused);
158
 
      notify_on_is_running ();
159
 
      notify_on_is_focused ();
160
 
    }
161
 
 
162
 
    construct 
163
 
    {
164
 
      this.container = new Clutter.Group ();
165
 
      add_actor (this.container);
166
 
 
167
 
      this.icon = new Ctk.Image (42);
168
 
      this.container.add_actor(this.icon);
169
 
 
170
 
      load_textures ();
171
 
        
172
 
      button_press_event.connect (this.on_pressed);
173
 
      button_release_event.connect (this.on_released);
174
 
      /* hook up glow for enter/leave events */
175
 
      enter_event.connect(this.on_mouse_enter);
176
 
      leave_event.connect(this.on_mouse_leave);
177
 
 
178
 
      /* hook up tooltip for enter/leave events */
179
 
      this.icon.enter_event.connect (this.on_enter);
180
 
      this.icon.leave_event.connect (this.on_leave);
181
 
      this.icon.set_reactive (true);
182
 
 
183
 
      this.clicked.connect (this.on_clicked);
184
 
      
185
 
      icon_dropshadow_effect = new Ctk.EffectDropShadow(3, 5, 5);
186
 
      this.icon.add_effect(icon_dropshadow_effect);
187
 
      this.icon.queue_relayout();
188
 
      
189
 
      set_reactive(true);
190
 
    }
191
 
 
192
 
    private void load_textures ()
193
 
    {      
194
 
      this.throbber = new Ctk.Image.from_filename (20, THROBBER_FILE);
195
 
      this.throbber.set_z_rotation_from_gravity (0.0f, Clutter.Gravity.CENTER);
196
 
      this.container.add_actor (this.throbber);
197
 
                                
198
 
      this.focused_indicator = new Ctk.Image.from_filename (8, FOCUSED_FILE);
199
 
      this.container.add_actor (this.focused_indicator);
200
 
 
201
 
      this.running_indicator = new Ctk.Image.from_filename (8, RUNNING_FILE);
202
 
      this.container.add_actor (this.running_indicator);
203
 
      
204
 
      this.throbber.set_opacity (0);
205
 
      this.focused_indicator.set_opacity (0);
206
 
      this.running_indicator.set_opacity (0);
207
 
      
208
 
      this.honeycomb_mask = new Gdk.Pixbuf.from_file(HONEYCOMB_MASK_FILE);
209
 
    
210
 
      relayout ();
211
 
    }
212
 
 
213
 
    /**
214
 
     * re-layouts the various indicators and throbbers in out view 
215
 
     */
216
 
    private void relayout ()
217
 
    {
218
 
      this.throbber.set_position (this.container.width - this.throbber.width,
219
 
                                  this.container.height - this.throbber.height);
220
 
 
221
 
      float mid_point_y = this.container.height / 2.0f;
222
 
      float focus_halfy = this.focused_indicator.height / 2.0f;
223
 
      float focus_halfx = this.container.width - this.focused_indicator.width;
224
 
 
225
 
      this.focused_indicator.set_position(focus_halfx + 12, 
226
 
                                          mid_point_y - focus_halfy);
227
 
      this.running_indicator.set_position (0, mid_point_y - focus_halfy);
228
 
 
229
 
      this.icon.set_position (6, 0);
230
 
    }
231
 
    
232
 
    /** 
233
 
     * taken from the prototype code and shamelessly stolen from
234
 
     * netbook launcher. needs to be improved at some point to deal
235
 
     * with all cases, it will miss some apps at the moment
236
 
     */
237
 
    static Gdk.Pixbuf make_icon(string? icon_name) 
238
 
    {
239
 
      /*
240
 
       * This code somehow manages to miss a lot of icon names 
241
 
       * (non found icons are replaced with stock missing image icons)
242
 
       * which is a little strange as I ported this code fron netbook launcher
243
 
       * pixbuf-cache.c i think, If anyone else has a better idea for this then
244
 
       * please give it a go. otherwise i will revisit this code the last week 
245
 
       * of the month sprint
246
 
       */
247
 
      Gdk.Pixbuf pixbuf;
248
 
      Gtk.IconTheme theme = Gtk.IconTheme.get_default ();
249
 
      
250
 
      if (icon_name == null)
251
 
        {
252
 
          pixbuf = theme.load_icon(Gtk.STOCK_MISSING_IMAGE, 42, 0);
253
 
          return pixbuf;
254
 
        }
255
 
        
256
 
      if (icon_name.has_prefix("file://")) 
257
 
        {
258
 
          string filename = "";
259
 
          /* this try/catch sort of isn't needed... but it makes valac stop 
260
 
           * printing warning messages
261
 
           */
262
 
          try 
263
 
          {
264
 
            filename = Filename.from_uri(icon_name);
265
 
          } 
266
 
          catch (GLib.ConvertError e)
267
 
          {
268
 
          }
269
 
          if (filename != "") 
270
 
            {
271
 
              pixbuf = new Gdk.Pixbuf.from_file_at_scale(icon_name, 
272
 
                                                         42, 42, true);
273
 
              if (pixbuf is Gdk.Pixbuf)
274
 
                  return pixbuf;
275
 
            }
276
 
        }
277
 
      
278
 
      if (Path.is_absolute(icon_name))
279
 
        {
280
 
          if (FileUtils.test(icon_name, FileTest.EXISTS)) 
281
 
            {
282
 
              pixbuf = new Gdk.Pixbuf.from_file_at_scale(icon_name, 
283
 
                                                         42, 42, true);
284
 
 
285
 
              if (pixbuf is Gdk.Pixbuf)
286
 
                return pixbuf;
287
 
            }
288
 
        }
289
 
 
290
 
      if (FileUtils.test ("/usr/share/pixmaps/" + icon_name, 
291
 
                          FileTest.IS_REGULAR))
292
 
        {
293
 
          pixbuf = new Gdk.Pixbuf.from_file_at_scale (
294
 
            "/usr/share/pixmaps/" + icon_name, 42, 42, true
295
 
            );
296
 
          
297
 
          if (pixbuf is Gdk.Pixbuf)
298
 
            return pixbuf;
299
 
        }
300
 
      
301
 
      Gtk.IconInfo info = theme.lookup_icon(icon_name, 42, 0);
302
 
      if (info != null) 
303
 
        {
304
 
          string filename = info.get_filename();
305
 
          if (FileUtils.test(filename, FileTest.EXISTS)) 
306
 
            {
307
 
              pixbuf = new Gdk.Pixbuf.from_file_at_scale(filename, 
308
 
                                                         42, 42, true);
309
 
            
310
 
              if (pixbuf is Gdk.Pixbuf)
311
 
                return pixbuf;
312
 
            }
313
 
        }
314
 
      
315
 
      try 
316
 
      {
317
 
        pixbuf = theme.load_icon(icon_name, 42, Gtk.IconLookupFlags.FORCE_SVG);
318
 
      }
319
 
      catch (GLib.Error e) 
320
 
      {
321
 
        warning ("could not load icon for %s - %s", icon_name, e.message);
322
 
        pixbuf = theme.load_icon(Gtk.STOCK_MISSING_IMAGE, 42, 0);
323
 
        return pixbuf;
324
 
      }
325
 
      return pixbuf;
326
 
          
327
 
    }
328
 
    
329
 
    /**
330
 
     * goes though our launcher application and generates icons and such
331
 
     */
332
 
    private void generate_view_from_app ()
333
 
    {
334
 
      var pixbuf = make_icon (app.icon_name);
335
 
      this.icon.set_from_pixbuf (pixbuf);
336
 
    }
337
 
  
338
 
    private void on_app_opened (Wnck.Application app) 
339
 
    {
340
 
      this.is_starting = false;
341
 
    }
342
 
 
343
 
    private void on_app_closed (Wnck.Application app) 
344
 
    {
345
 
      this.is_starting = false;
346
 
      if (!this.is_running && !this.is_sticky) 
347
 
        this.request_remove (this);
348
 
    }
349
 
 
350
 
    private bool on_enter (Clutter.Event event)
351
 
    {
352
 
      float xx, xy;
353
 
 
354
 
      get_transformed_position (out xx, out xy);
355
 
      xx += width + 4;
356
 
      /* TODO: 30 is the default tooltip height */
357
 
      xy += (height - 30) / 2;
358
 
      Unity.TooltipManager.get_default().show (this.tooltip, (int) xx, (int) xy);
359
 
      return false;
360
 
    } 
361
 
 
362
 
    private void on_clicked (ApplicationView appview)
363
 
    {
364
 
      /* hide the tooltip, to prevent it from stealing focus when
365
 
         the app starts and is focused */
366
 
      Unity.TooltipManager.get_default().hide (this.tooltip);
367
 
      
368
 
      if (is_starting)
369
 
      {
370
 
        return;
371
 
      }
372
 
 
373
 
      if (app.running)
374
 
      {
375
 
        // we only want to switch to the application, not launch it
376
 
        app.show ();
377
 
      }
378
 
      else 
379
 
      {
380
 
        this.is_starting = true;
381
 
        try 
382
 
        {
383
 
          app.launch ();
384
 
        } 
385
 
        catch (GLib.Error e)
386
 
        {
387
 
          critical ("could not launch application %s: %s", 
388
 
                    this.name, 
389
 
                    e.message);
390
 
          this.is_starting = false;
391
 
        }
392
 
      }
393
 
    }
394
 
 
395
 
    private bool on_leave (Clutter.Event event)
396
 
    {
397
 
      Unity.TooltipManager.get_default().hide (this.tooltip);
398
 
      return false;
399
 
    }
400
 
 
401
 
    private bool on_pressed(Clutter.Event src) 
402
 
    {
403
 
      var bevent = src.button;
404
 
      if (bevent.button == 1)
405
 
      {
406
 
        last_pressed_time = bevent.time;
407
 
      }      
408
 
      return false;
409
 
    }
410
 
 
411
 
    private bool on_released (Clutter.Event src)
412
 
    {
413
 
      var bevent = src.button;
414
 
      if (bevent.button == 1 &&
415
 
          (bevent.time - last_pressed_time) < 500)
416
 
      {
417
 
        this.clicked (this);
418
 
      }
419
 
      return false;
420
 
    }
421
 
    
422
 
    private bool on_mouse_enter(Clutter.Event src) 
423
 
    {
424
 
      this.is_hovering = true;
425
 
      icon_glow_effect = new Ctk.EffectGlow();
426
 
      Clutter.Color c = Clutter.Color() {
427
 
        red = 255,
428
 
        green = 255,
429
 
        blue = 255,
430
 
        alpha = 255
431
 
      };
432
 
      icon_glow_effect.set_background_texture(honeycomb_mask);
433
 
      icon_glow_effect.set_color(c);
434
 
      icon_glow_effect.set_factor(1.0f);
435
 
      this.icon.add_effect(icon_glow_effect);
436
 
      this.icon.queue_relayout();
437
 
 
438
 
      this.hover_anim = icon_glow_effect.animate (
439
 
        Clutter.AnimationMode.EASE_IN_OUT_SINE, 600, "factor", 0.0f);
440
 
      this.hover_anim.completed.connect (on_hover_anim_completed);
441
 
 
442
 
      return false;
443
 
    }
444
 
    
445
 
    private bool on_launch_timeout ()
446
 
    {
447
 
      this.is_starting = false;
448
 
 
449
 
      return false;
450
 
    }
451
 
 
452
 
    private void on_hover_anim_completed ()
453
 
    {
454
 
      if (is_hovering)
455
 
      {
456
 
        Idle.add (do_new_anim);
457
 
      }
458
 
    }
459
 
 
460
 
    private bool do_new_anim ()
461
 
    {
462
 
      float fadeto = 0.0f;
463
 
      if (icon_glow_effect.get_factor() <= 0.0f)
464
 
        fadeto = 1.0f;
465
 
      
466
 
      this.hover_anim = icon_glow_effect.animate(
467
 
        Clutter.AnimationMode.EASE_IN_OUT_CIRC, 600, "factor", fadeto);
468
 
      this.hover_anim.completed.connect (on_hover_anim_completed);
469
 
      return false;
470
 
    }
471
 
    
472
 
    
473
 
    private bool on_mouse_leave(Clutter.Event src) 
474
 
    {
475
 
      if (this.is_hovering)
476
 
        {
477
 
          this.is_hovering = false;
478
 
          this.hover_anim.completed ();
479
 
          this.icon.remove_effect(this.icon_glow_effect);
480
 
          this.icon.queue_relayout();
481
 
          print ("stopped animation");
482
 
          return true;
483
 
        }
484
 
 
485
 
      return false;
486
 
    }
487
 
    
488
 
    /**
489
 
     * if the application is not running anymore and we are not sticky, we
490
 
     * request to be removed
491
 
     */
492
 
    private void notify_on_is_running ()
493
 
    {
494
 
      this.is_starting = false;
495
 
      
496
 
      /* we need to show the running indicator when we are running */
497
 
      if (this.is_running)
498
 
      {
499
 
        this.running_indicator.set_opacity (255);
500
 
      }
501
 
      else
502
 
      {
503
 
        this.running_indicator.set_opacity (0);
504
 
        this.focused_indicator.set_opacity (0);
505
 
      }       
506
 
      
507
 
      if (!this.is_running && !this.is_sticky)
508
 
      {
509
 
        this.request_remove (this);
510
 
      }
511
 
    }
512
 
    
513
 
    private void notify_on_is_focused ()
514
 
    {
515
 
      if (app.focused) {
516
 
        this.focused_indicator.set_opacity (255);
517
 
        this.request_attention (this);
518
 
      }
519
 
      else
520
 
        this.focused_indicator.set_opacity (0);
521
 
      
522
 
      this.is_starting = false;
523
 
    }
524
 
 
525
 
  }
526
 
 
527
 
}