~robertcarr/bamf/ignore-shell-windows

« back to all changes in this revision

Viewing changes to WindowSwitcher/WindowSwitcher/SwitcherArea.cs

  • Committer: Jason Smith
  • Date: 2009-11-06 14:51:10 UTC
  • Revision ID: jason@t500-20091106145110-ogw9i0dz02vgf4p9
Kill window switcher

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
//  
2
 
//  Copyright (C) 2009 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, either version 3 of the License, or
7
 
//  (at your option) any later version.
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
 
 
18
 
using System;
19
 
using System.Collections.Generic;
20
 
using System.Linq;
21
 
 
22
 
using Cairo;
23
 
using Gdk;
24
 
using Gtk;
25
 
using Gnome;
26
 
using Wnck;
27
 
 
28
 
namespace WindowSwitcher
29
 
{
30
 
        
31
 
        
32
 
        public class SwitcherArea : Gtk.DrawingArea
33
 
        {
34
 
                class SwitcherItem
35
 
                {
36
 
                        public DesktopItem DesktopItem { get; private set; }
37
 
                        
38
 
                        public string DesktopFile { get; private set; }
39
 
 
40
 
                        IEnumerable<Wnck.Window> windows;
41
 
                        public IEnumerable<Wnck.Window> Windows {
42
 
                                get {
43
 
                                        return windows;
44
 
                                }
45
 
                        }
46
 
                        
47
 
                        public IEnumerable<Wnck.Window> VisibleWindows {
48
 
                                get { 
49
 
                                        return windows.Where (w => !w.IsSkipTasklist);
50
 
                                }
51
 
                        }
52
 
                        
53
 
                        public SwitcherItem (string desktopFile)
54
 
                        {
55
 
                                DesktopItem = Gnome.DesktopItem.NewFromFile (desktopFile, 0);
56
 
                                DesktopFile = desktopFile;
57
 
                                Build ();
58
 
                        }
59
 
                        
60
 
                        public SwitcherItem (DesktopItem item)
61
 
                        {
62
 
                                DesktopItem = item;
63
 
                                DesktopFile = item.Location;
64
 
                                if (DesktopFile.StartsWith ("file://"))
65
 
                                        DesktopFile = DesktopFile.Substring ("file://".Length);
66
 
                                Build ();
67
 
                        }
68
 
                        
69
 
                        void Build ()
70
 
                        {
71
 
                                List<Wnck.Window> stack = Wnck.Screen.Default.WindowsStacked.ToList ();
72
 
                                // to array for performance reasons
73
 
                                windows = LibWnckSync.Global.WindowsForDesktopFile (DesktopFile)
74
 
                                        .OrderByDescending (w => stack.IndexOf (w))
75
 
                                        .ToArray ();
76
 
                        }
77
 
                }
78
 
                
79
 
                const int IconSize = 64;
80
 
                const int BufferSize = 8;
81
 
                const int WindowItemHeight = 20;
82
 
                const int MinWidth = 300;
83
 
                
84
 
                Cairo.Color BackgroundColor = new Cairo.Color (0.7, 0.7, 0.7, .55);
85
 
                Cairo.Color HighlightColorTop = new Cairo.Color (0.2, 0.2, 0.2, .95);
86
 
                Cairo.Color HighlightColorBottom = new Cairo.Color (0, 0, 0, .95);
87
 
                Cairo.Color BorderColor = new Cairo.Color (.3, .3, .3, .7);
88
 
                
89
 
                Gdk.Pixbuf reflection;
90
 
                
91
 
                int WindowItemWidth {
92
 
                        get {
93
 
                                int fits = WidthRequest / MinWidth;
94
 
                                if (fits == 0)
95
 
                                        return MinWidth - 20;
96
 
                                return (WidthRequest / fits) - 20;
97
 
                        }
98
 
                }
99
 
                        
100
 
                int Columns {
101
 
                        get {
102
 
                                return WidthRequest / WindowItemWidth;
103
 
                        }
104
 
                }
105
 
                
106
 
                IEnumerable<SwitcherItem> SwitcherItems { get; set; }
107
 
                
108
 
                Wnck.Window current_window;
109
 
                
110
 
                Wnck.Window CurrentWindow { 
111
 
                        get { return current_window; }
112
 
                }
113
 
                
114
 
                SwitcherItem CurrentSwitcherItem {
115
 
                        get { 
116
 
                                return SwitcherItems
117
 
                                        .Where (si => si.Windows.Contains (current_window))
118
 
                                        .DefaultIfEmpty (null)
119
 
                                        .FirstOrDefault (); 
120
 
                        }
121
 
                }
122
 
                
123
 
                public SwitcherArea ()
124
 
                {
125
 
                        AddEvents ((int) Gdk.EventMask.AllEventsMask);
126
 
                        
127
 
                        SwitcherItems = null;
128
 
                        
129
 
                        Wnck.Global.ClientType = ClientType.Pager;
130
 
                        
131
 
                        Realized += delegate {
132
 
                                GdkWindow.SetBackPixmap (null, false);
133
 
                        };
134
 
                        
135
 
                        StyleSet += delegate {
136
 
                                if (IsRealized)
137
 
                                        GdkWindow.SetBackPixmap (null, false);
138
 
                        };
139
 
                        
140
 
                        Wnck.Screen.Default.WindowOpened += HandleWindowOpened;
141
 
                        Wnck.Screen.Default.WindowClosed += HandleWindowClosed; 
142
 
                        
143
 
                        reflection = new Gdk.Pixbuf (System.Reflection.Assembly.GetExecutingAssembly (), "reflection.png");
144
 
                }
145
 
                
146
 
                public void Next ()
147
 
                {
148
 
                        List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
149
 
                        int current = windows.IndexOf (current_window);
150
 
                        
151
 
                        if (current < windows.Count - 1)
152
 
                                SetWindow (windows [current + 1]);
153
 
                        else if (current == windows.Count - 1)
154
 
                                SetWindow (windows [0]);
155
 
                }
156
 
                
157
 
                public void Prev ()
158
 
                {
159
 
                        List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
160
 
                        int current = windows.IndexOf (current_window);
161
 
                        
162
 
                        if (current > 0)
163
 
                                SetWindow (windows [current - 1]);
164
 
                        else if (current == 0)
165
 
                                SetWindow (windows.Last ());
166
 
                }
167
 
                
168
 
                public void NextApp ()
169
 
                {
170
 
                        bool found = false;
171
 
                        foreach (SwitcherItem si in SwitcherItems) {
172
 
                                if (found) {
173
 
                                        SetWindow (si.VisibleWindows.First ());
174
 
                                        return;
175
 
                                } else if (si.VisibleWindows.Contains (CurrentWindow)) {
176
 
                                        found = true;
177
 
                                }
178
 
                        }
179
 
                        
180
 
                        Wnck.Window w = SwitcherItems.First ().VisibleWindows.First ();
181
 
                        SetWindow (w);
182
 
                }
183
 
                
184
 
                public void PrevApp ()
185
 
                {
186
 
                        if (SwitcherItems.First ().VisibleWindows.Contains (CurrentWindow)) {
187
 
                                SetWindow (SwitcherItems.Last ().VisibleWindows.First ());
188
 
                                return;
189
 
                        }
190
 
                        
191
 
                        SwitcherItem prev = null;
192
 
                        foreach (SwitcherItem si in SwitcherItems) {
193
 
                                if (si.VisibleWindows.Contains (CurrentWindow)) {
194
 
                                        if (prev != null)
195
 
                                                SetWindow (prev.VisibleWindows.First ());
196
 
                                        return;
197
 
                                }
198
 
                                prev = si;
199
 
                        }
200
 
                }
201
 
                
202
 
                public void Up ()
203
 
                {
204
 
                        List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
205
 
                        int current = windows.IndexOf (current_window);
206
 
                        
207
 
                        if (current >= Columns) {
208
 
                                SetWindow (windows [current - Columns]);
209
 
                        }
210
 
                }
211
 
                
212
 
                public void Down ()
213
 
                {
214
 
                        List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
215
 
                        int current = windows.IndexOf (current_window);
216
 
                        
217
 
                        if (windows.Count > current + Columns) {
218
 
                                SetWindow (windows [current + Columns]);
219
 
                        }
220
 
                }
221
 
                
222
 
                public void Right ()
223
 
                {
224
 
                        List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
225
 
                        int current = windows.IndexOf (current_window);
226
 
                        
227
 
                        if (current % Columns < Columns - 1)
228
 
                                SetWindow (windows [current + 1]);
229
 
                }
230
 
                
231
 
                public void Left ()
232
 
                {
233
 
                        List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
234
 
                        int current = windows.IndexOf (current_window);
235
 
                        
236
 
                        if (current % Columns > 0)
237
 
                                SetWindow (windows [current - 1]);
238
 
                }
239
 
                
240
 
                void SetWindow (Wnck.Window w)
241
 
                {
242
 
                        current_window = w;
243
 
                        QueueDraw ();                   
244
 
                }
245
 
                
246
 
                void ActivateWindow (Wnck.Window w)
247
 
                {
248
 
                        if (!w.IsOnWorkspace (Wnck.Screen.Default.ActiveWorkspace))
249
 
                                w.Workspace.Activate (Gtk.Global.CurrentEventTime);
250
 
                        
251
 
                        if (w.IsMinimized)
252
 
                                w.Unminimize (Gtk.Global.CurrentEventTime);
253
 
                        
254
 
                        w.Activate (Gtk.Global.CurrentEventTime);
255
 
                }
256
 
 
257
 
                void HandleWindowOpened (object o, WindowOpenedArgs args)
258
 
                {
259
 
                        
260
 
                }
261
 
 
262
 
                void HandleWindowClosed (object o, WindowClosedArgs args)
263
 
                {
264
 
                        
265
 
                }
266
 
                
267
 
                protected override bool OnExposeEvent (Gdk.EventExpose evnt)
268
 
                {
269
 
                        using (Cairo.Context cr = CairoHelper.Create (GdkWindow)) {
270
 
                                cr.Operator = Operator.Source;
271
 
                                cr.Color = new Cairo.Color (0, 0, 0, 0);
272
 
                                cr.Paint ();
273
 
                                cr.Operator = Operator.Over;
274
 
                                
275
 
                                DrawAndClipBackground (cr, evnt.Area);
276
 
                                DrawIcons (cr, evnt.Area);
277
 
                        }
278
 
                        
279
 
                        return true;
280
 
                }
281
 
                
282
 
                void DrawAndClipBackground (Cairo.Context cr, Gdk.Rectangle region)
283
 
                {
284
 
                        cr.RoundedRectangle (region.X + 2, region.Y + 2, region.Width - 4, region.Height - 4, 8);
285
 
                        cr.Color = BackgroundColor;
286
 
                        cr.FillPreserve ();
287
 
                        
288
 
                        cr.Color = BorderColor;
289
 
                        cr.Stroke ();
290
 
                        
291
 
                        cr.RoundedRectangle (region.X + 3, region.Y + 3, region.Width - 6, region.Height - 6, 7);
292
 
                        cr.Clip ();
293
 
                        
294
 
                        CairoHelper.SetSourcePixbuf (cr, reflection, -100, -100);
295
 
                        cr.PaintWithAlpha (.35);
296
 
                }
297
 
                
298
 
                void DrawIcons (Cairo.Context cr, Gdk.Rectangle region)
299
 
                {
300
 
                        int offset = 0;
301
 
                        foreach (SwitcherItem item in SwitcherItems) {
302
 
                                DrawIcon (cr, region, item, offset);
303
 
                                offset++;
304
 
                        }
305
 
                }
306
 
                
307
 
                void DrawIcon (Cairo.Context cr, Gdk.Rectangle region, SwitcherItem item, int offset)
308
 
                {
309
 
                        Gnome.DesktopItem di = item.DesktopItem;
310
 
                        Gdk.Pixbuf icon;
311
 
                        
312
 
                        if (di.AttrExists ("Icon") && Gtk.IconTheme.Default.HasIcon (di.GetString ("Icon"))) {
313
 
                                icon = Gtk.IconTheme.Default.LoadIcon (di.GetString ("Icon"), IconSize, 0);
314
 
                        } else if (item.VisibleWindows.First ().Icon != null) {
315
 
                                icon = item.VisibleWindows.First ().Icon;
316
 
                        } else {
317
 
                                icon = Gtk.IconTheme.Default.LoadIcon ("emblem-noread", IconSize, 0);
318
 
                        }
319
 
                        
320
 
                        if (icon.Height != IconSize) {
321
 
                                double delta = IconSize / (double) icon.Height;
322
 
                                Gdk.Pixbuf tmp = icon.ScaleSimple ((int) (icon.Height * delta), (int) (icon.Width * delta), InterpType.Bilinear);
323
 
                                icon.Dispose ();
324
 
                                icon = tmp;
325
 
                        }
326
 
                                
327
 
                        bool isCurrent = item.VisibleWindows.Contains (CurrentWindow);
328
 
                        Gdk.Rectangle iconRegion = new Gdk.Rectangle (offset * (2 * BufferSize + IconSize), region.Y, IconSize + BufferSize * 2, IconSize + BufferSize * 2);
329
 
                        
330
 
                        // draw highlight
331
 
                        if (isCurrent) {
332
 
                                int yOffset = iconRegion.Y + iconRegion.Height + BufferSize;
333
 
                                cr.MoveTo (iconRegion.X, 0);
334
 
                                cr.LineTo (iconRegion.X, yOffset);
335
 
                                cr.LineTo (0, yOffset);
336
 
                                cr.LineTo (0, HeightRequest);
337
 
                                cr.LineTo (WidthRequest, HeightRequest);
338
 
                                cr.LineTo (WidthRequest, yOffset);
339
 
                                cr.LineTo (iconRegion.X + iconRegion.Width, yOffset);
340
 
                                cr.LineTo (iconRegion.X + iconRegion.Width, 0);
341
 
                                
342
 
                                
343
 
                                LinearGradient lg = new LinearGradient (0, region.Y, 0, region.Height);
344
 
                                lg.AddColorStop (0, HighlightColorTop);
345
 
                                lg.AddColorStop (1, HighlightColorBottom);
346
 
                                
347
 
                                cr.Pattern = lg;
348
 
                                
349
 
                                lg.Destroy ();
350
 
                                cr.Fill ();
351
 
                        }
352
 
                                
353
 
                        CairoHelper.SetSourcePixbuf (cr, icon, iconRegion.X + BufferSize, iconRegion.Y + BufferSize);
354
 
                        cr.Paint ();
355
 
                        
356
 
                        icon.Dispose ();
357
 
                        
358
 
                        if (isCurrent) {
359
 
                                int i = 0;
360
 
                                int columns = Columns;
361
 
                                int columnBuffer = (WidthRequest % WindowItemWidth) / (columns + 1);
362
 
                                
363
 
                                foreach (Wnck.Window w in item.VisibleWindows) {
364
 
                                        int column = i % columns;
365
 
                                        int row = i / columns;
366
 
                                        DrawWindowItem (cr, 
367
 
                                                        new Gdk.Rectangle (columnBuffer + (WindowItemWidth + columnBuffer) * column, 
368
 
                                                                           (region.Y + 4 * BufferSize + IconSize) + (row * (WindowItemHeight + BufferSize)), 
369
 
                                                                           WindowItemWidth, 
370
 
                                                                           WindowItemHeight),
371
 
                                                        w);
372
 
                                        
373
 
                                        i++;
374
 
                                }
375
 
                        }
376
 
                }
377
 
                
378
 
                void DrawWindowItem (Cairo.Context cr, Gdk.Rectangle area, Wnck.Window window)
379
 
                {
380
 
                        bool isCurrent = window == CurrentWindow;
381
 
                        Gdk.Pixbuf icon = window.Icon;
382
 
                        
383
 
                        if (icon.Height != area.Height) {
384
 
                                double delta = area.Height / (double) icon.Height;
385
 
                                Gdk.Pixbuf tmp = icon.ScaleSimple ((int) (icon.Height * delta), (int) (icon.Width * delta), InterpType.Bilinear);
386
 
                                icon.Dispose ();
387
 
                                icon = tmp;
388
 
                        }
389
 
                        
390
 
                        CairoHelper.SetSourcePixbuf (cr, icon, area.X, area.Y);
391
 
                        cr.PaintWithAlpha ((isCurrent) ? 1 : .5);
392
 
                        
393
 
                        Pango.Layout layout = Pango.CairoHelper.CreateLayout (cr);
394
 
                        
395
 
                        layout.FontDescription = Style.FontDescription;
396
 
                        layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels (12);
397
 
                        layout.FontDescription.Weight = Pango.Weight.Bold;
398
 
                        
399
 
                        layout.SetText (window.Name);
400
 
                        layout.Width = Pango.Units.FromPixels (WindowItemWidth - area.Height - BufferSize);
401
 
                        layout.Ellipsize = Pango.EllipsizeMode.End;
402
 
                        
403
 
                        Pango.Rectangle inkRect, logicalRect;
404
 
                        layout.GetExtents (out inkRect, out logicalRect);
405
 
                        
406
 
                        int y = area.Y + (int) ((area.Height - Pango.Units.ToPixels (logicalRect.Height)) / 2.0);
407
 
                        
408
 
                        cr.MoveTo (area.X + area.Height + BufferSize, y);
409
 
                        Pango.CairoHelper.LayoutPath (cr, layout);
410
 
                        
411
 
                        if (isCurrent)
412
 
                                cr.Color = new Cairo.Color (1, 1, 1);
413
 
                        else
414
 
                                cr.Color = new Cairo.Color (1, 1, 1, .5);
415
 
                        cr.Fill ();
416
 
                        
417
 
                        icon.Dispose ();
418
 
                }
419
 
                
420
 
                protected override void OnShown ()
421
 
                {
422
 
                        current_window = PreviousWindow ();
423
 
                        
424
 
                        // order things to get consistent feel, to array for performance
425
 
                        SwitcherItems = GetSwitcherItems ().OrderBy (si => si.DesktopItem.GetString ("Name")).ToArray ();
426
 
                        
427
 
                        WidthRequest = Math.Max (MinWidth, (IconSize + BufferSize * 2) * SwitcherItems.Count ());
428
 
                        
429
 
                        double rows = Math.Ceiling (SwitcherItems.Max (si => si.Windows.Count ()) / (double) Columns);
430
 
                        
431
 
                        int height = IconSize + 4 * BufferSize + (WindowItemHeight + BufferSize) * (int) rows;
432
 
                        HeightRequest = Math.Max (100, height);
433
 
                        
434
 
                        base.OnShown ();
435
 
                }
436
 
                
437
 
                Wnck.Window PreviousWindow ()
438
 
                {
439
 
                        IEnumerable<Wnck.Window> stack = Wnck.Screen.Default.WindowsStacked;
440
 
                        
441
 
                        if (!stack.Any ())
442
 
                                return null;
443
 
                        
444
 
                        if (stack.Count () == 1)
445
 
                                return stack.First ();
446
 
                        
447
 
                        return stack
448
 
                                .Reverse ()
449
 
                                .Where (w => !w.IsSkipTasklist)
450
 
                                .Skip (1)
451
 
                                .First ();
452
 
                }
453
 
                
454
 
                protected override void OnHidden ()
455
 
                {
456
 
                        if (current_window != null)
457
 
                                ActivateWindow (current_window);
458
 
                        base.OnHidden ();
459
 
                }
460
 
 
461
 
 
462
 
                IEnumerable<SwitcherItem> GetSwitcherItems ()
463
 
                {
464
 
                        foreach (string file in RunningDesktopFiles ()) {
465
 
                                SwitcherItem si = new SwitcherItem (file);
466
 
                                if (si.VisibleWindows.Any ())
467
 
                                        yield return si;
468
 
                        }
469
 
                }
470
 
                                 
471
 
                IEnumerable<string> RunningDesktopFiles ()
472
 
                {
473
 
                        return Wnck.Screen.Default.Windows
474
 
                                .Select (w => LibWnckSync.Global.DesktopItemForWindow (w))
475
 
                                .Where (s => !string.IsNullOrEmpty (s))
476
 
                                .Distinct ();
477
 
                }
478
 
        }
479
 
}