1/*
2 * Copyright (c) 2016 gnome-pomodoro contributors
3 *
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.
8 *
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.
13 *
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/>.
16 *
17 * Authors: Kamil Prusko <kamilprusko@gmail.com>
18 *
19 */
20
21using GLib;
22
23
24namespace GnomePlugin
25{
26    /* Leas amount of time in seconds between detected events
27     * to say that user become active
28     */
29    private const double IDLE_MONITOR_MIN_IDLE_TIME = 0.5;
30
31    private const string CURRENT_DESKTOP_VARIABLE = "XDG_CURRENT_DESKTOP";
32
33    public class ApplicationExtension : Peas.ExtensionBase, Pomodoro.ApplicationExtension, GLib.AsyncInitable
34    {
35        private Pomodoro.Timer                  timer;
36        private GLib.Settings                   settings;
37        private Pomodoro.CapabilityGroup        capabilities;
38        private GnomePlugin.GnomeShellExtension shell_extension;
39        private GnomePlugin.IdleMonitor         idle_monitor;
40        private uint                            become_active_id = 0;
41        private bool                            can_enable = false;
42        private double                          last_activity_time = 0.0;
43
44        construct
45        {
46            this.settings = Pomodoro.get_settings ().get_child ("preferences");
47            this.can_enable = GLib.Environment.get_variable (CURRENT_DESKTOP_VARIABLE) == "GNOME";
48
49            // try {
50            //     this.init_async.begin (GLib.Priority.DEFAULT, null);
51            // }
52            // catch (GLib.Error error) {
53            //     warning ("Failed to initialize ApplicationExtension");
54            // }
55        }
56
57        public async bool init_async (int               io_priority = GLib.Priority.DEFAULT,
58                                      GLib.Cancellable? cancellable = null)
59                                      throws GLib.Error
60        {
61            var application = Pomodoro.Application.get_default ();
62
63            /* Mutter IdleMonitor */
64            if (this.idle_monitor == null) {
65                this.capabilities = new Pomodoro.CapabilityGroup ("gnome");
66
67                try {
68                    this.idle_monitor = new GnomePlugin.IdleMonitor ();
69
70                    this.timer = Pomodoro.Timer.get_default ();
71                    this.timer.state_changed.connect_after (this.on_timer_state_changed);
72
73                    this.capabilities.add (new Pomodoro.Capability ("idle-monitor"));
74
75                    application.capabilities.add_group (this.capabilities, Pomodoro.Priority.HIGH);
76                }
77                catch (GLib.Error error) {
78                    // Gnome.IdleMonitor not available
79                }
80            }
81
82            /* GNOME Shell extension */
83            if (this.can_enable && this.shell_extension == null) {
84                this.shell_extension = new GnomePlugin.GnomeShellExtension (Config.EXTENSION_UUID,
85                                                                            Config.EXTENSION_DIR);
86
87                yield this.shell_extension.enable (cancellable);
88            }
89
90            return true;
91        }
92
93        ~ApplicationExtension ()
94        {
95            this.timer.state_changed.disconnect (this.on_timer_state_changed);
96
97            if (this.become_active_id != 0) {
98                this.idle_monitor.remove_watch (this.become_active_id);
99                this.become_active_id = 0;
100            }
101        }
102
103        private void on_timer_state_changed (Pomodoro.TimerState state,
104                                             Pomodoro.TimerState previous_state)
105        {
106            if (this.become_active_id != 0) {
107                this.idle_monitor.remove_watch (this.become_active_id);
108                this.become_active_id = 0;
109            }
110
111            if (state is Pomodoro.PomodoroState &&
112                previous_state is Pomodoro.BreakState &&
113                previous_state.is_completed () &&
114                this.settings.get_boolean ("pause-when-idle"))
115            {
116                this.become_active_id = this.idle_monitor.add_user_active_watch (this.on_become_active);
117
118                this.timer.pause ();
119            }
120        }
121
122        /**
123         * on_become_active callback
124         *
125         * We want to detect user/human activity so it sparse events.
126         */
127        private void on_become_active (GnomePlugin.IdleMonitor monitor,
128                                       uint                    id)
129        {
130            var timestamp = Pomodoro.get_current_time ();
131
132            if (timestamp - this.last_activity_time < IDLE_MONITOR_MIN_IDLE_TIME) {
133                this.become_active_id = 0;
134
135                this.timer.resume ();
136            }
137            else {
138                this.become_active_id = this.idle_monitor.add_user_active_watch (this.on_become_active);
139            }
140
141            this.last_activity_time = timestamp;
142        }
143    }
144
145    public class PreferencesDialogExtension : Peas.ExtensionBase, Pomodoro.PreferencesDialogExtension
146    {
147        private Pomodoro.PreferencesDialog dialog;
148
149        private GLib.Settings settings;
150        private GLib.List<Gtk.ListBoxRow> rows;
151
152        construct
153        {
154            this.settings = new GLib.Settings ("org.gnome.pomodoro.plugins.gnome");
155            this.dialog = Pomodoro.PreferencesDialog.get_default ();
156
157            this.setup_main_page ();
158        }
159
160        private void setup_main_page ()
161        {
162            var main_page = this.dialog.get_page ("main") as Pomodoro.PreferencesMainPage;
163
164            var hide_system_notifications_toggle = new Gtk.Switch ();
165            hide_system_notifications_toggle.valign = Gtk.Align.CENTER;
166
167            var row = this.create_row (_("Hide other notifications"),
168                                       hide_system_notifications_toggle);
169            row.name = "hide-system-notifications";
170            main_page.lisboxrow_sizegroup.add_widget (row);
171            main_page.desktop_listbox.add (row);
172            this.rows.prepend (row);
173
174            this.settings.bind ("hide-system-notifications",
175                                hide_system_notifications_toggle,
176                                "active",
177                                GLib.SettingsBindFlags.DEFAULT);
178        }
179
180        ~PreferencesDialogExtension ()
181        {
182            foreach (var row in this.rows) {
183                row.destroy ();
184            }
185
186            this.rows = null;
187        }
188
189        private Gtk.ListBoxRow create_row (string     label,
190                                           Gtk.Widget widget)
191        {
192            var name_label = new Gtk.Label (label);
193            name_label.halign = Gtk.Align.START;
194            name_label.valign = Gtk.Align.BASELINE;
195
196            var box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
197            box.pack_start (name_label, true, true, 0);
198            box.pack_start (widget, false, true, 0);
199
200            var row = new Gtk.ListBoxRow ();
201            row.activatable = false;
202            row.selectable = false;
203            row.add (box);
204            row.show_all ();
205
206            return row;
207        }
208    }
209}
210
211
212[ModuleInit]
213public void peas_register_types (GLib.TypeModule module)
214{
215    var object_module = module as Peas.ObjectModule;
216
217    object_module.register_extension_type (typeof (Pomodoro.ApplicationExtension),
218                                           typeof (GnomePlugin.ApplicationExtension));
219
220    object_module.register_extension_type (typeof (Pomodoro.PreferencesDialogExtension),
221                                           typeof (GnomePlugin.PreferencesDialogExtension));
222}
223