2
// Copyright (C) 2009 Canonical Ltd.
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.
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.
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/>.
19
using System.Collections.Generic;
28
namespace WindowSwitcher
32
public class SwitcherArea : Gtk.DrawingArea
36
public DesktopItem DesktopItem { get; private set; }
38
public string DesktopFile { get; private set; }
40
IEnumerable<Wnck.Window> windows;
41
public IEnumerable<Wnck.Window> Windows {
47
public IEnumerable<Wnck.Window> VisibleWindows {
49
return windows.Where (w => !w.IsSkipTasklist);
53
public SwitcherItem (string desktopFile)
55
DesktopItem = Gnome.DesktopItem.NewFromFile (desktopFile, 0);
56
DesktopFile = desktopFile;
60
public SwitcherItem (DesktopItem item)
63
DesktopFile = item.Location;
64
if (DesktopFile.StartsWith ("file://"))
65
DesktopFile = DesktopFile.Substring ("file://".Length);
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))
79
const int IconSize = 64;
80
const int BufferSize = 8;
81
const int WindowItemHeight = 20;
82
const int MinWidth = 300;
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);
89
Gdk.Pixbuf reflection;
93
int fits = WidthRequest / MinWidth;
96
return (WidthRequest / fits) - 20;
102
return WidthRequest / WindowItemWidth;
106
IEnumerable<SwitcherItem> SwitcherItems { get; set; }
108
Wnck.Window current_window;
110
Wnck.Window CurrentWindow {
111
get { return current_window; }
114
SwitcherItem CurrentSwitcherItem {
117
.Where (si => si.Windows.Contains (current_window))
118
.DefaultIfEmpty (null)
123
public SwitcherArea ()
125
AddEvents ((int) Gdk.EventMask.AllEventsMask);
127
SwitcherItems = null;
129
Wnck.Global.ClientType = ClientType.Pager;
131
Realized += delegate {
132
GdkWindow.SetBackPixmap (null, false);
135
StyleSet += delegate {
137
GdkWindow.SetBackPixmap (null, false);
140
Wnck.Screen.Default.WindowOpened += HandleWindowOpened;
141
Wnck.Screen.Default.WindowClosed += HandleWindowClosed;
143
reflection = new Gdk.Pixbuf (System.Reflection.Assembly.GetExecutingAssembly (), "reflection.png");
148
List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
149
int current = windows.IndexOf (current_window);
151
if (current < windows.Count - 1)
152
SetWindow (windows [current + 1]);
153
else if (current == windows.Count - 1)
154
SetWindow (windows [0]);
159
List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
160
int current = windows.IndexOf (current_window);
163
SetWindow (windows [current - 1]);
164
else if (current == 0)
165
SetWindow (windows.Last ());
168
public void NextApp ()
171
foreach (SwitcherItem si in SwitcherItems) {
173
SetWindow (si.VisibleWindows.First ());
175
} else if (si.VisibleWindows.Contains (CurrentWindow)) {
180
Wnck.Window w = SwitcherItems.First ().VisibleWindows.First ();
184
public void PrevApp ()
186
if (SwitcherItems.First ().VisibleWindows.Contains (CurrentWindow)) {
187
SetWindow (SwitcherItems.Last ().VisibleWindows.First ());
191
SwitcherItem prev = null;
192
foreach (SwitcherItem si in SwitcherItems) {
193
if (si.VisibleWindows.Contains (CurrentWindow)) {
195
SetWindow (prev.VisibleWindows.First ());
204
List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
205
int current = windows.IndexOf (current_window);
207
if (current >= Columns) {
208
SetWindow (windows [current - Columns]);
214
List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
215
int current = windows.IndexOf (current_window);
217
if (windows.Count > current + Columns) {
218
SetWindow (windows [current + Columns]);
224
List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
225
int current = windows.IndexOf (current_window);
227
if (current % Columns < Columns - 1)
228
SetWindow (windows [current + 1]);
233
List<Wnck.Window> windows = new List<Wnck.Window> (CurrentSwitcherItem.Windows);
234
int current = windows.IndexOf (current_window);
236
if (current % Columns > 0)
237
SetWindow (windows [current - 1]);
240
void SetWindow (Wnck.Window w)
246
void ActivateWindow (Wnck.Window w)
248
if (!w.IsOnWorkspace (Wnck.Screen.Default.ActiveWorkspace))
249
w.Workspace.Activate (Gtk.Global.CurrentEventTime);
252
w.Unminimize (Gtk.Global.CurrentEventTime);
254
w.Activate (Gtk.Global.CurrentEventTime);
257
void HandleWindowOpened (object o, WindowOpenedArgs args)
262
void HandleWindowClosed (object o, WindowClosedArgs args)
267
protected override bool OnExposeEvent (Gdk.EventExpose evnt)
269
using (Cairo.Context cr = CairoHelper.Create (GdkWindow)) {
270
cr.Operator = Operator.Source;
271
cr.Color = new Cairo.Color (0, 0, 0, 0);
273
cr.Operator = Operator.Over;
275
DrawAndClipBackground (cr, evnt.Area);
276
DrawIcons (cr, evnt.Area);
282
void DrawAndClipBackground (Cairo.Context cr, Gdk.Rectangle region)
284
cr.RoundedRectangle (region.X + 2, region.Y + 2, region.Width - 4, region.Height - 4, 8);
285
cr.Color = BackgroundColor;
288
cr.Color = BorderColor;
291
cr.RoundedRectangle (region.X + 3, region.Y + 3, region.Width - 6, region.Height - 6, 7);
294
CairoHelper.SetSourcePixbuf (cr, reflection, -100, -100);
295
cr.PaintWithAlpha (.35);
298
void DrawIcons (Cairo.Context cr, Gdk.Rectangle region)
301
foreach (SwitcherItem item in SwitcherItems) {
302
DrawIcon (cr, region, item, offset);
307
void DrawIcon (Cairo.Context cr, Gdk.Rectangle region, SwitcherItem item, int offset)
309
Gnome.DesktopItem di = item.DesktopItem;
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;
317
icon = Gtk.IconTheme.Default.LoadIcon ("emblem-noread", IconSize, 0);
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);
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);
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);
343
LinearGradient lg = new LinearGradient (0, region.Y, 0, region.Height);
344
lg.AddColorStop (0, HighlightColorTop);
345
lg.AddColorStop (1, HighlightColorBottom);
353
CairoHelper.SetSourcePixbuf (cr, icon, iconRegion.X + BufferSize, iconRegion.Y + BufferSize);
360
int columns = Columns;
361
int columnBuffer = (WidthRequest % WindowItemWidth) / (columns + 1);
363
foreach (Wnck.Window w in item.VisibleWindows) {
364
int column = i % columns;
365
int row = i / columns;
367
new Gdk.Rectangle (columnBuffer + (WindowItemWidth + columnBuffer) * column,
368
(region.Y + 4 * BufferSize + IconSize) + (row * (WindowItemHeight + BufferSize)),
378
void DrawWindowItem (Cairo.Context cr, Gdk.Rectangle area, Wnck.Window window)
380
bool isCurrent = window == CurrentWindow;
381
Gdk.Pixbuf icon = window.Icon;
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);
390
CairoHelper.SetSourcePixbuf (cr, icon, area.X, area.Y);
391
cr.PaintWithAlpha ((isCurrent) ? 1 : .5);
393
Pango.Layout layout = Pango.CairoHelper.CreateLayout (cr);
395
layout.FontDescription = Style.FontDescription;
396
layout.FontDescription.AbsoluteSize = Pango.Units.FromPixels (12);
397
layout.FontDescription.Weight = Pango.Weight.Bold;
399
layout.SetText (window.Name);
400
layout.Width = Pango.Units.FromPixels (WindowItemWidth - area.Height - BufferSize);
401
layout.Ellipsize = Pango.EllipsizeMode.End;
403
Pango.Rectangle inkRect, logicalRect;
404
layout.GetExtents (out inkRect, out logicalRect);
406
int y = area.Y + (int) ((area.Height - Pango.Units.ToPixels (logicalRect.Height)) / 2.0);
408
cr.MoveTo (area.X + area.Height + BufferSize, y);
409
Pango.CairoHelper.LayoutPath (cr, layout);
412
cr.Color = new Cairo.Color (1, 1, 1);
414
cr.Color = new Cairo.Color (1, 1, 1, .5);
420
protected override void OnShown ()
422
current_window = PreviousWindow ();
424
// order things to get consistent feel, to array for performance
425
SwitcherItems = GetSwitcherItems ().OrderBy (si => si.DesktopItem.GetString ("Name")).ToArray ();
427
WidthRequest = Math.Max (MinWidth, (IconSize + BufferSize * 2) * SwitcherItems.Count ());
429
double rows = Math.Ceiling (SwitcherItems.Max (si => si.Windows.Count ()) / (double) Columns);
431
int height = IconSize + 4 * BufferSize + (WindowItemHeight + BufferSize) * (int) rows;
432
HeightRequest = Math.Max (100, height);
437
Wnck.Window PreviousWindow ()
439
IEnumerable<Wnck.Window> stack = Wnck.Screen.Default.WindowsStacked;
444
if (stack.Count () == 1)
445
return stack.First ();
449
.Where (w => !w.IsSkipTasklist)
454
protected override void OnHidden ()
456
if (current_window != null)
457
ActivateWindow (current_window);
462
IEnumerable<SwitcherItem> GetSwitcherItems ()
464
foreach (string file in RunningDesktopFiles ()) {
465
SwitcherItem si = new SwitcherItem (file);
466
if (si.VisibleWindows.Any ())
471
IEnumerable<string> RunningDesktopFiles ()
473
return Wnck.Screen.Default.Windows
474
.Select (w => LibWnckSync.Global.DesktopItemForWindow (w))
475
.Where (s => !string.IsNullOrEmpty (s))