1// -*- Mode: vala; indent-tabs-mode: nil; tab-width: 4 -*-
2/*-
3 * Copyright (c) 2011-2018 elementary, Inc. (https://elementary.io)
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 * Authored by: Maxwell Barvian <maxwell@elementary.io>
19 *              Corentin Noël <corentin@elementary.io>
20 */
21
22namespace Maya {
23    namespace Option {
24        private static bool add_event = false;
25        private static string show_day = null;
26    }
27
28    public class Application : Gtk.Application {
29        public MainWindow window;
30        public static GLib.Settings? saved_state = null;
31        public static GLib.Settings? wingpanel_settings = null;
32        public static bool run_in_background = false;
33
34        static construct {
35            if (SettingsSchemaSource.get_default ().lookup ("io.elementary.calendar.savedstate", true) != null) {
36                saved_state = new GLib.Settings ("io.elementary.calendar.savedstate");
37            }
38
39            if (GLib.SettingsSchemaSource.get_default ().lookup ("io.elementary.desktop.wingpanel.datetime", true) != null) {
40                wingpanel_settings = new GLib.Settings ("io.elementary.desktop.wingpanel.datetime");
41            }
42        }
43
44        construct {
45            flags |= ApplicationFlags.HANDLES_OPEN;
46
47            application_id = Build.EXEC_NAME;
48
49            GLib.Intl.setlocale (LocaleCategory.ALL, "");
50            GLib.Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
51            GLib.Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
52            GLib.Intl.textdomain (GETTEXT_PACKAGE);
53
54            var provider = new Gtk.CssProvider ();
55            provider.load_from_resource ("/io/elementary/calendar/Application.css");
56            Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default (), provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
57        }
58
59        public const OptionEntry[] APP_OPTIONS = {
60            { "add-event", 'a', 0, OptionArg.NONE, out Option.add_event, N_("Create an event"), null },
61            { "show-day", 's', 0, OptionArg.STRING, out Option.show_day, N_("Focus the given day"), N_("date") },
62            { "background", 'b', 0, OptionArg.NONE, out run_in_background, "Run the Application in background", null},
63            { null }
64        };
65
66        protected override void activate () {
67            if (run_in_background) {
68                run_in_background = false;
69                new Calendar.TodayEventMonitor ();
70                hold ();
71                return;
72            }
73
74            if (get_windows () != null) {
75                get_windows ().data.present (); // present window if app is already running
76                return;
77            }
78
79            if (Option.show_day != null) {
80                var date = Date ();
81                date.set_parse (Option.show_day);
82                if (date.valid () == true) {
83                    var datetime = get_selected_datetime ();
84                    datetime = datetime.add_years ((int)date.get_year () - datetime.get_year ());
85                    datetime = datetime.add_days ((int)date.get_day_of_year () - datetime.get_day_of_year ());
86
87                    saved_state.set_string ("selected-day", datetime.format ("%Y-%j"));
88                    saved_state.set_string ("month-page", datetime.format ("%Y-%m"));
89                } else {
90                    warning ("Invalid date '%s' - Ignoring", Option.show_day);
91                }
92            }
93
94            var calmodel = Calendar.EventStore.get_default ();
95            calmodel.load_all_sources ();
96
97            init_gui ();
98            window.show_all ();
99
100            if (Option.add_event) {
101                Idle.add (() => {
102                    window.on_tb_add_clicked (window.calview.selected_date);
103                    return false;
104                });
105            }
106
107            var granite_settings = Granite.Settings.get_default ();
108            var gtk_settings = Gtk.Settings.get_default ();
109
110            gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK;
111
112            granite_settings.notify["prefers-color-scheme"].connect (() => {
113                gtk_settings.gtk_application_prefer_dark_theme = granite_settings.prefers_color_scheme == Granite.Settings.ColorScheme.DARK;
114            });
115        }
116
117        public override void open (File[] files, string hint) {
118            if (get_windows () == null) {
119                var calmodel = Calendar.EventStore.get_default ();
120                calmodel.load_all_sources ();
121
122                init_gui ();
123                window.show_all ();
124            } else {
125                get_windows ().data.present (); // present window if app is already running
126            }
127
128            var dialog = new Maya.View.ImportDialog (files);
129            dialog.transient_for = window;
130            dialog.show_all ();
131        }
132
133        /**
134         * Initializes the graphical window and its components
135         */
136        void init_gui () {
137            int window_x, window_y;
138            var rect = Gtk.Allocation ();
139
140            saved_state.get ("window-position", "(ii)", out window_x, out window_y);
141            saved_state.get ("window-size", "(ii)", out rect.width, out rect.height);
142
143            window = new MainWindow (this);
144            window.title = _(Build.APP_NAME);
145            window.set_allocation (rect);
146
147            if (window_x != -1 || window_y != -1) {
148                window.move (window_x, window_y);
149            }
150
151            if (saved_state.get_boolean ("window-maximized")) {
152                window.maximize ();
153            }
154
155            window.destroy.connect (on_quit);
156
157            var quit_action = new SimpleAction ("quit", null);
158            quit_action.activate.connect (() => {
159                if (window != null) {
160                    window.destroy ();
161                }
162            });
163
164            add_action (quit_action);
165            set_accels_for_action ("app.quit", { "<Control>q" });
166        }
167
168        private void on_quit () {
169            Calendar.EventStore.get_default ().delete_trashed_calendars ();
170        }
171
172        public static DateTime get_selected_datetime () {
173            var selected_day = saved_state.get_string ("selected-day");
174            if (selected_day == null || selected_day == "") {
175                return new DateTime.now_local ();
176            }
177
178            var numbers = selected_day.split ("-", 2);
179            var dt = new DateTime.local (int.parse (numbers[0]), 1, 1, 0, 0, 0);
180            dt = dt.add_days (int.parse (numbers[1]) - 1);
181            return dt;
182        }
183    }
184
185    public static int main (string[] args) {
186        var context = new OptionContext (_("Calendar"));
187        context.add_main_entries (Application.APP_OPTIONS, "maya");
188        context.add_group (Gtk.get_option_group (true));
189
190        try {
191            context.parse (ref args);
192        } catch (Error e) {
193            warning (e.message);
194        }
195
196        GtkClutter.init (ref args);
197        var app = new Application ();
198
199        return app.run (args);
200    }
201}
202