~online-accounts/online-accounts-shotwell/packaging

« back to all changes in this revision

Viewing changes to .pc/04_no_resize_grip.patch/src/AppWindow.vala

  • Committer: Alberto Mardegan
  • Date: 2012-01-10 09:30:01 UTC
  • Revision ID: alberto.mardegan@canonical.com-20120110093001-d57569wxpidwc87n
Unapply all patches

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Copyright 2009-2011 Yorba Foundation
2
 
 *
3
 
 * This software is licensed under the GNU LGPL (version 2.1 or later).
4
 
 * See the COPYING file in this distribution. 
5
 
 */
6
 
 
7
 
public class FullscreenWindow : PageWindow {
8
 
    public const int TOOLBAR_INVOCATION_MSEC = 250;
9
 
    public const int TOOLBAR_DISMISSAL_SEC = 2;
10
 
    public const int TOOLBAR_CHECK_DISMISSAL_MSEC = 500;
11
 
    
12
 
    private Gtk.Window toolbar_window = new Gtk.Window(Gtk.WindowType.POPUP);
13
 
    private Gtk.UIManager ui = new Gtk.UIManager();
14
 
    private Gtk.ToolButton close_button = new Gtk.ToolButton.from_stock(Gtk.Stock.LEAVE_FULLSCREEN);
15
 
    private Gtk.ToggleToolButton pin_button = new Gtk.ToggleToolButton.from_stock(Resources.PIN_TOOLBAR);
16
 
    private bool is_toolbar_shown = false;
17
 
    private bool waiting_for_invoke = false;
18
 
    private time_t left_toolbar_time = 0;
19
 
    private bool switched_to = false;
20
 
    private bool is_toolbar_dismissal_enabled = true;
21
 
 
22
 
    public FullscreenWindow(Page page) {
23
 
        set_current_page(page);
24
 
 
25
 
        File ui_file = Resources.get_ui("fullscreen.ui");
26
 
 
27
 
        try {
28
 
            ui.add_ui_from_file(ui_file.get_path());
29
 
        } catch (Error err) {
30
 
            error("Error loading UI file %s: %s", ui_file.get_path(), err.message);
31
 
        }
32
 
        
33
 
        Gtk.ActionGroup action_group = new Gtk.ActionGroup("FullscreenActionGroup");
34
 
        action_group.add_actions(create_actions(), this);
35
 
        ui.insert_action_group(action_group, 0);
36
 
        ui.ensure_update();
37
 
 
38
 
        // add the accelerators for the hosted page as well
39
 
        Gtk.AccelGroup hosted_accel_group = page.ui.get_accel_group();
40
 
        if (hosted_accel_group != null)
41
 
            add_accel_group(hosted_accel_group);
42
 
        
43
 
        // the local accelerator group must come after host accelerator group so that they cover the
44
 
        // old accelerator group bindings
45
 
        Gtk.AccelGroup accel_group = ui.get_accel_group();
46
 
        if (accel_group != null)
47
 
            add_accel_group(accel_group);
48
 
        
49
 
        set_screen(AppWindow.get_instance().get_screen());
50
 
        
51
 
        // Needed so fullscreen will occur on correct monitor in multi-monitor setups
52
 
        Gdk.Rectangle monitor = get_monitor_geometry();
53
 
        move(monitor.x, monitor.y);
54
 
        
55
 
        set_border_width(0);
56
 
        
57
 
        pin_button.set_label(_("Pin Toolbar"));
58
 
        pin_button.set_tooltip_text(_("Pin the toolbar open"));
59
 
        pin_button.clicked.connect(update_toolbar_dismissal);
60
 
        
61
 
        close_button.set_tooltip_text(_("Leave fullscreen"));
62
 
        close_button.clicked.connect(on_close);
63
 
        
64
 
        Gtk.Toolbar toolbar = page.get_toolbar();
65
 
        toolbar.set_show_arrow(false);
66
 
 
67
 
        if (page is SlideshowPage) {
68
 
            // slideshow page doesn't own toolbar to hide it, subscribe to signal instead
69
 
            ((SlideshowPage) page).hide_toolbar.connect(hide_toolbar);
70
 
        } else {
71
 
            // only non-slideshow pages should have pin button
72
 
            toolbar.insert(pin_button, -1); 
73
 
        }
74
 
 
75
 
        page.set_cursor_hide_time(TOOLBAR_DISMISSAL_SEC * 1000);
76
 
        page.start_cursor_hiding();
77
 
 
78
 
        toolbar.insert(close_button, -1);
79
 
        
80
 
        // set up toolbar along bottom of screen
81
 
        toolbar_window.set_screen(get_screen());
82
 
        toolbar_window.set_border_width(0);
83
 
        toolbar_window.add(toolbar);
84
 
        
85
 
        toolbar_window.realize.connect(on_toolbar_realized);
86
 
        
87
 
        add(page);
88
 
 
89
 
        // call to set_default_size() saves one repaint caused by changing
90
 
        // size from default to full screen. In slideshow mode, this change
91
 
        // also causes pixbuf cache updates, so it really saves some work.
92
 
        set_default_size(monitor.width, monitor.height);
93
 
        
94
 
        // need to create a Gdk.Window to set masks
95
 
        fullscreen();
96
 
        show_all();
97
 
 
98
 
        // capture motion events to show the toolbar
99
 
        add_events(Gdk.EventMask.POINTER_MOTION_MASK);
100
 
        
101
 
        // start off with toolbar invoked, as a clue for the user
102
 
        invoke_toolbar();
103
 
    }
104
 
 
105
 
    public void disable_toolbar_dismissal() {
106
 
        is_toolbar_dismissal_enabled = false;
107
 
    }
108
 
    
109
 
    public void update_toolbar_dismissal() {
110
 
        is_toolbar_dismissal_enabled = !pin_button.get_active();
111
 
    }
112
 
 
113
 
    private Gdk.Rectangle get_monitor_geometry() {
114
 
        Gdk.Rectangle monitor;
115
 
 
116
 
        get_screen().get_monitor_geometry(
117
 
            get_screen().get_monitor_at_window(AppWindow.get_instance().get_window()), out monitor);
118
 
 
119
 
        return monitor;
120
 
    }
121
 
    
122
 
    public override bool configure_event(Gdk.EventConfigure event) {
123
 
        bool result = base.configure_event(event);
124
 
        
125
 
        if (!switched_to) {
126
 
            get_current_page().switched_to();
127
 
            switched_to = true;
128
 
        }
129
 
        
130
 
        return result;
131
 
    }
132
 
    
133
 
    private Gtk.ActionEntry[] create_actions() {
134
 
        Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
135
 
        
136
 
        Gtk.ActionEntry leave_fullscreen = { "LeaveFullscreen", Gtk.Stock.LEAVE_FULLSCREEN,
137
 
            TRANSLATABLE, "F11", TRANSLATABLE, on_close };
138
 
        leave_fullscreen.label = _("Leave _Fullscreen");
139
 
        leave_fullscreen.tooltip = _("Leave fullscreen");
140
 
        actions += leave_fullscreen;
141
 
 
142
 
        return actions;
143
 
    }
144
 
 
145
 
    public override bool key_press_event(Gdk.EventKey event) {
146
 
        // check for an escape/abort 
147
 
        if (Gdk.keyval_name(event.keyval) == "Escape") {
148
 
            on_close();
149
 
            
150
 
            return true;
151
 
        }
152
 
 
153
 
       // ...then let the base class take over
154
 
       return (base.key_press_event != null) ? base.key_press_event(event) : false;
155
 
    }
156
 
    
157
 
    private void on_close() {
158
 
        hide_toolbar();
159
 
        toolbar_window = null;
160
 
        
161
 
        AppWindow.get_instance().end_fullscreen();
162
 
    }
163
 
    
164
 
    public void close() {
165
 
        on_close();
166
 
    }
167
 
    
168
 
    public override void destroy() {
169
 
        Page? page = get_current_page();
170
 
        clear_current_page();
171
 
        
172
 
        if (page != null) {
173
 
            page.stop_cursor_hiding();
174
 
            page.switching_from();
175
 
        }
176
 
        
177
 
        base.destroy();
178
 
    }
179
 
    
180
 
    public override bool delete_event(Gdk.Event event) {
181
 
        on_close();
182
 
        AppWindow.get_instance().destroy();
183
 
        
184
 
        return true;
185
 
    }
186
 
    
187
 
    public override bool motion_notify_event(Gdk.EventMotion event) {
188
 
        if (!is_toolbar_shown) {
189
 
            // if pointer is in toolbar height range without the mouse down (i.e. in the middle of
190
 
            // an edit operation) and it stays there the necessary amount of time, invoke the
191
 
            // toolbar
192
 
            if (!waiting_for_invoke && is_pointer_in_toolbar()) {
193
 
                Timeout.add(TOOLBAR_INVOCATION_MSEC, on_check_toolbar_invocation);
194
 
                waiting_for_invoke = true;
195
 
            }
196
 
        }
197
 
        
198
 
        return (base.motion_notify_event != null) ? base.motion_notify_event(event) : false;
199
 
    }
200
 
    
201
 
    private bool is_pointer_in_toolbar() {
202
 
        int py, wy;
203
 
        get_display().get_pointer(null, null, out py, null);
204
 
        toolbar_window.window.get_geometry(null, out wy, null, null, null);
205
 
        return (py >= wy);
206
 
    }
207
 
    
208
 
    private bool on_check_toolbar_invocation() {
209
 
        waiting_for_invoke = false;
210
 
        
211
 
        if (is_toolbar_shown)
212
 
            return false;
213
 
        
214
 
        if (!is_pointer_in_toolbar())
215
 
            return false;
216
 
        
217
 
        invoke_toolbar();
218
 
        
219
 
        return false;
220
 
    }
221
 
    
222
 
    private void on_toolbar_realized() {
223
 
        Gtk.Requisition req;
224
 
        toolbar_window.size_request(out req);
225
 
        
226
 
        // place the toolbar in the center of the monitor along the bottom edge
227
 
        Gdk.Rectangle monitor = get_monitor_geometry();
228
 
        int tx = monitor.x + (monitor.width - req.width) / 2;
229
 
        if (tx < 0)
230
 
            tx = 0;
231
 
 
232
 
        int ty = monitor.y + monitor.height - req.height;
233
 
        if (ty < 0)
234
 
            ty = 0;
235
 
            
236
 
        toolbar_window.move(tx, ty);
237
 
        toolbar_window.set_opacity(Resources.TRANSIENT_WINDOW_OPACITY);
238
 
    }
239
 
 
240
 
    private void invoke_toolbar() {
241
 
        toolbar_window.show_all();
242
 
 
243
 
        is_toolbar_shown = true;
244
 
        
245
 
        Timeout.add(TOOLBAR_CHECK_DISMISSAL_MSEC, on_check_toolbar_dismissal);
246
 
    }
247
 
    
248
 
    private bool on_check_toolbar_dismissal() {
249
 
        if (!is_toolbar_shown)
250
 
            return false;
251
 
        
252
 
        if (toolbar_window == null)
253
 
            return false;
254
 
        
255
 
        // if dismissal is disabled, keep open but keep checking
256
 
        if ((!is_toolbar_dismissal_enabled))
257
 
            return true;
258
 
        
259
 
        // if the pointer is in toolbar range, keep it alive, but keep checking
260
 
        if (is_pointer_in_toolbar()) {
261
 
            left_toolbar_time = 0;
262
 
 
263
 
            return true;
264
 
        }
265
 
        
266
 
        // if this is the first time noticed, start the timer and keep checking
267
 
        if (left_toolbar_time == 0) {
268
 
            left_toolbar_time = time_t();
269
 
            
270
 
            return true;
271
 
        }
272
 
        
273
 
        // see if enough time has elapsed
274
 
        time_t now = time_t();
275
 
        assert(now >= left_toolbar_time);
276
 
 
277
 
        if (now - left_toolbar_time < TOOLBAR_DISMISSAL_SEC)
278
 
            return true;
279
 
        
280
 
        hide_toolbar();
281
 
        
282
 
        return false;
283
 
    }
284
 
    
285
 
    private void hide_toolbar() {
286
 
        toolbar_window.hide();
287
 
        is_toolbar_shown = false;
288
 
    }
289
 
}
290
 
 
291
 
// PageWindow is a Gtk.Window with essential functions for hosting a Page.  There may be more than
292
 
// one PageWindow in the system, and closing one does not imply exiting the application.
293
 
//
294
 
// PageWindow offers support for hosting a single Page; multiple Pages must be handled by the
295
 
// subclass.  A subclass should set current_page to the user-visible Page for it to receive
296
 
// various notifications.  It is the responsibility of the subclass to notify Pages when they're
297
 
// switched to and from, and other aspects of the Page interface.
298
 
public abstract class PageWindow : Gtk.Window {
299
 
    private Page current_page = null;
300
 
    private int busy_counter = 0;
301
 
    
302
 
    protected virtual void switched_pages(Page? old_page, Page? new_page) {
303
 
    }
304
 
    
305
 
    public PageWindow() {
306
 
        // the current page needs to know when modifier keys are pressed
307
 
        add_events(Gdk.EventMask.KEY_PRESS_MASK | Gdk.EventMask.KEY_RELEASE_MASK
308
 
            | Gdk.EventMask.STRUCTURE_MASK);
309
 
    }
310
 
    
311
 
    public Page? get_current_page() {
312
 
        return current_page;
313
 
    }
314
 
    
315
 
    public virtual void set_current_page(Page page) {
316
 
        if (current_page != null)
317
 
            current_page.clear_container();
318
 
        
319
 
        Page? old_page = current_page;
320
 
        current_page = page;
321
 
        current_page.set_container(this);
322
 
        
323
 
        switched_pages(old_page, page);
324
 
    }
325
 
    
326
 
    public virtual void clear_current_page() {
327
 
        if (current_page != null)
328
 
            current_page.clear_container();
329
 
        
330
 
        Page? old_page = current_page;
331
 
        current_page = null;
332
 
        
333
 
        switched_pages(old_page, null);
334
 
    }
335
 
    
336
 
    public override bool key_press_event(Gdk.EventKey event) {
337
 
        if (get_focus() is Gtk.Entry && get_focus().key_press_event(event))
338
 
            return true;
339
 
        
340
 
        if (current_page != null && current_page.notify_app_key_pressed(event))
341
 
            return true;
342
 
        
343
 
        return (base.key_press_event != null) ? base.key_press_event(event) : false;
344
 
    }
345
 
    
346
 
    public override bool key_release_event(Gdk.EventKey event) {
347
 
        if (get_focus() is Gtk.Entry && get_focus().key_release_event(event))
348
 
            return true;
349
 
       
350
 
        if (current_page != null && current_page.notify_app_key_released(event))
351
 
                return true;
352
 
        
353
 
        return (base.key_release_event != null) ? base.key_release_event(event) : false;
354
 
    }
355
 
 
356
 
    public override bool focus_in_event(Gdk.EventFocus event) {
357
 
        if (current_page != null && current_page.notify_app_focus_in(event))
358
 
                return true;
359
 
        
360
 
        return (base.focus_in_event != null) ? base.focus_in_event(event) : false;
361
 
    }
362
 
 
363
 
    public override bool focus_out_event(Gdk.EventFocus event) {
364
 
        if (current_page != null && current_page.notify_app_focus_out(event))
365
 
                return true;
366
 
        
367
 
        return (base.focus_out_event != null) ? base.focus_out_event(event) : false;
368
 
    }
369
 
    
370
 
    public override bool configure_event(Gdk.EventConfigure event) {
371
 
        if (current_page != null) {
372
 
            if (current_page.notify_configure_event(event))
373
 
                return true;
374
 
        }
375
 
 
376
 
        return (base.configure_event != null) ? base.configure_event(event) : false;
377
 
    }
378
 
 
379
 
    public void set_busy_cursor() {
380
 
        if (busy_counter++ > 0)
381
 
            return;
382
 
        
383
 
        window.set_cursor(new Gdk.Cursor(Gdk.CursorType.WATCH));
384
 
        spin_event_loop(10);
385
 
    }
386
 
    
387
 
    public void set_normal_cursor() {
388
 
        if (busy_counter <= 0) {
389
 
            busy_counter = 0;
390
 
            return;
391
 
        } else if (--busy_counter > 0) {
392
 
            return;
393
 
        }
394
 
        
395
 
        window.set_cursor(new Gdk.Cursor(Gdk.CursorType.LEFT_PTR));
396
 
        spin_event_loop(10);
397
 
    }
398
 
    
399
 
}
400
 
 
401
 
// AppWindow is the parent window for most windows in Shotwell (FullscreenWindow is the exception).
402
 
// There are multiple types of AppWindows (LibraryWindow, DirectWindow) for different tasks, but only 
403
 
// one AppWindow may exist per process.  Thus, if the user closes an AppWindow, the program exits.
404
 
//
405
 
// AppWindow also offers support for going into fullscreen mode.  It handles the interface
406
 
// notifications Page is expecting when switching back and forth.
407
 
public abstract class AppWindow : PageWindow {
408
 
    public const int DND_ICON_SCALE = 128;
409
 
    
410
 
    protected static AppWindow instance = null;
411
 
    
412
 
    private static FullscreenWindow fullscreen_window = null;
413
 
    private static CommandManager command_manager = null;
414
 
    
415
 
    // the AppWindow maintains its own UI manager because the first UIManager an action group is
416
 
    // added to is the one that claims its accelerators
417
 
    protected Gtk.UIManager ui = new Gtk.UIManager();
418
 
    protected Gtk.ActionGroup[] common_action_groups;
419
 
    protected bool maximized = false;
420
 
    protected Dimensions dimensions;
421
 
    protected int pos_x = 0;
422
 
    protected int pos_y = 0;
423
 
    
424
 
    private Gtk.ActionGroup common_action_group = new Gtk.ActionGroup("AppWindowGlobalActionGroup");
425
 
    
426
 
    public AppWindow() {
427
 
        // although there are multiple AppWindow types, only one may exist per-process
428
 
        assert(instance == null);
429
 
        instance = this;
430
 
 
431
 
        title = Resources.APP_TITLE;
432
 
        
433
 
        GLib.List<Gdk.Pixbuf> pixbuf_list = new GLib.List<Gdk.Pixbuf>();
434
 
        foreach (string resource in Resources.APP_ICONS)
435
 
            pixbuf_list.append(Resources.get_icon(resource, 0));
436
 
        set_default_icon_list(pixbuf_list);
437
 
 
438
 
        // restore previous size and maximization state
439
 
        if (this is LibraryWindow) {
440
 
            Config.Facade.get_instance().get_library_window_state(out maximized, out dimensions);
441
 
        } else {
442
 
            assert(this is DirectWindow);
443
 
            Config.Facade.get_instance().get_direct_window_state(out maximized, out dimensions);
444
 
        }
445
 
 
446
 
        set_default_size(dimensions.width, dimensions.height);
447
 
 
448
 
        if (maximized)
449
 
            maximize();
450
 
 
451
 
        assert(command_manager == null);
452
 
        command_manager = new CommandManager();
453
 
        command_manager.altered.connect(on_command_manager_altered);
454
 
        
455
 
        // Because the first UIManager to associated with an ActionGroup claims the accelerators,
456
 
        // need to create the AppWindow's ActionGroup early on and add it to an application-wide
457
 
        // UIManager.  In order to activate those accelerators, we need to create a dummy UI string
458
 
        // that lists all the common actions.  We build it on-the-fly from the actions associated
459
 
        // with each ActionGroup while we're adding the groups to the UIManager.
460
 
        common_action_groups = create_common_action_groups();
461
 
        foreach (Gtk.ActionGroup group in common_action_groups)
462
 
            ui.insert_action_group(group, 0);
463
 
        
464
 
        try {
465
 
            ui.add_ui_from_string(build_dummy_ui_string(common_action_groups), -1);
466
 
        } catch (Error err) {
467
 
            error("Unable to add AppWindow UI: %s", err.message);
468
 
        }
469
 
        
470
 
        ui.ensure_update();
471
 
        add_accel_group(ui.get_accel_group());
472
 
    }
473
 
    
474
 
    private Gtk.ActionEntry[] create_common_actions() {
475
 
        Gtk.ActionEntry[] actions = new Gtk.ActionEntry[0];
476
 
        
477
 
        Gtk.ActionEntry quit = { "CommonQuit", Gtk.Stock.QUIT, TRANSLATABLE, "<Ctrl>Q",
478
 
            TRANSLATABLE, on_quit };
479
 
        quit.label = _("_Quit");
480
 
        actions += quit;
481
 
 
482
 
        Gtk.ActionEntry about = { "CommonAbout", Gtk.Stock.ABOUT, TRANSLATABLE, null,
483
 
            TRANSLATABLE, on_about };
484
 
        about.label = _("_About");
485
 
        actions += about;
486
 
 
487
 
        Gtk.ActionEntry fullscreen = { "CommonFullscreen", Gtk.Stock.FULLSCREEN,
488
 
            TRANSLATABLE, "F11", TRANSLATABLE, on_fullscreen };
489
 
        fullscreen.label = _("Fulls_creen");
490
 
        actions += fullscreen;
491
 
 
492
 
        Gtk.ActionEntry help_contents = { "CommonHelpContents", Gtk.Stock.HELP,
493
 
            TRANSLATABLE, "F1", TRANSLATABLE, on_help_contents };
494
 
        help_contents.label = _("_Contents");
495
 
        actions += help_contents;
496
 
        
497
 
        Gtk.ActionEntry help_faq = { "CommonHelpFAQ", null, TRANSLATABLE, null, 
498
 
            TRANSLATABLE, on_help_faq };
499
 
        help_faq.label = _("_Frequently Asked Questions");
500
 
        actions += help_faq;
501
 
        
502
 
        Gtk.ActionEntry undo = { "CommonUndo", Gtk.Stock.UNDO, TRANSLATABLE, "<Ctrl>Z",
503
 
            TRANSLATABLE, on_undo };
504
 
        undo.label = Resources.UNDO_MENU;
505
 
        actions += undo;
506
 
        
507
 
        Gtk.ActionEntry redo = { "CommonRedo", Gtk.Stock.REDO, TRANSLATABLE, "<Ctrl><Shift>Z",
508
 
            TRANSLATABLE, on_redo };
509
 
        redo.label = Resources.REDO_MENU;
510
 
        actions += redo;
511
 
 
512
 
        Gtk.ActionEntry jump_to_file = { "CommonJumpToFile", Gtk.Stock.JUMP_TO, TRANSLATABLE, 
513
 
            "<Ctrl><Shift>M", TRANSLATABLE, on_jump_to_file };
514
 
        jump_to_file.label = Resources.JUMP_TO_FILE_MENU;
515
 
        actions += jump_to_file;
516
 
        
517
 
        Gtk.ActionEntry select_all = { "CommonSelectAll", Gtk.Stock.SELECT_ALL, TRANSLATABLE,
518
 
            "<Ctrl>A", TRANSLATABLE, on_select_all };
519
 
        select_all.label = Resources.SELECT_ALL_MENU;
520
 
        actions += select_all;
521
 
        
522
 
        Gtk.ActionEntry select_none = { "CommonSelectNone", null, null,
523
 
            "<Ctrl><Shift>A", TRANSLATABLE, on_select_none };
524
 
        actions += select_none;
525
 
        
526
 
        return actions;
527
 
    }
528
 
    
529
 
    protected abstract void on_fullscreen();
530
 
    
531
 
    public static bool has_instance() {
532
 
        return instance != null;
533
 
    }
534
 
    
535
 
    public static AppWindow get_instance() {
536
 
        return instance;
537
 
    }
538
 
 
539
 
    public static FullscreenWindow get_fullscreen() {
540
 
        return fullscreen_window;
541
 
    }
542
 
 
543
 
    public static Gtk.Builder create_builder(string glade_filename = "shotwell.glade", void *user = null) {
544
 
        Gtk.Builder builder = new Gtk.Builder();
545
 
        try {
546
 
            builder.add_from_file(AppDirs.get_resources_dir().get_child("ui").get_child(
547
 
                glade_filename).get_path());
548
 
        } catch(GLib.Error error) {
549
 
            warning("Unable to create Gtk.Builder: %s\n", error.message);
550
 
        }
551
 
        
552
 
        builder.connect_signals(user);
553
 
        
554
 
        return builder;
555
 
    }
556
 
    
557
 
    public static void error_message(string message, Gtk.Window? parent = null) {
558
 
        error_message_with_title(Resources.APP_TITLE, message, parent);
559
 
    }
560
 
    
561
 
    public static void error_message_with_title(string title, string message, Gtk.Window? parent = null) {
562
 
        // Per the Gnome HIG (http://library.gnome.org/devel/hig-book/2.32/windows-alert.html.en),            
563
 
        // alert-style dialogs mustn't have titles; we use the title as the primary text, and the
564
 
        // existing message as the secondary text.
565
 
        Gtk.MessageDialog dialog = new Gtk.MessageDialog.with_markup((parent != null) ? parent : get_instance(),
566
 
            Gtk.DialogFlags.MODAL, Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, "%s", build_alert_body_text(title, message));
567
 
            
568
 
        // Occasionally, with_markup doesn't actually do anything, but set_markup always works.
569
 
        dialog.set_markup(build_alert_body_text(title, message));
570
 
 
571
 
        dialog.use_markup = true;
572
 
        dialog.run();
573
 
        dialog.destroy();
574
 
    }
575
 
    
576
 
    public static bool negate_affirm_question(string message, string negative, string affirmative,
577
 
        string? title = null, Gtk.Window? parent = null) {
578
 
        Gtk.MessageDialog dialog = new Gtk.MessageDialog((parent != null) ? parent : get_instance(),
579
 
            Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, "%s", build_alert_body_text(title, message));
580
 
 
581
 
        dialog.set_markup(build_alert_body_text(title, message));
582
 
        dialog.add_buttons(negative, Gtk.ResponseType.NO, affirmative, Gtk.ResponseType.YES);
583
 
        
584
 
        bool response = (dialog.run() == Gtk.ResponseType.YES);
585
 
 
586
 
        dialog.destroy();
587
 
        
588
 
        return response;
589
 
    }
590
 
 
591
 
    public static Gtk.ResponseType negate_affirm_cancel_question(string message, string negative,
592
 
        string affirmative, string? title = null, Gtk.Window? parent = null) {
593
 
        Gtk.MessageDialog dialog = new Gtk.MessageDialog.with_markup((parent != null) ? parent : get_instance(),
594
 
            Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, "%s", build_alert_body_text(title, message));
595
 
 
596
 
        dialog.add_buttons(negative, Gtk.ResponseType.NO, affirmative, Gtk.ResponseType.YES,
597
 
            _("_Cancel"), Gtk.ResponseType.CANCEL);
598
 
        
599
 
        // Occasionally, with_markup doesn't actually enable markup, but set_markup always works.
600
 
        dialog.set_markup(build_alert_body_text(title, message));
601
 
        dialog.use_markup = true;
602
 
 
603
 
        int response = dialog.run();
604
 
        
605
 
        dialog.destroy();
606
 
        
607
 
        return (Gtk.ResponseType) response;
608
 
    }
609
 
    
610
 
    public static Gtk.ResponseType affirm_cancel_question(string message, string affirmative,
611
 
        string? title = null, Gtk.Window? parent = null) {
612
 
        Gtk.MessageDialog dialog = new Gtk.MessageDialog.with_markup((parent != null) ? parent : get_instance(),
613
 
            Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, message);
614
 
        dialog.title = (title != null) ? title : Resources.APP_TITLE;
615
 
        dialog.add_buttons(affirmative, Gtk.ResponseType.YES, _("_Cancel"),
616
 
            Gtk.ResponseType.CANCEL);
617
 
        
618
 
        int response = dialog.run();
619
 
        
620
 
        dialog.destroy();
621
 
        
622
 
        return (Gtk.ResponseType) response;
623
 
    }
624
 
    
625
 
    public static Gtk.ResponseType negate_affirm_all_cancel_question(string message, 
626
 
        string negative, string affirmative, string affirmative_all, string? title = null,
627
 
        Gtk.Window? parent = null) {
628
 
        Gtk.MessageDialog dialog = new Gtk.MessageDialog((parent != null) ? parent : get_instance(),
629
 
            Gtk.DialogFlags.MODAL, Gtk.MessageType.QUESTION, Gtk.ButtonsType.NONE, "%s", message);
630
 
        dialog.title = (title != null) ? title : Resources.APP_TITLE;
631
 
        dialog.add_buttons(negative, Gtk.ResponseType.NO, affirmative, Gtk.ResponseType.YES,
632
 
            affirmative_all, Gtk.ResponseType.APPLY,  _("_Cancel"), Gtk.ResponseType.CANCEL);
633
 
        
634
 
        int response = dialog.run();
635
 
        
636
 
        dialog.destroy();
637
 
        
638
 
        return (Gtk.ResponseType) response;
639
 
    }
640
 
    
641
 
    public static void database_error(DatabaseError err) {
642
 
        panic(_("A fatal error occurred when accessing Shotwell's library.  Shotwell cannot continue.\n\n%s").printf(
643
 
            err.message));
644
 
    }
645
 
    
646
 
    public static void panic(string msg) {
647
 
        critical(msg);
648
 
        error_message(msg);
649
 
        
650
 
        Application.get_instance().panic();
651
 
    }
652
 
    
653
 
    public abstract string get_app_role();
654
 
 
655
 
    protected void on_about() {
656
 
        Gtk.show_about_dialog(this,
657
 
            "version", Resources.APP_VERSION,
658
 
            "comments", get_app_role(),
659
 
            "copyright", Resources.COPYRIGHT,
660
 
            "website", Resources.YORBA_URL,
661
 
            "license", Resources.LICENSE,
662
 
            "website-label", _("Visit the Yorba web site"),
663
 
            "authors", Resources.AUTHORS,
664
 
            "logo", Resources.get_icon(Resources.ICON_ABOUT_LOGO, -1),
665
 
            "translator-credits", _("translator-credits"),
666
 
            null
667
 
        );
668
 
    }
669
 
 
670
 
    private void on_help_contents() {
671
 
        try {
672
 
            Resources.launch_help(get_screen());
673
 
        } catch (Error err) {
674
 
            error_message(_("Unable to display help: %s").printf(err.message));
675
 
        }
676
 
    }
677
 
    
678
 
    private void on_help_faq() {
679
 
        try {
680
 
            show_uri(Resources.FAQ_URL);
681
 
        } catch (Error err) {
682
 
            error_message(_("Unable to display FAQ: %s").printf(err.message));
683
 
        }
684
 
    }
685
 
    
686
 
    protected virtual void on_quit() {
687
 
        Application.get_instance().exit();
688
 
    }
689
 
 
690
 
    protected void on_jump_to_file() {
691
 
        if (get_current_page().get_view().get_selected_count() != 1)
692
 
            return;
693
 
 
694
 
        MediaSource? media = get_current_page().get_view().get_selected_at(0).get_source()
695
 
            as MediaSource;
696
 
        if (media == null)
697
 
            return;
698
 
        
699
 
        try {
700
 
            AppWindow.get_instance().show_file_uri(media.get_master_file().get_parent());
701
 
        } catch (Error err) {
702
 
            AppWindow.error_message(Resources.jump_to_file_failed(err));
703
 
        }
704
 
    }
705
 
    
706
 
    protected override void destroy() {
707
 
        on_quit();
708
 
    }
709
 
    
710
 
    public void show_file_uri(File file) throws Error {
711
 
        show_uri(file.get_uri());
712
 
    }
713
 
    
714
 
    public void show_uri(string url) throws Error {
715
 
        sys_show_uri(window.get_screen(), url);
716
 
    }
717
 
    
718
 
    protected virtual Gtk.ActionGroup[] create_common_action_groups() {
719
 
        Gtk.ActionGroup[] groups = new Gtk.ActionGroup[0];
720
 
        
721
 
        common_action_group.add_actions(create_common_actions(), this);
722
 
        groups += common_action_group;
723
 
        
724
 
        return groups;
725
 
    }
726
 
    
727
 
    public Gtk.ActionGroup[] get_common_action_groups() {
728
 
        return common_action_groups;
729
 
    }
730
 
    
731
 
    public virtual void replace_common_placeholders(Gtk.UIManager ui) {
732
 
    }
733
 
    
734
 
    public void go_fullscreen(Page page) {
735
 
        // if already fullscreen, use that
736
 
        if (fullscreen_window != null) {
737
 
            fullscreen_window.present();
738
 
            
739
 
            return;
740
 
        }
741
 
 
742
 
        get_position(out pos_x, out pos_y);
743
 
        hide();
744
 
        
745
 
        FullscreenWindow fsw = new FullscreenWindow(page);
746
 
        
747
 
        if (get_current_page() != null)
748
 
            get_current_page().switching_to_fullscreen(fsw);
749
 
        
750
 
        fullscreen_window = fsw;
751
 
        fullscreen_window.present();
752
 
    }
753
 
    
754
 
    public void end_fullscreen() {
755
 
        if (fullscreen_window == null)
756
 
            return;
757
 
        
758
 
        move(pos_x, pos_y);
759
 
 
760
 
        show_all();
761
 
        
762
 
        if (get_current_page() != null)
763
 
            get_current_page().returning_from_fullscreen(fullscreen_window);
764
 
        
765
 
        fullscreen_window.hide();
766
 
        fullscreen_window.destroy();
767
 
        fullscreen_window = null;
768
 
        
769
 
        present();
770
 
    }
771
 
    
772
 
    public Gtk.Action? get_common_action(string name) {
773
 
        foreach (Gtk.ActionGroup group in common_action_groups) {
774
 
            Gtk.Action? action = group.get_action(name);
775
 
            if (action != null)
776
 
                return action;
777
 
        }
778
 
        
779
 
        warning("No common action found: %s", name);
780
 
        
781
 
        return null;
782
 
    }
783
 
    
784
 
    public void set_common_action_sensitive(string name, bool sensitive) {
785
 
        Gtk.Action? action = get_common_action(name);
786
 
        if (action != null)
787
 
            action.sensitive = sensitive;
788
 
    }
789
 
    
790
 
    public void set_common_action_important(string name, bool important) {
791
 
        Gtk.Action? action = get_common_action(name);
792
 
        if (action != null)
793
 
            action.is_important = important;
794
 
    }
795
 
    
796
 
    public void set_common_action_visible(string name, bool visible) {
797
 
        Gtk.Action? action = get_common_action(name);
798
 
        if (action != null)
799
 
            action.visible = visible;
800
 
    }
801
 
    
802
 
    protected override void switched_pages(Page? old_page, Page? new_page) {
803
 
        update_common_action_availability(old_page, new_page);
804
 
        
805
 
        if (old_page != null) {
806
 
            old_page.get_view().contents_altered.disconnect(on_update_common_actions);
807
 
            old_page.get_view().selection_group_altered.disconnect(on_update_common_actions);
808
 
            old_page.get_view().items_state_changed.disconnect(on_update_common_actions);
809
 
        }
810
 
        
811
 
        if (new_page != null) {
812
 
            new_page.get_view().contents_altered.connect(on_update_common_actions);
813
 
            new_page.get_view().selection_group_altered.connect(on_update_common_actions);
814
 
            new_page.get_view().items_state_changed.connect(on_update_common_actions);
815
 
            
816
 
            update_common_actions(new_page, new_page.get_view().get_selected_count(),
817
 
                new_page.get_view().get_count());
818
 
        }
819
 
        
820
 
        base.switched_pages(old_page, new_page);
821
 
    }
822
 
    
823
 
    // This is called when a Page is switched out and certain common actions are simply
824
 
    // unavailable for the new one.  This is different than update_common_actions() in that that
825
 
    // call is made when state within the Page has changed.
826
 
    protected virtual void update_common_action_availability(Page? old_page, Page? new_page) {
827
 
        bool is_checkerboard = new_page is CheckerboardPage;
828
 
        
829
 
        set_common_action_sensitive("CommonSelectAll", is_checkerboard);
830
 
        set_common_action_sensitive("CommonSelectNone", is_checkerboard);
831
 
    }
832
 
    
833
 
    // This is a counterpart to Page.update_actions(), but for common Gtk.Actions
834
 
    // NOTE: Although CommonFullscreen is declared here, it's implementation is up to the subclasses,
835
 
    // therefore they need to update its action.
836
 
    protected virtual void update_common_actions(Page page, int selected_count, int count) {
837
 
        if (page is CheckerboardPage)
838
 
            set_common_action_sensitive("CommonSelectAll", count > 0);
839
 
        set_common_action_sensitive("CommonJumpToFile", selected_count == 1);
840
 
        
841
 
        decorate_undo_action();
842
 
        decorate_redo_action();
843
 
    }
844
 
    
845
 
    private void on_update_common_actions() {
846
 
        Page? page = get_current_page();
847
 
        if (page != null)
848
 
            update_common_actions(page, page.get_view().get_selected_count(), page.get_view().get_count());
849
 
    }
850
 
    
851
 
    public static CommandManager get_command_manager() {
852
 
        return command_manager;
853
 
    }
854
 
    
855
 
    private void on_command_manager_altered() {
856
 
        decorate_undo_action();
857
 
        decorate_redo_action();
858
 
    }
859
 
    
860
 
    private void decorate_command_manager_action(string name, string prefix,
861
 
        string default_explanation, CommandDescription? desc) {
862
 
        Gtk.Action? action = get_common_action(name);
863
 
        if (action == null)
864
 
            return;
865
 
        
866
 
        if (desc != null) {
867
 
            action.label = "%s %s".printf(prefix, desc.get_name());
868
 
            action.tooltip = desc.get_explanation();
869
 
            action.sensitive = true;
870
 
        } else {
871
 
            action.label = prefix;
872
 
            action.tooltip = default_explanation;
873
 
            action.sensitive = false;
874
 
        }
875
 
    }
876
 
    
877
 
    public void decorate_undo_action() {
878
 
        decorate_command_manager_action("CommonUndo", Resources.UNDO_MENU, "",
879
 
            get_command_manager().get_undo_description());
880
 
    }
881
 
    
882
 
    public void decorate_redo_action() {
883
 
        decorate_command_manager_action("CommonRedo", Resources.REDO_MENU, "",
884
 
            get_command_manager().get_redo_description());
885
 
    }
886
 
    
887
 
    private void on_undo() {
888
 
        command_manager.undo();
889
 
    }
890
 
    
891
 
    private void on_redo() {
892
 
        command_manager.redo();
893
 
    }
894
 
    
895
 
    private void on_select_all() {
896
 
        Page? page = get_current_page() as CheckerboardPage;
897
 
        if (page != null)
898
 
            page.get_view().select_all();
899
 
    }
900
 
    
901
 
    private void on_select_none() {
902
 
        Page? page = get_current_page() as CheckerboardPage;
903
 
        if (page != null)
904
 
            page.get_view().unselect_all();
905
 
    }
906
 
    
907
 
    public override bool configure_event(Gdk.EventConfigure event) {
908
 
        maximized = (window.get_state() == Gdk.WindowState.MAXIMIZED);
909
 
 
910
 
        if (!maximized)
911
 
            get_size(out dimensions.width, out dimensions.height);
912
 
 
913
 
        return base.configure_event(event);
914
 
    }
915
 
    
916
 
}
917