Source code for timelinelib.wxgui.frames.mainframe.mainframecontroller

# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg
#
# This file is part of Timeline.
#
# Timeline is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Timeline is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Timeline.  If not, see <http://www.gnu.org/licenses/>.


import os

from timelinelib.canvas.data import TimePeriod
from timelinelib.wxgui.dialogs.getfilepath.view import open_get_file_path_dialog, FUNC_SAVE_AS, FUNC_OPEN, FUNC_NEW
from timelinelib.db.utils import get_new_path_from_user
from timelinelib.wxgui.dialogs.slideshow.view import open_slideshow_dialog
from timelinelib.wxgui.utils import display_error_message
from timelinelib.wxgui.utils import display_warning_message
from timelinelib.wxgui.utils import get_user_ack
from timelinelib.dataexport.timelinexml import export_db_to_timeline_xml
from timelinelib.wxgui.utils import display_information_message
from timelinelib.wxgui.frames.mainframe.lockhandler import LockHandler, LockedException
from timelinelib.meta.about import get_title
from timelinelib.wxgui.dialogs.exceptionreport.exceptionreport import exception_report
from timelinelib.db.utils import get_modification_date


[docs]class MainFrameController: """ Controller for :class:`~timelinelib.wxgui.frames.mainframe.mainframe.MainFrame`. It is responsible for the business logic. """
[docs] def __init__(self, main_frame, db_open_fn, config): self._main_frame = main_frame self._db_open_fn = db_open_fn self._config = config self._timeline = None self._timelinepath = None self._last_changed = None self._lock_handler = LockHandler(main_frame) self._error_dialog = display_error_message
[docs] def populate(self): """ Find out if there is info saved in the config object of a last used timeline. If so open that timeline. """ if self._config.has_files(): self._open_or_create_timeline(self._config.first_file) elif self._config.has_recently_opened_files(): self.open_timeline_if_exists(self._config.get_latest_recently_opened_file())
[docs] def set_error_dialog(self, error_dialog): """For test purposes.""" self._error_dialog = error_dialog
[docs] def open_timeline_if_exists(self, path): """ Open the timeline file given by path. If the file doesn't exist an error message is displayed. """ if os.path.exists(path): self._open_or_create_timeline(path) else: display_error_message(_("File '%s' does not exist.") % path, self._main_frame)
# GUI updates
[docs] def set_title(self): """Update the title of the MainFrame window.""" self._main_frame.SetTitle(get_title(self._timelinepath))
[docs] def get_readonly_text_in_status_bar(self): """Return the text to be displayed in the third column of the status bar.""" if self._timeline is not None and self._timeline.is_read_only(): return _("read-only") return ""
# Concurrent editing
[docs] def ok_to_edit(self): """Returns True if no one else is editing the timeline.""" try: if self._timeline is None: return True if self._timeline.is_read_only(): return False if self._lock_handler.locked(self._timelinepath): display_warning_message("The Timeline is Locked by someone else.\nTry again later") return False if not os.path.exists(self._timelinepath): self._lock_handler.lock(self._timelinepath, self._timeline) return True last_changed = get_modification_date(self._timelinepath) if last_changed > self._last_changed: ack = get_user_ack( _("Someone else has changed the Timeline.\nYou have two choices!\n 1. Set Timeline in Read-Only mode.\n 2. Synchronize Timeline.\n\nDo you want to Synchronize?")) if ack: self._reload_from_disk() else: self.set_timeline_in_readonly_mode() return False if last_changed > 0: self._lock_handler.lock(self._timelinepath, self._timeline) return True except LockedException: return False
[docs] def edit_ends(self): """Used to clean up after an update, such as releasing the lock.""" if self._lock_handler.the_lock_is_mine(self._timelinepath): self._last_changed = get_modification_date(self._timelinepath) self._lock_handler.unlock(self._timelinepath)
# File Menu action handlers (New, Open, Open recent, Save as, Import, Export, Exit
[docs] def create_new_timeline(self, timetype=None): """File menu New-action handler that creates a new timline.""" path = get_new_path_from_user(timetype, self._timelinepath) self._open_or_create_timeline(path, timetype)
[docs] def open_existing_timeline(self): """File menu Open-action handler that opens an existing timeline.""" path = "" if self._timeline is not None: path = os.path.dirname(self._timeline.path) self._open_or_create_timeline(open_get_file_path_dialog(FUNC_OPEN, path))
[docs] def save_as(self): """File menu SaveAs-action handler to save the current timeline with a different name.""" path = open_get_file_path_dialog(FUNC_SAVE_AS, self._timeline.path) self._main_frame.main_panel.save_view_properties() if path is not None: assert path.endswith(".timeline") export_db_to_timeline_xml(self._timeline, path) self._open_or_create_timeline(path, save_current_data=False)
# View menu action handlers
[docs] def start_slide_show(self): """View menu action handler to create a slide show.""" open_slideshow_dialog(self._timeline, self._main_frame.canvas)
# Timeline menu action handlers
[docs] def measure_distance_between_two_events(self): """ Display an information message box that describes the distance between two selected events in the timeline. """ event_ids = self._main_frame.main_panel.get_ids_of_two_first_selected_events() caption = _("Distance between selected events") display_information_message(caption, self._timeline.get_distance_info(event_ids))
# Help menu action handlers
[docs] def open_gregorian_tutorial_timeline(self, *args, **kwargs): """Help menu action handler to open a sample Gregorian timeline, in read-only mode.""" self._open_or_create_timeline(":tutorial:")
[docs] def open_numeric_tutorial_timeline(self, *args, **kwargs): """Help menu action handler to open a sample Numeric timeline, in read-only mode.""" self._open_or_create_timeline(":numtutorial:")
[docs] def get_visible_categories(self): """ Used by the function export-to-listbox to select events to export. filtered_listbox_export is a flag that can be set in the settings dialog and decides if the export function shall filter out events having categories that are not shown in the timeline. Returns: list: A list of categories """ if self._config.filtered_listbox_export: return [cat for cat in self._timeline.get_categories() if self._main_frame.view_properties.is_category_visible(cat)] else: return [cat for cat in self._timeline.get_categories()]
[docs] def get_start_and_end_for_all_visible_events(self): """Return start time for the first visible event and the end time for the last visible event.""" return self._timeline.get_start_and_end_for_all_visible_events(self._main_frame.view_properties.filter_events)
[docs] def save_application_config(self): """Save user settings to percistent storage.""" self._config.set_window_size(self._main_frame.GetSize()) self._config.set_window_pos(self._main_frame.GetPosition()) self._config.window_maximized = self._main_frame.IsMaximized() self._config.sidebar_width = self._main_frame.main_panel.get_sidebar_width() self._config.write()
[docs] def set_timeline_in_readonly_mode(self): """ Is called to set a timeline in read-only-mode, which means that it cannot be edited. This state is not saved when the timeline application is closed or when another timeline is opened. When reopening the timeline file the read-only-mode is gone. """ self._timeline.set_readonly() self._main_frame.DisplayReadonly(self.get_readonly_text_in_status_bar()) self._main_frame.EnableDisableMenus()
[docs] def exit(self): """ Actions taken when the application is closed: * Stop timers * Save configuration data * Save timeline data * Destroy the MainFrame window """ self._main_frame.controller.save_application_config() try: if self._main_frame.controller.ok_to_edit(): self._main_frame.main_panel.save_view_properties() finally: self._main_frame.controller.edit_ends() self._main_frame.Destroy()
# Internal functions def _open_or_create_timeline(self, path, timetype=None, save_current_data=True): if path is not None: if save_current_data: self._main_frame.main_panel.save_view_properties() try: self._timeline = self._db_open_fn(path, timetype=timetype) except Exception as e: message = _("Unable to open timeline '%s'.") % path + "\n\n" + str(e) exception_report(self._error_dialog, message) else: self._config.append_recently_opened(path) self._main_frame.UpdateOpenRecentSubmenu() self._timelinepath = self._timeline.path = path self._main_frame.DisplayTimeline(self._timeline) self._last_changed = get_modification_date(self._timelinepath) self._main_frame.UpdateNavigationMenuItems() self._main_frame.EnableDisableMenus() def _reload_from_disk(self): self._open_or_create_timeline(self._timelinepath, save_current_data=False) self._main_frame.canvas.Redraw() def _period_for_all_visible_events(self): start_and_end = self.get_start_and_end_for_all_visible_events() if start_and_end: return TimePeriod(*start_and_end).zoom(-1) else: return self._timeline.displayed_period