~ubuntu-branches/ubuntu/wily/gnome-do/wily

« back to all changes in this revision

Viewing changes to Do.Interface.Wink/src/Do.Interface.Wink/WindowUtils.cs

  • Committer: Bazaar Package Importer
  • Author(s): Christopher James Halse Rogers
  • Date: 2009-06-27 10:40:45 UTC
  • mfrom: (1.1.8 upstream) (0.1.5 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090627104045-7st10y1cqr6dpz37
Tags: 0.8.2+dfsg-1
* New upstream release
  + No longer uses a plugin repository.  Fixes many plugin-
    related issues. (LP: #343096, LP: #330025, LP #345001)
  + No longer blocks on "About Do" (LP: #361679)
  + Reacts correctly when a Composite manager is enabled/
    disabled at runtime. (LP: #346347, LP: #390150)
  + Fixes for space reserved by Docky blocking drag and 
    drop operations. (LP: #354729, LP: #347052, LP: #382843)
  + Properly sets "Hidden" key on autostart files in response to 
    "Start on login" option.  (Closes: #526023) (LP: #369988)
* debian/patches/10_application_search_path:
  + Drop; included upstream
* debian/patches/10_sk_translation_update:
  + Import sk translation update from Debian BTS.
    (Closes: #531779)
* debian/patches/11_fix_autostart_when_directory_does_not_exist:
  + Patch from upstream.  Fixes the "Start on login" option when the 
    ~/.config/autostart directory does not exist. (LP: #393729)
* debian/control:
  + Update standards version to 3.8.2; no changes required.
  + Add libtool to Build-Depends; required for autoreconf.
  + Add Recommends: on new gnome-do-docklets package.
* debian/gnome-do.1
  + Fix spelling: GNOME-Do => GNOME Do.
  + Miscelaneous lintian fixes; NAME section, escaping minus signs with \-
* debian/copyright:
  + Update for new copyright holders.
  + Minor update to DEP-5 format

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// WindowUtils.cs
 
2
// 
 
3
// Copyright (C) 2008 GNOME Do
 
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 as published by
 
7
// the Free Software Foundation, either version 3 of the License, or
 
8
// (at your option) any later version.
 
9
//
 
10
// This program is distributed in the hope that it will be useful,
 
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
// GNU General Public License for more details.
 
14
//
 
15
// You should have received a copy of the GNU General Public License
 
16
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
17
//
 
18
 
 
19
using System;
 
20
using System.Collections.Generic;
 
21
using System.Diagnostics;
 
22
using System.IO;
 
23
using System.Linq;
 
24
using System.Text.RegularExpressions;
 
25
 
 
26
using Do.Platform;
 
27
 
 
28
using Wnck;
 
29
 
 
30
namespace Do.Interface.Wink
 
31
{
 
32
        
 
33
        public static class WindowUtils
 
34
        {
 
35
                enum OpenOfficeProducts {
 
36
                        Writer,
 
37
                        Calc,
 
38
                        Base,
 
39
                        Math,
 
40
                        Impress,
 
41
                }
 
42
                
 
43
                static bool initialized;
 
44
                
 
45
                static string RemapFile {
 
46
                        get { return Path.Combine (Services.Paths.UserDataDirectory, "RemapFile"); }
 
47
                }
 
48
                
 
49
                static IEnumerable<string> PrefixStrings {
 
50
                        get {
 
51
                                yield return "gksu";
 
52
                                yield return "sudo";
 
53
                                yield return "java";
 
54
                                yield return "mono";
 
55
                                yield return "ruby";
 
56
                                yield return "padsp";
 
57
                                yield return "aoss";
 
58
                                yield return "python(\\d.\\d)?";
 
59
                                yield return "(ba)?sh";
 
60
                        }
 
61
                }
 
62
                
 
63
                public static IEnumerable<Regex> BadPrefixes { get; private set; }
 
64
                
 
65
                static Dictionary<string, string> RemapDictionary { get; set; }
 
66
                
 
67
                static List<Window> window_list;
 
68
                static bool window_list_update_needed;
 
69
                
 
70
                static Dictionary<int, string> exec_lines = new Dictionary<int, string> ();
 
71
                static DateTime last_update = new DateTime (0);
 
72
                
 
73
                #region ctor
 
74
                public static void Initialize ()
 
75
                {
 
76
                        if (initialized)
 
77
                                return;
 
78
                        
 
79
                        initialized = true;
 
80
                        Wnck.Global.ClientType = Wnck.ClientType.Pager;
 
81
                        
 
82
                        List<Regex> regex = new List<Regex> ();
 
83
                        foreach (string s in PrefixStrings) {
 
84
                                 regex.Add (new Regex (string.Format ("^{0}$", s), RegexOptions.IgnoreCase));
 
85
                        }
 
86
                        
 
87
                        BadPrefixes = regex.AsEnumerable ();
 
88
                        
 
89
                        Wnck.Screen.Default.WindowClosed += delegate {
 
90
                                window_list_update_needed = true;
 
91
                        };
 
92
                        
 
93
                        Wnck.Screen.Default.WindowOpened += delegate {
 
94
                                window_list_update_needed = true;
 
95
                        };
 
96
                        
 
97
                        Wnck.Screen.Default.ApplicationOpened += delegate {
 
98
                                window_list_update_needed = true;
 
99
                        };
 
100
                        
 
101
                        Wnck.Screen.Default.ApplicationClosed += delegate {
 
102
                                window_list_update_needed = true;
 
103
                        };
 
104
                        
 
105
                        BuildRemapDictionary ();
 
106
                }
 
107
                #endregion
 
108
                
 
109
                #region Private Methods
 
110
                static void BuildRemapDictionary ()
 
111
                {
 
112
                        if (!File.Exists (RemapFile)) {
 
113
                                RemapDictionary = BuildDefaultRemapDictionary ();
 
114
                                
 
115
                                try {
 
116
                                        using (StreamWriter writer = new StreamWriter (RemapFile)) {
 
117
                                                writer.WriteLine ("# Docky Remap File");
 
118
                                                writer.WriteLine ("# Add key value pairs following dictionary syntax");
 
119
                                                writer.WriteLine ("# key, value");
 
120
                                                writer.WriteLine ("# key, altKey, value");
 
121
                                                writer.WriteLine ("# Lines starting with # are comments, otherwise # is a valid character");
 
122
                                                
 
123
                                                foreach (KeyValuePair<string, string> kvp in RemapDictionary) {
 
124
                                                        writer.WriteLine ("{0}, {1}", kvp.Key, kvp.Value);
 
125
                                                }
 
126
                                                writer.Close ();
 
127
                                        }
 
128
                                } catch {
 
129
                                }
 
130
                        } else {
 
131
                                RemapDictionary = new Dictionary<string, string> ();
 
132
                                
 
133
                                try {
 
134
                                        using (StreamReader reader = new StreamReader (RemapFile)) {
 
135
                                                string line;
 
136
                                                while (!reader.EndOfStream) {
 
137
                                                        line = reader.ReadLine ();
 
138
                                                        if (line.StartsWith ("#") || !line.Contains (","))
 
139
                                                                continue;
 
140
                                                        string [] array = line.Split (',');
 
141
                                                        if (array.Length < 2 || array [0].Length == 0)
 
142
                                                                continue;
 
143
                                                        
 
144
                                                        string val = array [array.Length - 1].Trim ().ToLower ();
 
145
                                                        if (string.IsNullOrEmpty (val))
 
146
                                                                continue;
 
147
                                                        
 
148
                                                        for (int i=0; i < array.Length - 1; i++) {
 
149
                                                                string key = array [i].Trim ().ToLower ();
 
150
                                                                if (string.IsNullOrEmpty (key))
 
151
                                                                        continue;
 
152
                                                                RemapDictionary [key] = val;
 
153
                                                        }
 
154
                                                }
 
155
                                                
 
156
                                                reader.Close ();
 
157
                                        }
 
158
                                } catch {
 
159
                                        Log.Error ("Could not read remap file");
 
160
                                        RemapDictionary = BuildDefaultRemapDictionary ();
 
161
                                }
 
162
                        }
 
163
                }
 
164
                
 
165
                static Dictionary<string, string> BuildDefaultRemapDictionary ()
 
166
                {
 
167
                        Dictionary<string, string> remapDict = new Dictionary<string, string> ();
 
168
                        remapDict ["banshee.exe"] = "banshee";
 
169
                        remapDict ["banshee-1"] = "banshee";
 
170
                        remapDict ["azureus"] = "vuze";
 
171
                        remapDict ["thunderbird-3.0"] = "thunderbird";
 
172
                        remapDict ["thunderbird-bin"] = "thunderbird";
 
173
                        
 
174
                        return remapDict;
 
175
                }
 
176
                
 
177
                static void UpdateExecList ()
 
178
                {
 
179
                        if ((DateTime.UtcNow - last_update).TotalMilliseconds < 200) return;
 
180
                        
 
181
                        Dictionary<int, string> old = exec_lines;
 
182
                        
 
183
                        exec_lines = new Dictionary<int, string> ();
 
184
                        
 
185
                        foreach (string dir in Directory.GetDirectories ("/proc")) {
 
186
                                int pid;
 
187
                                try { pid = Convert.ToInt32 (Path.GetFileName (dir)); } 
 
188
                                catch { continue; }
 
189
                                
 
190
                                if (old.ContainsKey (pid)) {
 
191
                                        exec_lines [pid] = old [pid];
 
192
                                        continue;
 
193
                                }
 
194
                                
 
195
                                string exec_line = CmdLineForPid (pid);
 
196
                                if (string.IsNullOrEmpty (exec_line))
 
197
                                        continue;
 
198
                                
 
199
                                if (exec_line.Contains ("java") && exec_line.Contains ("jar")) {
 
200
                                        foreach (Window window in GetWindows ()) {
 
201
                                                if (window == null)
 
202
                                                        continue;
 
203
                                                
 
204
                                                if (window.Pid == pid || window.Application.Pid == pid) {
 
205
                                                        exec_line = window.ClassGroup.ResClass;
 
206
                                                                
 
207
                                                        // Vuze is retarded
 
208
                                                        if (exec_line == "SWT")
 
209
                                                                exec_line = window.Name;
 
210
                                                }
 
211
                                        }
 
212
                                }       
 
213
                                
 
214
                                exec_line = ProcessExecString (exec_line);
 
215
                                        
 
216
                                exec_lines [pid] = exec_line;
 
217
                        }
 
218
                        
 
219
                        last_update = DateTime.UtcNow;
 
220
                }
 
221
                
 
222
                static ClickAction GetClickAction (IEnumerable<Window> windows)
 
223
                {
 
224
                        if (!windows.Any ())
 
225
                                return ClickAction.None;
 
226
                        
 
227
                        if (windows.Any (w => w.IsMinimized && w.IsInViewport (Wnck.Screen.Default.ActiveWorkspace)))
 
228
                                return ClickAction.Restore;
 
229
                        
 
230
                        if (windows.Any (w => w.IsActive && w.IsInViewport (Wnck.Screen.Default.ActiveWorkspace)))
 
231
                                return ClickAction.Minimize;
 
232
                        
 
233
                        return ClickAction.Focus;
 
234
                }
 
235
                #endregion
 
236
                
 
237
                #region Public Methods
 
238
                /// <summary>
 
239
                /// Returns a list of all windows on the default screen
 
240
                /// </summary>
 
241
                /// <returns>
 
242
                /// A <see cref="List"/>
 
243
                /// </returns>
 
244
                public static List<Window> GetWindows ()
 
245
                {
 
246
                        if (window_list == null || window_list_update_needed)
 
247
                                window_list = new List<Window> (Wnck.Screen.Default.WindowsStacked);
 
248
                        
 
249
                        return window_list;
 
250
                }
 
251
                
 
252
                /// <summary>
 
253
                /// Gets the command line excec string for a PID
 
254
                /// </summary>
 
255
                /// <param name="pid">
 
256
                /// A <see cref="System.Int32"/>
 
257
                /// </param>
 
258
                /// <returns>
 
259
                /// A <see cref="System.String"/>
 
260
                /// </returns>
 
261
                public static string CmdLineForPid (int pid)
 
262
                {
 
263
                        string cmdline = null;
 
264
                        
 
265
                        try {
 
266
                                string procPath = new [] { "/proc", pid.ToString (), "cmdline" }.Aggregate (Path.Combine);
 
267
                                using (StreamReader reader = new StreamReader (procPath)) {
 
268
                                        cmdline = reader.ReadLine ();
 
269
                                        reader.Close ();
 
270
                                }
 
271
                        } catch { }
 
272
                        
 
273
                        return cmdline;
 
274
                }
 
275
                
 
276
                public static List<Window> WindowListForCmd (string exec)
 
277
                {
 
278
                        List<Window> windows = new List<Window> ();
 
279
                        if (string.IsNullOrEmpty (exec))
 
280
                                return windows;
 
281
                        
 
282
                        // open office hakk
 
283
                        if (exec.Contains ("ooffice")) {
 
284
                                return GetOpenOfficeWindows (exec);
 
285
                        }
 
286
                        
 
287
                        exec = ProcessExecString (exec);
 
288
                        if (string.IsNullOrEmpty (exec))
 
289
                                return windows;
 
290
                        
 
291
                        UpdateExecList ();
 
292
                        
 
293
                        foreach (KeyValuePair<int, string> kvp in exec_lines) {
 
294
                                if (!string.IsNullOrEmpty (kvp.Value) && kvp.Value.Contains (exec)) {
 
295
                                        // we have a matching exec, now we just find every window whose PID matches this exec
 
296
                                        foreach (Window window in GetWindows ()) {
 
297
                                                if (window == null)
 
298
                                                        continue;
 
299
                                                
 
300
                                                // this window matches the right PID and exec string, we can match it.
 
301
                                                bool pidMatch = window.Pid == kvp.Key || 
 
302
                                                        (window.Application != null && window.Application.Pid == kvp.Key);
 
303
                                                
 
304
                                                if (pidMatch)
 
305
                                                        windows.Add (window);
 
306
                                        }
 
307
                                }
 
308
                        }
 
309
                        
 
310
                        return windows.Distinct ().ToList ();
 
311
                }
 
312
                
 
313
                static List<Window> GetOpenOfficeWindows (string exec)
 
314
                {
 
315
                        if (exec.Contains ("writer")) {
 
316
                                return GetWindows ().Where ((Wnck.Window w) => w.Name.Contains ("OpenOffice.org Writer")).ToList ();
 
317
                        } else if (exec.Contains ("math")) {
 
318
                                return GetWindows ().Where ((Wnck.Window w) => w.Name.Contains ("OpenOffice.org Math")).ToList ();
 
319
                        } else if (exec.Contains ("calc")) {
 
320
                                return GetWindows ().Where ((Wnck.Window w) => w.Name.Contains ("OpenOffice.org Calc")).ToList ();
 
321
                        } else if (exec.Contains ("impress")) {
 
322
                                return GetWindows ().Where ((Wnck.Window w) => w.Name.Contains ("OpenOffice.org Impress")).ToList ();
 
323
                        } else if (exec.Contains ("draw")) {
 
324
                                return GetWindows ().Where ((Wnck.Window w) => w.Name.Contains ("OpenOffice.org Draw")).ToList ();
 
325
                        } else {
 
326
                                return new List<Window> (0);
 
327
                        }
 
328
                }
 
329
                
 
330
                /// <summary>
 
331
                /// This method takes in an "execution string" from proc and applies a heureustic to try
 
332
                /// to magic out the name of the actual executing application.  The executing binary is not
 
333
                /// the desired target all the time.
 
334
                /// </summary>
 
335
                /// <param name="exec">
 
336
                /// A <see cref="System.String"/>
 
337
                /// </param>
 
338
                /// <returns>
 
339
                /// A <see cref="System.String"/>
 
340
                /// </returns>
 
341
                public static string ProcessExecString (string exec)
 
342
                {
 
343
                        if (string.IsNullOrEmpty (exec))
 
344
                                return exec;
 
345
                        
 
346
                        // lower it and trim off white space so we can abuse whitespace a bit
 
347
                        exec = exec.ToLower ().Trim ();
 
348
                        
 
349
                        // if the user has specified a specific mapping, we can use that here
 
350
                        if (RemapDictionary.ContainsKey (exec))
 
351
                                return RemapDictionary [exec];
 
352
                        
 
353
                        // this is the "split" character or the argument separator.  If the string contains a null
 
354
                        // it was fetched from /proc/PID/cmdline and will be nicely split up. Otherwise things get a bit
 
355
                        // nasty, and it likely came from a .desktop file.
 
356
                        char splitChar = Convert.ToChar (0x0);
 
357
                        splitChar = exec.Contains (splitChar) ? splitChar : ' ';
 
358
                        
 
359
                        // this part is here soley for the remap file so that users may specify to remap based on just the name
 
360
                        // without the full path.  If no remap file match is found, the net effect of this is nothing.
 
361
                        if (exec.StartsWith ("/")) {
 
362
                                string first_part = exec.Split (splitChar) [0];
 
363
                                int length = first_part.Length;
 
364
                                first_part = first_part.Split ('/').Last ();
 
365
                                
 
366
                                if (length < exec.Length)
 
367
                                         first_part = first_part + " " + exec.Substring (length + 1);
 
368
                                                
 
369
                                if (RemapDictionary.ContainsKey (first_part)) {
 
370
                                        return RemapDictionary [first_part];
 
371
                                }
 
372
                        }
 
373
                        
 
374
                        string [] parts = exec.Split (splitChar);
 
375
                        for (int i = 0; i < parts.Length; i++) {
 
376
                                // we're going to use this a lot
 
377
                                string out_val = parts [i];
 
378
                                
 
379
                                // arguments are useless
 
380
                                if (out_val.StartsWith ("-"))
 
381
                                        continue;
 
382
                                
 
383
                                // we want the end of paths
 
384
                                if (out_val.Contains ("/"))
 
385
                                        out_val = out_val.Split ('/').Last ();
 
386
                                
 
387
                                // wine apps can do it backwards... who knew?
 
388
                                if (out_val.Contains ("\\"))
 
389
                                        out_val = out_val.Split ('\\').Last ();
 
390
                                
 
391
                                // null out our part if is a bad prefix
 
392
                                foreach (Regex regex in BadPrefixes) {
 
393
                                        if (regex.IsMatch (out_val)) {
 
394
                                                out_val = null;
 
395
                                                break;
 
396
                                        }
 
397
                                }
 
398
                                
 
399
                                // check if it was a bad prefix...
 
400
                                if (!string.IsNullOrEmpty (out_val)) {
 
401
                                        // sometimes we hide things with shell scripts.  This is the most common method of doing it.
 
402
                                        if (out_val.EndsWith (".real"))
 
403
                                                out_val = out_val.Substring (0, out_val.Length - ".real".Length);
 
404
                                        
 
405
                                        // give the remap dictionary one last shot at this
 
406
                                        if (RemapDictionary.ContainsKey (out_val))
 
407
                                                out_val = RemapDictionary [out_val];
 
408
                                        return out_val;
 
409
                                }
 
410
                        }
 
411
                        return null;
 
412
                }
 
413
                
 
414
                /// <summary>
 
415
                /// Performs the "logical" click action on an entire group of applications
 
416
                /// </summary>
 
417
                /// <param name="apps">
 
418
                /// A <see cref="IEnumerable"/>
 
419
                /// </param>
 
420
                public static void PerformLogicalClick (IEnumerable<Window> windows)
 
421
                {
 
422
                        List<Window> stack = new List<Window> (Wnck.Screen.Default.WindowsStacked);
 
423
                        windows = windows.OrderByDescending (w => stack.IndexOf (w));
 
424
                        
 
425
                        bool not_in_viewport = !windows.Any (w => !w.IsSkipTasklist && w.IsInViewport (w.Screen.ActiveWorkspace));
 
426
                        bool urgent = windows.Any (w => w.NeedsAttention ());
 
427
                        
 
428
                        if (not_in_viewport || urgent) {
 
429
                                foreach (Wnck.Window window in windows) {
 
430
                                        if (urgent && !window.NeedsAttention ())
 
431
                                                continue;
 
432
                                        if (!window.IsSkipTasklist) {
 
433
                                                WindowControl.IntelligentFocusOffViewportWindow (window, windows);
 
434
                                                return;
 
435
                                        }
 
436
                                }
 
437
                        }
 
438
                        
 
439
                        switch (GetClickAction (windows)) {
 
440
                        case ClickAction.Focus:
 
441
                                WindowControl.FocusWindows (windows);
 
442
                                break;
 
443
                        case ClickAction.Minimize:
 
444
                                WindowControl.MinimizeWindows (windows);
 
445
                                break;
 
446
                        case ClickAction.Restore:
 
447
                                WindowControl.RestoreWindows (windows);
 
448
                                break;
 
449
                        }
 
450
                }
 
451
                
 
452
                #endregion
 
453
        }
 
454
}