diff options
Diffstat (limited to 'src/dialogs/ProgressDialog.vala')
| -rw-r--r-- | src/dialogs/ProgressDialog.vala | 178 | 
1 files changed, 178 insertions, 0 deletions
| diff --git a/src/dialogs/ProgressDialog.vala b/src/dialogs/ProgressDialog.vala new file mode 100644 index 0000000..9368764 --- /dev/null +++ b/src/dialogs/ProgressDialog.vala @@ -0,0 +1,178 @@ +/* Copyright 2016 Software Freedom Conservancy Inc. + * Copyright 2017 Jens Georg <mail@jensge.org> + * + * This software is licensed under the GNU LGPL (version 2.1 or later). + * See the COPYING file in this distribution. + */ + +public class ProgressDialog : Gtk.Window { +    private Gtk.ProgressBar progress_bar = new Gtk.ProgressBar(); +    private Gtk.Button cancel_button = null; +    private Cancellable cancellable; +    private uint64 last_count = uint64.MAX; +    private int update_every = 1; +    private int minimum_on_screen_time_msec = 500; +    private ulong time_started; +#if UNITY_SUPPORT +    UnityProgressBar uniprobar = UnityProgressBar.get_instance(); +#endif + +    public ProgressDialog(Gtk.Window? owner, string text, Cancellable? cancellable = null) { +        this.cancellable = cancellable; + +        set_title(text); +        set_resizable(false); +        if (owner != null) +            set_transient_for(owner); +        set_modal(true); +        set_type_hint(Gdk.WindowTypeHint.DIALOG); + +        progress_bar.set_size_request(300, -1); +        progress_bar.set_show_text(true); + +        Gtk.Box vbox_bar = new Gtk.Box(Gtk.Orientation.VERTICAL, 0); +        vbox_bar.pack_start(progress_bar, true, false, 0); + +        if (cancellable != null) { +            cancel_button = new Gtk.Button.with_mnemonic(Resources.CANCEL_LABEL); +            cancel_button.clicked.connect(on_cancel); +            delete_event.connect(on_window_closed); +        } + +        Gtk.Box hbox = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 8); +        hbox.pack_start(vbox_bar, true, false, 0); +        if (cancel_button != null) +            hbox.pack_end(cancel_button, false, false, 0); + +        Gtk.Label primary_text_label = new Gtk.Label(""); +        primary_text_label.set_markup("<span weight=\"bold\">%s</span>".printf(text)); +        primary_text_label.xalign = 0.0f; +        primary_text_label.yalign = 0.5f; + +        Gtk.Box vbox = new Gtk.Box(Gtk.Orientation.VERTICAL, 12); +        vbox.pack_start(primary_text_label, false, false, 0); +        vbox.pack_start(hbox, true, false, 0); +        vbox.halign = Gtk.Align.CENTER; +        vbox.valign = Gtk.Align.CENTER; +        vbox.hexpand = true; +        vbox.vexpand = true; +        vbox.margin_start = 12; +        vbox.margin_end = 12; +        vbox.margin_top = 12; +        vbox.margin_bottom = 12; + +        add(vbox); + +        time_started = now_ms(); +    } + +    public override void realize() { +        base.realize(); + +        // if unable to cancel the progress bar, remove the close button +        if (cancellable == null) +            get_window().set_functions(Gdk.WMFunction.MOVE); +    } + +    public void update_display_every(int update_every) { +        assert(update_every >= 1); + +        this.update_every = update_every; +    } + +    public void set_minimum_on_screen_time_msec(int minimum_on_screen_time_msec) { +        this.minimum_on_screen_time_msec = minimum_on_screen_time_msec; +    } + +    public void set_fraction(int current, int total) { +        set_percentage((double) current / (double) total); +    } + +    public void set_percentage(double pct) { +        pct = pct.clamp(0.0, 1.0); + +        maybe_show_all(pct); + +        progress_bar.set_fraction(pct); +        progress_bar.set_text(_("%d%%").printf((int) (pct * 100.0))); + +#if UNITY_SUPPORT +        //UnityProgressBar: set progress +        uniprobar.set_progress(pct); +#endif +    } + +    public void set_status(string text) { +        progress_bar.set_text(text); + +#if UNITY_SUPPORT +        //UnityProgressBar: try to draw progress bar +        uniprobar.set_visible(true); +#endif +        show_all(); +    } + +    // This can be used as a ProgressMonitor delegate. +    public bool monitor(uint64 count, uint64 total, bool do_event_loop = true) { +        if ((last_count == uint64.MAX) || (count - last_count) >= update_every) { +            set_percentage((double) count / (double) total); +            last_count = count; +        } + +        bool keep_going = (cancellable != null) ? !cancellable.is_cancelled() : true; + +        // TODO: get rid of this.  non-trivial, as some progress-monitor operations are blocking +        // and need to allow the event loop to spin +        // +        // Important: Since it's possible the progress dialog might be destroyed inside this call, +        // avoid referring to "this" afterwards at all costs (in case all refs have been dropped) + +        if (do_event_loop) +            spin_event_loop(); + +        return keep_going; +    } + +    public new void close() { +#if UNITY_SUPPORT +        //UnityProgressBar: reset +        uniprobar.reset(); +#endif +        hide(); +        destroy(); +    } + +    private bool on_window_closed() { +        on_cancel(); +        return false; // return false so that the system handler will remove the window from +                      // the screen +    } + +    private void on_cancel() { +        if (cancellable != null) +            cancellable.cancel(); + +        cancel_button.sensitive = false; +    } + +    private void maybe_show_all(double pct) { +        // Appear only after a while because some jobs may take only a +        // fraction of second to complete so there's no point in showing progress. +        if (!this.visible && now_ms() - time_started > minimum_on_screen_time_msec) { +            // calculate percents completed in one ms +            double pps = pct * 100.0 / minimum_on_screen_time_msec; +            // calculate [very rough] estimate of time to complete in ms +            double ttc = 100.0 / pps; +            // If there is still more work to do for at least MINIMUM_ON_SCREEN_TIME_MSEC, +            // finally display the dialog. +            if (ttc > minimum_on_screen_time_msec) { +#if UNITY_SUPPORT +                //UnityProgressBar: try to draw progress bar +                uniprobar.set_visible(true); +#endif +                show_all(); +                spin_event_loop(); +            } +        } +    } +} | 
