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