1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import os.path
20
21 import wx.lib.newevent
22
23 from timelinelib.canvas.data import TimePeriod
24 from timelinelib.config.dotfile import read_config
25 from timelinelib.config.paths import ICONS_DIR
26 from timelinelib.config.paths import LOCALE_DIR
27 from timelinelib.dataexport.timelinexml import export_db_to_timeline_xml
28 from timelinelib.db import db_open
29 from timelinelib.features.experimental.experimentalfeatures import ExperimentalFeatures
30 from timelinelib.meta.about import APPLICATION_NAME
31 from timelinelib.calendar.bosparanian.timetype import BosparanianTimeType
32 from timelinelib.calendar.num.timetype import NumTimeType
33 from timelinelib.utils import ex_msg
34 from timelinelib.wxgui.dialogs.changenowdate.view import ChangeNowDateDialog
35 from timelinelib.wxgui.dialogs.eraseditor.view import ErasEditorDialog
36 from timelinelib.wxgui.dialogs.setcategory.view import SetCategoryDialog
37 from timelinelib.wxgui.dialogs.timeeditor.view import TimeEditorDialog
38 from timelinelib.wxgui.frames.helpbrowserframe.helpbrowserframe import HelpBrowserFrame
39 from timelinelib.wxgui.frames.mainframe.mainframecontroller import LockedException
40 from timelinelib.wxgui.frames.mainframe.mainframecontroller import MainFrameController
41 from timelinelib.wxgui.timer import TimelineTimer
42 from timelinelib.wxgui.utils import display_categories_editor_moved_message
43 from timelinelib.wxgui.utils import display_error_message
44 from timelinelib.wxgui.utils import display_information_message
45 from timelinelib.wxgui.utils import WildcardHelper
46 import timelinelib.wxgui.frames.mainframe.guicreator as guic
47 from timelinelib.wxgui.frames.mainframe.controllerapi import MainFrameApiUsedByController
48 from timelinelib.wxgui.frames.mainframe.alertcontroller import AlertController
49 from timelinelib.wxgui.frames.mainframe.menucontroller import MenuController
50 from timelinelib.calendar.coptic.timetype import CopticTimeType
51 from timelinelib.calendar.pharaonic.timetype import PharaonicTimeType
52
53 CatsViewChangedEvent, EVT_CATS_VIEW_CHANGED = wx.lib.newevent.NewCommandEvent()
54
55
56 -class MainFrame(wx.Frame, guic.GuiCreator, MainFrameApiUsedByController):
57
58 - def __init__(self, application_arguments):
59 self.config = read_config(application_arguments.get_config_file_path())
60 wx.Frame.__init__(self, None, size=self.config.get_window_size(),
61 pos=self.config.get_window_pos(),
62 style=wx.DEFAULT_FRAME_STYLE, name="main_frame")
63 self.Bind(EVT_CATS_VIEW_CHANGED, self._on_cats_view_changed)
64
65 self.locale = wx.Locale(wx.LANGUAGE_DEFAULT)
66 self.locale.AddCatalogLookupPathPrefix(LOCALE_DIR)
67 self.locale.AddCatalog("wxstd")
68 self.help_browser = HelpBrowserFrame(self)
69 self.controller = MainFrameController(self, db_open, self.config)
70 self.menu_controller = MenuController()
71 self._set_initial_values_to_member_variables()
72 self._create_print_data()
73 self._set_experimental_features()
74 self._create_gui()
75 self.Maximize(self.config.window_maximized)
76 self.SetTitle(APPLICATION_NAME)
77 self.SetIcons(self._load_icon_bundle())
78 self.main_panel.show_welcome_panel()
79 self.enable_disable_menus()
80 self.controller.on_started(application_arguments)
81 self._create_and_start_timer()
82 self.prev_time_period = None
83
84 - def DisplayErrorMessage(self, message):
86
87 - def DisplayStatus(self, message):
88 self.status_bar_adapter.set_text(message)
89
90
93
94 - def display_time_editor_dialog(self, time_type, initial_time,
95 handle_new_time_fn, title):
96 dialog = TimeEditorDialog(self, self.config, time_type, initial_time, title)
97 dialog.ShowModal()
98 result = dialog.GetReturnCode()
99 if result == wx.ID_OK:
100 handle_new_time_fn(dialog.GetTime())
101 dialog.Destroy()
102
103 - def display_now_date_editor_dialog(self, handle_new_time_fn, title):
104 dialog = ChangeNowDateDialog(self, self.config, self.timeline, handle_new_time_fn, title)
105 dialog.Show()
106
108 self.prev_time_period = self.main_panel.get_time_period()
109
110
111 - def ok_to_edit(self):
112 try:
113 return self.controller.ok_to_edit()
114 except LockedException:
115 return False
116
118 return self.controller._get_lockpath()
119
120 - def edit_ends(self):
122
124 return self.main_panel.get_view_properties()
125
128
129
131 self.timeline = None
132 self.timeline_wildcard_helper = WildcardHelper(
133 _("Timeline files"), ["timeline", "ics"])
134 self.images_svg_wildcard_helper = WildcardHelper(
135 _("SVG files"), ["svg"])
136
138 self.printData = wx.PrintData()
139 self.printData.SetPaperId(wx.PAPER_A4)
140 self.printData.SetPrintMode(wx.PRINT_MODE_PRINTER)
141 self.printData.SetOrientation(wx.LANDSCAPE)
142
144 self.alert_dialog_open = False
145 self.timer = TimelineTimer(self)
146 self.timer.register(self._timer_tick)
147 self.timer.start(10000)
148
149 - def _timer_tick(self, evt):
150 self._handle_event_alerts()
151
154
156 bundle = wx.IconBundle()
157 for size in ["16", "32", "48"]:
158 iconpath = os.path.join(ICONS_DIR, "%s.png" % size)
159 icon = wx.Icon(wx.Bitmap(wx.Image(iconpath)))
160 bundle.AddIcon(icon)
161 return bundle
162
163
164 - def _create_new_timeline(self, timetype=None):
165 path = self._get_file_path()
166 if path is not None:
167 self.controller.open_timeline(path, timetype)
168
170 self._create_new_timeline(BosparanianTimeType())
171
173 self._create_new_timeline(CopticTimeType())
174
176 self._create_new_timeline(PharaonicTimeType())
177
179 self._create_new_timeline(NumTimeType())
180
181 - def _get_file_path(self):
182 path = None
183 wildcard = self.timeline_wildcard_helper.wildcard_string()
184 dialog = wx.FileDialog(self, message=_("Create Timeline"),
185 wildcard=wildcard, style=wx.FD_SAVE)
186 if dialog.ShowModal() == wx.ID_OK:
187 path = self.timeline_wildcard_helper.get_path(dialog)
188 if os.path.exists(path):
189 msg_first_part = _("The specified timeline already exists.")
190 msg_second_part = _("Opening timeline instead of creating new.")
191 wx.MessageBox("%s\n\n%s" % (msg_first_part, msg_second_part),
192 _("Information"),
193 wx.OK | wx.ICON_INFORMATION, self)
194 dialog.Destroy()
195 return path
196
198 dialog = wx.DirDialog(self, message=_("Create Timeline"))
199 if dialog.ShowModal() == wx.ID_OK:
200 self.controller.open_timeline(dialog.GetPath())
201 dialog.Destroy()
202
204 directory = ""
205 if self.timeline is not None:
206 directory = os.path.dirname(self.timeline.path)
207 wildcard = self.timeline_wildcard_helper.wildcard_string()
208 dialog = wx.FileDialog(self, message=_("Open Timeline"),
209 defaultDir=directory,
210 wildcard=wildcard, style=wx.FD_OPEN)
211 if dialog.ShowModal() == wx.ID_OK:
212 self.controller.open_timeline(dialog.GetPath())
213 dialog.Destroy()
214
215 - def _save_as(self):
216 new_timeline_path = self._get_new_timeline_path_from_user()
217 self._save_timeline_to_new_path(new_timeline_path)
218
220 defaultDir = os.path.dirname(self.timeline.path)
221 wildcard_helper = WildcardHelper(_("Timeline files"), ["timeline"])
222 wildcard = wildcard_helper.wildcard_string()
223 style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
224 message = _("Save Timeline As")
225 dialog = wx.FileDialog(self, message=message, defaultDir=defaultDir,
226 wildcard=wildcard, style=style)
227 if dialog.ShowModal() == wx.ID_OK:
228 new_timeline_path = wildcard_helper.get_path(dialog)
229 else:
230 new_timeline_path = None
231 dialog.Destroy()
232 return new_timeline_path
233
234 - def _save_timeline_to_new_path(self, new_timeline_path):
235 if new_timeline_path is not None:
236 assert new_timeline_path.endswith(".timeline")
237 export_db_to_timeline_xml(self.timeline, new_timeline_path)
238 self.controller.open_timeline(new_timeline_path)
239
240 - def _window_on_close(self, event):
241 self.timer.stop()
242 self._save_application_config()
243 try:
244 if self.ok_to_edit():
245 self.save_current_timeline_data()
246 finally:
247 self.edit_ends()
248 self.Destroy()
249
251 self.config.set_window_size(self.GetSize())
252 self.config.set_window_pos(self.GetPosition())
253 self.config.window_maximized = self.IsMaximized()
254 self.config.sidebar_width = self.main_panel.get_sidebar_width()
255 try:
256 self.config.write()
257 except IOError as ex:
258 friendly = _("Unable to write configuration file.")
259 msg = "%s\n\n%s" % (friendly, ex_msg(ex))
260 display_error_message(msg, self)
261
263 if self.timeline:
264 self.main_panel.save_view_properties(self.timeline)
265
266
268 event1, event2 = self._get_selected_events()
269 self._display_distance(event1.distance_to(event2))
270
272 event_id_1, event_id_2 = self.main_panel.get_ids_of_two_first_selected_events()
273 event1 = self.timeline.find_event_with_id(event_id_1)
274 event2 = self.timeline.find_event_with_id(event_id_2)
275 return event1, event2
276
277 - def _display_distance(self, distance):
278 caption = _("Distance between selected events")
279 if distance is None:
280 distance_text = _("Events are overlapping or distance is 0")
281 else:
282 distance_text = self.timeline.get_time_type().format_delta(distance)
283 display_information_message(caption, distance_text)
284
285 - def _set_category(self):
286 dialog = SetCategoryDialog(self, self.timeline)
287 dialog.ShowModal()
288 dialog.Destroy()
289 self.main_panel.redraw_timeline()
290
292 selected_event_ids = self.main_panel.get_selected_event_ids()
293 dialog = SetCategoryDialog(self, self.timeline, selected_event_ids)
294 dialog.ShowModal()
295 dialog.Destroy()
296
299
300 - def _edit_eras(self):
301 dialog = ErasEditorDialog(self, self.timeline, self.config)
302 dialog.ShowModal()
303 dialog.Destroy()
304 self.main_panel.redraw_timeline()
305
306 - def _fit_all_events(self):
307 all_period = self._period_for_all_visible_events()
308 if all_period is None:
309 return
310 if all_period.is_period():
311 self.main_panel.Navigate(lambda tp: tp.update(all_period.start_time, all_period.end_time))
312 else:
313 self.main_panel.Navigate(lambda tp: tp.center(all_period.mean_time()))
314
316 try:
317 visible_events = self._all_visible_events()
318 if len(visible_events) > 0:
319 start = self._first_time(visible_events)
320 end = self._last_time(visible_events)
321 return TimePeriod(start, end).zoom(-1)
322 else:
323 return None
324 except ValueError as ex:
325 display_error_message(str(ex))
326 return None
327
329 all_events = self.timeline.get_all_events()
330 return self.main_panel.get_visible_events(all_events)
331
332 - def _first_time(self, events):
335
336 - def _last_time(self, events):
339
341 events = self._all_visible_events()
342 first_time = self._first_time(events)
343 last_time = self._last_time(events)
344 return self.main_panel.get_export_periods(first_time, last_time)
345
346
348 if self.timeline is None:
349 return
350 if self.alert_dialog_open:
351 return
352 self._display_events_alerts()
353 self.alert_dialog_open = False
354
356 self.alert_dialog_open = True
357 all_events = self.timeline.get_all_events()
358 AlertController(self).display_events_alerts(all_events, self.timeline.get_time_type())
359