~and471/slickpanel/trunk

« back to all changes in this revision

Viewing changes to panel/items/windowlist.vala

  • Committer: Andrew Higginson
  • Date: 2014-12-29 17:48:31 UTC
  • Revision ID: at.higginson@gmail.com-20141229174831-bukedaw8kny4aq8t
updating

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright 2010 Andrew Higginson
2
 
 *
3
 
 * This program is free software; you can redistribute it and/or modify
4
 
 * it under the terms of the GNU General Public License as published by
5
 
 * the Free Software Foundation; either version 2 of the License, or
6
 
 * (at your option) any later version.
7
 
*/
8
 
 
9
 
namespace SlickPanel {
10
 
 
11
 
private Wnck.Screen screen;
12
 
private Gdk.Screen gdk_screen;
13
 
 
14
 
public class WindowList : Item {
15
 
    
16
 
    private Gtk.Builder builder;
17
 
    private Gee.ArrayList<Wnck.Window> windows = new Gee.ArrayList<Wnck.Window>();
18
 
 
19
 
    public WindowList() {
20
 
        this.type_ = Settings.ItemType.WINDOW_LIST;
21
 
        this.expand = true;
22
 
        this.widget = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 0);
23
 
        
24
 
        (this.widget as Gtk.Box).set_homogeneous(true);
25
 
        screen = Wnck.Screen.get_default();
26
 
        gdk_screen = Gdk.Screen.get_default();
27
 
        screen.window_opened.connect(this.on_screen_window_opened);
28
 
        screen.window_closed.connect(this.on_screen_window_closed);
29
 
        screen.active_window_changed.connect(this.on_screen_active_window_changed);
30
 
        screen.active_workspace_changed.connect(this.on_active_workspace_changed);
31
 
        Timeout.add(100, (SourceFunc) this.on_workspace_checker_timeout);
32
 
    }
33
 
    
34
 
    
35
 
    public override void set_orientation(Gtk.Orientation orientation) {
36
 
        var box = new Gtk.Box(orientation, 0);
37
 
        box.set_homogeneous(true);
38
 
        
39
 
        foreach (var window in this.windows) {
40
 
            var button = this.get_button_for_window(window);
41
 
            (this.widget as Gtk.Box).remove(button);
42
 
            box.pack_start(button, true, true);
43
 
        }
44
 
        this.widget = box;
45
 
        this.pack_widget();
46
 
    }
47
 
    
48
 
    public override void set_settings(Settings.Item item_settings) {
49
 
        //do something
50
 
    }
51
 
 
52
 
    /* Private Functions */
53
 
    private void update_window_buttons() {
54
 
        foreach (var window in this.windows) {
55
 
            if (!this.window_should_be_shown(window)) {
56
 
                var button = this.get_button_for_window(window);
57
 
                if (button != null) {
58
 
                    (this.widget as Gtk.Box).remove(button);
59
 
                    button.destroy();
60
 
                }
61
 
            } else if (this.get_button_for_window(window) == null) {
62
 
                this.add_window_button(window);
63
 
            }
64
 
        }
65
 
    }
66
 
    
67
 
    private WindowButton? get_button_for_window(Wnck.Window window) {
68
 
        foreach (var widget in (this.widget as Gtk.Box).get_children()) {
69
 
            var button = widget as WindowButton;
70
 
            if (button.window == window) {
71
 
                return button;
72
 
            }
73
 
        }
74
 
        return null;
75
 
    }
76
 
    
77
 
    private bool window_should_be_shown(Wnck.Window window) {
78
 
        bool show_in_tasklist = !window.is_skip_tasklist();
79
 
        bool in_same_workspace = window.get_workspace() == screen.get_active_workspace();
80
 
        if (screen.get_active_workspace() == null) {
81
 
            in_same_workspace = true;
82
 
        }
83
 
        bool in_same_monitor = true;
84
 
        
85
 
        if (gdk_screen.get_n_monitors() > 1) {
86
 
            int x, y, width, height;
87
 
            window.get_geometry(out x, out y, out width, out height);
88
 
            
89
 
            var panel_monitor = gdk_screen.get_monitor_at_window(this.widget.get_window());
90
 
            var window_monitor = gdk_screen.get_monitor_at_point(x+(width/2), y+(height /2));
91
 
            
92
 
            if (panel_monitor != window_monitor) {
93
 
                in_same_monitor = false;
94
 
            }
95
 
        }
96
 
        
97
 
        return (show_in_tasklist && in_same_workspace && in_same_monitor);
98
 
    }
99
 
    
100
 
    private void add_window_button(Wnck.Window window) {
101
 
        var button = new WindowButton(window);
102
 
        button.window = window;
103
 
 
104
 
        (this.widget as Gtk.Box).pack_start(button, true, true);
105
 
        button.show();
106
 
        this.update_window_icon_geometries();
107
 
    }
108
 
    
109
 
    private void add_window(Wnck.Window window) {
110
 
        this.add_window_button(window);
111
 
    }
112
 
    
113
 
    private void remove_window(Wnck.Window window) {
114
 
        var button = this.get_button_for_window(window);
115
 
        if (button != null) {
116
 
            (this.widget as Gtk.Box).remove(button);
117
 
            button.destroy();
118
 
            this.update_window_icon_geometries();
119
 
        }
120
 
    }
121
 
    
122
 
    /* Callbacks */
123
 
 
124
 
    private void on_screen_window_opened(Wnck.Window window) {
125
 
        this.windows.add(window);
126
 
        bool window_should_be_shown = this.window_should_be_shown(window);
127
 
        if (window_should_be_shown) {
128
 
            this.add_window(window);
129
 
        } else {
130
 
            this.remove_window(window);
131
 
        }
132
 
        window.workspace_changed.connect(this.on_window_workspace_changed);
133
 
        window.geometry_changed.connect(this.on_window_geometry_changed);
134
 
        window.name_changed.connect(this.on_window_name_changed);
135
 
    }
136
 
    
137
 
    private void on_screen_window_closed(Wnck.Window window) {
138
 
        this.windows.remove(window);
139
 
        window.workspace_changed.disconnect(this.on_window_workspace_changed);
140
 
        window.geometry_changed.disconnect(this.on_window_geometry_changed);
141
 
        window.name_changed.disconnect(this.on_window_name_changed);
142
 
        this.remove_window(window);
143
 
    }
144
 
    
145
 
    private void on_window_geometry_changed(Wnck.Window window) {
146
 
        bool window_should_be_shown = this.window_should_be_shown(window);
147
 
        bool prev_window_should_be_shown = this.get_button_for_window(window) != null;
148
 
        
149
 
        if (window_should_be_shown != prev_window_should_be_shown) {
150
 
            if (window_should_be_shown) {
151
 
                this.add_window(window);
152
 
            } else {
153
 
                this.remove_window(window);
154
 
            }
155
 
        }
156
 
    }
157
 
    
158
 
    private void on_screen_active_window_changed(Wnck.Window? previous_active_window) {
159
 
        if (previous_active_window != null) {
160
 
            var button_previous_active = this.get_button_for_window(previous_active_window);
161
 
            if (button_previous_active != null) {
162
 
                button_previous_active.set_active(false);
163
 
            }
164
 
        }
165
 
    
166
 
        var active_window = screen.get_active_window();
167
 
        if (active_window != null) {
168
 
            var button = this.get_button_for_window(active_window);
169
 
            if (button != null) {
170
 
                button.set_active(true);
171
 
            }
172
 
        }
173
 
    }
174
 
    
175
 
    private void on_window_name_changed(Wnck.Window window) {
176
 
        var button = this.get_button_for_window(window);
177
 
        if (button != null) {
178
 
            button.label.set_text(window.get_name());
179
 
        }
180
 
    }
181
 
    
182
 
    private void on_window_workspace_changed(Wnck.Window window) {
183
 
        bool window_should_be_shown = this.window_should_be_shown(window);
184
 
        bool prev_window_should_be_shown = this.get_button_for_window(window) != null;
185
 
        
186
 
        if (window_should_be_shown != prev_window_should_be_shown) {
187
 
            if (window_should_be_shown) {
188
 
                this.add_window(window);
189
 
            } else {
190
 
                this.remove_window(window);
191
 
            }
192
 
        }
193
 
    }
194
 
    
195
 
    private void on_active_workspace_changed() {
196
 
        this.update_window_buttons();
197
 
    }
198
 
 
199
 
    private bool on_workspace_checker_timeout() {
200
 
        if (screen.get_active_workspace() == null) {
201
 
            return true;
202
 
        } else {
203
 
            this.update_window_buttons();
204
 
            return false;
205
 
        }
206
 
    }
207
 
    
208
 
    private void update_window_icon_geometries() {
209
 
        foreach (var child in (this.widget as Gtk.Box).get_children()) {
210
 
            var button = child as WindowButton;
211
 
            Gtk.Allocation allocation;
212
 
            button.get_allocation(out allocation);
213
 
            
214
 
            int x, y, width, height;
215
 
            button.get_parent_window().get_origin(out x, out y);
216
 
            
217
 
            x += allocation.x;
218
 
            y += allocation.y;
219
 
            width = allocation.width;
220
 
            height = allocation.height;
221
 
            
222
 
            Log.info("(%d, %d)  %d x %d".printf(x, y, width, height));
223
 
            button.window.set_icon_geometry(x, y, width, height);
224
 
        }
225
 
    }
226
 
 
227
 
    /* Public Functions */
228
 
    public override bool set_xml_property(string name, string value) {
229
 
        bool handled = base.set_xml_property(name, value);
230
 
        if (handled) {
231
 
            return true;
232
 
        }
233
 
        switch (name) {
234
 
            case "ellipsis":
235
 
                return true;
236
 
            default:
237
 
                return false;
238
 
        }
239
 
    }
240
 
}
241
 
 
242
 
public class WindowButton : Gtk.ToggleButton {
243
 
    
244
 
    public Wnck.Window window;
245
 
    public QuickLists.QuickList? quicklist;
246
 
    public new Gtk.Label label;
247
 
    
248
 
    private WindowMenu menu;
249
 
    private new bool pressed = false;
250
 
    
251
 
    public WindowButton(Wnck.Window window) {
252
 
        this.window = window;
253
 
        this.button_press_event.connect(this.on_button_press_event);
254
 
        this.button_release_event.connect(this.on_button_release_event);
255
 
        
256
 
        var hbox = new Gtk.HBox(false, 6);
257
 
        this.label = new Gtk.Label(window.get_name());
258
 
        var pixbuf = window.get_mini_icon();
259
 
        var image = new Gtk.Image.from_pixbuf(pixbuf);
260
 
        
261
 
        label.set_ellipsize(Pango.EllipsizeMode.END);
262
 
        label.set_alignment(0.0f, 0.5f);
263
 
        
264
 
        hbox.pack_start(image, false, false);
265
 
        hbox.pack_start(label, true, true);
266
 
        this.add(hbox);
267
 
        
268
 
        hbox.show_all();
269
 
        
270
 
        try {
271
 
            this.quicklist = new QuickLists.QuickList.from_window(window);
272
 
        } catch (QuickLists.Error e) {
273
 
            this.quicklist = null;
274
 
        }
275
 
        
276
 
        this.menu = new WindowMenu(this.window, this.quicklist);
277
 
    }
278
 
    
279
 
    private bool on_button_press_event(Gdk.EventButton event) {
280
 
        this.pressed = true;
281
 
        return false;
282
 
    }
283
 
    
284
 
    private bool on_button_release_event(Gdk.EventButton event) {
285
 
        if (this.pressed) {
286
 
            this.on_click_event(event);
287
 
        }
288
 
        this.pressed = false;
289
 
        return false;
290
 
    }
291
 
    
292
 
    private void on_click_event(Gdk.EventButton event) {
293
 
        if (Gdk.ModifierType.BUTTON1_MASK in event.state) {
294
 
            if (screen.get_active_window() == this.window) {
295
 
                this.window.minimize();
296
 
            } else {
297
 
                this.window.activate(event.time);
298
 
            }
299
 
        } else if (Gdk.ModifierType.BUTTON3_MASK in event.state) {
300
 
            this.menu.popup(null, null, null, event.button, event.time);
301
 
        }
302
 
    }
303
 
}
304
 
 
305
 
public class WindowMenu : Gtk.Menu {
306
 
 
307
 
    private QuickLists.QuickList? quicklist;
308
 
    private Wnck.Window window;
309
 
    
310
 
    private Gtk.MenuItem menuitem_maximize;
311
 
    private Gtk.MenuItem menuitem_minimize;
312
 
 
313
 
    public WindowMenu(Wnck.Window window, QuickLists.QuickList? quicklist) {
314
 
        this.quicklist = quicklist;
315
 
        this.window = window;
316
 
        this.create_quicklist_action_items();
317
 
        this.create_window_action_items();
318
 
        this.create_end_items();
319
 
        this.show_all();
320
 
        this.window.state_changed.connect(this.on_window_state_changed);
321
 
    }
322
 
    
323
 
    private void create_quicklist_action_items() {
324
 
        if (quicklist == null) {
325
 
            return;
326
 
        }
327
 
        foreach (QuickLists.Action action in quicklist.get_actions()) {
328
 
            var name = action.name; // Needs to be declared to avoid weird valac bug
329
 
            var menuitem = new Gtk.MenuItem.with_label(name);
330
 
            menuitem.activate.connect(this.on_quicklist_action_menuitem_activate);
331
 
            this.add(menuitem);
332
 
        }
333
 
        if (quicklist.get_actions().length > 0) {
334
 
            this.add_separator();
335
 
        }
336
 
        this.on_window_state_changed(window.get_state(), window.get_state());
337
 
    }
338
 
    
339
 
    private void create_window_action_items() {
340
 
        for (int i = 0; i < WindowAction.N_ACTIONS; i++) {
341
 
            var action = (WindowAction) i;
342
 
            var menuitem = new Gtk.MenuItem.with_label(action.get_name());
343
 
            menuitem.activate.connect(() => {
344
 
                this.on_window_action_menuitem_activate(action);
345
 
            });
346
 
            this.add(menuitem);
347
 
            switch(action) {
348
 
                case WindowAction.MAXIMIZE:
349
 
                    this.menuitem_maximize = menuitem;
350
 
                    break;
351
 
                case WindowAction.MINIMIZE:
352
 
                    this.menuitem_minimize = menuitem;
353
 
                    break;
354
 
                default:
355
 
                    break;
356
 
            }
357
 
        }
358
 
        this.add_separator();
359
 
    }
360
 
    
361
 
    private void create_end_items() {
362
 
        var menuitem_close = new Gtk.MenuItem.with_label(_("Close"));
363
 
        menuitem_close.activate.connect(this.on_menuitem_close_activate);
364
 
        this.add(menuitem_close);
365
 
    }
366
 
    
367
 
    private void add_separator() {
368
 
        var menuitem_separator = new Gtk.SeparatorMenuItem();
369
 
        this.add(menuitem_separator);
370
 
    }
371
 
    
372
 
    private void on_quicklist_action_menuitem_activate(Gtk.MenuItem menuitem) {
373
 
        var action = this.quicklist.get_action_from_name(menuitem.get_label());
374
 
        Process.spawn_async(null, action.exec.split(" "), null, SpawnFlags.SEARCH_PATH, null, null);
375
 
    }
376
 
    
377
 
    private void on_window_action_menuitem_activate(WindowAction action) {
378
 
        action.do_(this.window);
379
 
    }
380
 
    
381
 
    private void on_menuitem_close_activate() {
382
 
        this.window.close(Gtk.get_current_event_time());
383
 
    }
384
 
    
385
 
    private void on_window_state_changed(Wnck.WindowState changed_mask, Wnck.WindowState new_state) {
386
 
        bool maximized = (Wnck.WindowState.MAXIMIZED_VERTICALLY in new_state);
387
 
        bool minimized = (Wnck.WindowState.MINIMIZED in new_state);
388
 
        
389
 
        this.menuitem_maximize.set_label(WindowAction.MAXIMIZE.get_name(maximized));
390
 
        this.menuitem_minimize.set_label(WindowAction.MINIMIZE.get_name(minimized));
391
 
    }
392
 
}
393
 
 
394
 
}