1#!/usr/local/bin/python3.8
2# coding: utf-8
3
4# Copyright (C) 2018 Robert Griesel
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
18import gi
19gi.require_version('Gtk', '3.0')
20from gi.repository import Gtk
21from gi.repository import Gio
22
23import sys
24import gettext
25import argparse
26import os.path
27
28from setzer.workspace.workspace import Workspace
29import setzer.workspace.workspace_viewgtk as view
30from setzer.app.service_locator import ServiceLocator
31from setzer.dialogs.dialog_locator import DialogLocator
32
33
34class MainApplicationController(Gtk.Application):
35
36    def __init__(self):
37        Gtk.Application.__init__(self, application_id='org.cvfosammmm.Setzer', flags=Gio.ApplicationFlags.HANDLES_OPEN)
38
39    def do_startup(self):
40        Gtk.Application.do_startup(self)
41        self.is_active = False
42
43        # setup gettext
44        gettext.install('setzer', names=('ngettext',), localedir='@localedir_path@')
45
46        # get settings
47        self.settings = ServiceLocator.get_settings()
48
49        # init static variables
50        ServiceLocator.init_setzer_version('@setzer_version@')
51        ServiceLocator.init_resources_path('@resources_path@')
52        ServiceLocator.init_app_icons_path('@app_icons_path@')
53        resources_path = ServiceLocator.get_resources_path()
54        app_icons_path = ServiceLocator.get_app_icons_path()
55        Gtk.IconTheme.append_search_path(Gtk.IconTheme.get_default(), os.path.join(resources_path, 'icons'))
56        for folder in ['arrows', 'greek_letters', 'misc_math', 'misc_text', 'operators', 'relations']:
57            path = os.path.join(resources_path, 'symbols', folder)
58            Gtk.IconTheme.append_search_path(Gtk.IconTheme.get_default(), path)
59        Gtk.IconTheme.append_search_path(Gtk.IconTheme.get_default(), app_icons_path)
60
61        # init main window, model, dialogs
62        self.main_window = view.MainWindow(self)
63        ServiceLocator.init_main_window(self.main_window)
64
65    def do_open(self, files, number_of_files, hint=""):
66        if not self.is_active:
67            self.activate()
68            self.is_active = True
69
70        # open first session file if any
71        for file in files:
72            if file.get_path().endswith('.stzs'):
73                filename = file.get_path()
74
75                active_document = self.workspace.get_active_document()
76                documents = self.workspace.get_all_documents()
77                unsaved_documents = self.workspace.get_unsaved_documents()
78                dialog = DialogLocator.get_dialog('close_confirmation')
79                not_save_to_close_documents = dialog.run(unsaved_documents)['not_save_to_close_documents']
80
81                if len(not_save_to_close_documents) == 0:
82                    if documents != None:
83                        for document in documents:
84                            self.workspace.remove_document(document)
85                    self.workspace.load_documents_from_session_file(filename)
86
87        # open latex and bibtex files
88        for file in files:
89            if file.get_path().endswith('.tex') or file.get_path().endswith('.bib'):
90                document_candidate = self.workspace.get_document_by_filename(file.get_path())
91                if document_candidate != None:
92                    self.workspace.set_active_document(document_candidate)
93                else:
94                    self.workspace.create_document_from_filename(file.get_path(), activate=True)
95
96    def do_activate(self):
97        if not self.is_active:
98            self.activate()
99            self.is_active = True
100
101    def activate(self):
102        self.workspace = Workspace()
103        ServiceLocator.init_workspace(self.workspace)
104        ServiceLocator.init_autocomplete_provider(self.workspace)
105        DialogLocator.init_dialogs(self.main_window, self.workspace)
106
107        # init view
108        if self.settings.get_value('window_state', 'is_maximized'):
109            self.main_window.maximize()
110        else:
111            self.main_window.unmaximize()
112        self.main_window.set_default_size(self.settings.get_value('window_state', 'width'),
113                                          self.settings.get_value('window_state', 'height'))
114        self.main_window.current_width = self.settings.get_value('window_state', 'width')
115        self.main_window.current_height = self.settings.get_value('window_state', 'height')
116        self.first_window_state_event = True
117        self.main_window.show_all()
118
119        self.color_manager = ServiceLocator.get_color_manager()
120        self.fg_color = self.color_manager.get_theme_color('theme_fg_color')
121        self.bg_color = self.color_manager.get_theme_color('theme_bg_color')
122
123        self.main_window.connect('size-allocate', self.on_window_size_allocate)
124        self.main_window.connect('notify::is-maximized', self.on_window_maximize_event)
125        self.main_window.connect('delete-event', self.on_window_close)
126        self.main_window.connect('draw', self.on_window_draw)
127
128        # init controller
129        self.workspace.init_workspace_controller()
130        self.workspace.actions.quit_action.connect('activate', self.on_quit_action)
131
132    def on_window_size_allocate(self, main_window, window_size):
133        ''' signal handler, update window size variables '''
134
135        if not main_window.ismaximized:
136            main_window.current_width, main_window.current_height = main_window.get_size()
137
138    def on_window_maximize_event(self, main_window, state_event):
139        ''' signal handler, update window state variables '''
140
141        main_window.ismaximized = main_window.is_maximized()
142        return False
143
144    def on_window_draw(self, main_window, context):
145        ''' check for theme changes, update sidebar, textviews '''
146
147        fg_color = self.color_manager.get_theme_color('theme_fg_color')
148        bg_color = self.color_manager.get_theme_color('theme_bg_color')
149        if self.fg_color.red != fg_color.red or self.bg_color.red != bg_color.red:
150            self.fg_color = fg_color
151            self.bg_color = bg_color
152
153            try: documents = self.workspace.open_documents
154            except AttributeError: pass
155            else:
156                is_dark_mode = ServiceLocator.get_is_dark_mode()
157                for document in documents:
158                    document.set_dark_mode(is_dark_mode)
159        return False
160
161    def save_window_state(self):
162        main_window = self.main_window
163        self.settings.set_value('window_state', 'width', main_window.current_width)
164        self.settings.set_value('window_state', 'height', main_window.current_height)
165        self.settings.set_value('window_state', 'is_maximized', main_window.ismaximized)
166        self.settings.set_value('window_state', 'show_sidebar', self.workspace.show_sidebar)
167        self.settings.set_value('window_state', 'sidebar_paned_position', self.workspace.sidebar_position)
168        self.settings.set_value('window_state', 'show_help', self.workspace.show_help)
169        self.settings.set_value('window_state', 'show_preview', self.workspace.show_preview)
170        self.settings.set_value('window_state', 'preview_paned_position', self.workspace.preview_position)
171        self.settings.set_value('window_state', 'show_build_log', self.workspace.show_build_log)
172        self.settings.set_value('window_state', 'build_log_paned_position', self.workspace.build_log_position)
173        self.settings.pickle()
174
175    def on_window_close(self, window=None, parameter=None):
176        self.save_quit()
177        return True
178
179    def on_quit_action(self, action=None, parameter=None):
180        self.save_quit()
181
182    def save_quit(self):
183        for document in self.workspace.open_documents: document.state_manager.save_document_state()
184
185        documents = self.workspace.get_unsaved_documents()
186        active_document = self.workspace.get_active_document()
187
188        if documents == None or active_document == None or DialogLocator.get_dialog('close_confirmation').run(documents)['all_save_to_close']:
189            self.save_window_state()
190            self.workspace.save_to_disk()
191            self.quit()
192
193
194argparser = argparse.ArgumentParser(usage='%(prog)s [OPTION...] [FILE...]')
195argparser.add_argument('-V', '--version', action='version', version='@setzer_version@')
196argparser.add_argument('file', nargs='*', help=argparse.SUPPRESS)
197argparser.parse_args()
198
199main_controller = MainApplicationController()
200exit_status = main_controller.run(sys.argv)
201sys.exit(exit_status)
202