# 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 wx
from timelinelib.canvas.data import TimeOutOfRangeLeftError
from timelinelib.canvas.data import TimeOutOfRangeRightError
from timelinelib.canvas import TimelineCanvas
from timelinelib.utils import ex_msg
from timelinelib.wxgui.cursor import Cursor
from timelinelib.wxgui.keyboard import Keyboard
from timelinelib.wxgui.components.maincanvas.inputhandler import InputHandler
from timelinelib.general.methodcontainer import MethodContainer
from timelinelib.canvas.data import TimePeriod
from timelinelib.canvas.timelinecanvas import LEFT_RESIZE_HANDLE
from timelinelib.canvas.timelinecanvas import RIGHT_RESIZE_HANDLE
from timelinelib.canvas.timelinecanvas import MOVE_HANDLE
[docs]class MainCanvas(TimelineCanvas):
[docs] def __init__(self, parent, main_frame, config):
TimelineCanvas.__init__(self, parent, config)
self._main_frame = main_frame
self.SetInputHandler(InputHandler(self))
self.dragscroll_timer = wx.Timer(self, -1)
self.Bind(wx.EVT_LEFT_DOWN, self._on_left_down)
self.Bind(wx.EVT_LEFT_DCLICK, self._on_left_dclick)
self.Bind(wx.EVT_LEFT_UP, self._on_left_up)
self.Bind(wx.EVT_MOTION, self._on_motion)
self.Bind(wx.EVT_TIMER, self._on_dragscroll, self.dragscroll_timer)
self.Bind(wx.EVT_MIDDLE_DOWN, self._on_middle_down)
self.Bind(wx.EVT_MOUSEWHEEL, self._on_mousewheel)
[docs] def SetDb(self, db):
db.display_in_canvas(self)
[docs] def Navigate(self, navigation_fn):
try:
TimelineCanvas.Navigate(self, navigation_fn)
except TimeOutOfRangeLeftError as e:
self._main_frame.DisplayStatus(_("Can't scroll more to the left"))
except TimeOutOfRangeRightError as e:
self._main_frame.DisplayStatus(_("Can't scroll more to the right"))
except (ValueError, OverflowError) as e:
self._main_frame.DisplayStatus(ex_msg(e))
else:
self._main_frame.DisplayStatus("")
[docs] def SetInputHandler(self, input_handler):
self._input_handler = input_handler
def _on_left_down(self, evt):
self._main_frame.main_panel.save_time_period()
self.SetFocus()
self._input_handler.left_mouse_down(self._get_cursor(evt),
self._get_keyboard(evt))
def _on_left_dclick(self, evt):
"""
Event handler used when the left mouse button has been double clicked.
If the timeline is readonly, no action is taken.
If the mouse hits an event, a dialog opens for editing this event.
Otherwise a dialog for creating a new event is opened.
"""
if self.IsReadOnly():
return
# Since the event sequence is, 1. EVT_LEFT_DOWN 2. EVT_LEFT_UP
# 3. EVT_LEFT_DCLICK we must compensate for the toggle_event_selection
# that occurs in the handling of EVT_LEFT_DOWN, since we still want
# the event(s) selected or deselected after a left doubleclick
# It doesn't look too god but I havent found any other way to do it.
self.ToggleEventSelection(evt)
def _get_cursor(self, evt):
return Cursor(evt.GetX(), evt.GetY())
def _get_keyboard(self, evt):
return Keyboard(evt.ControlDown(), evt.ShiftDown(), evt.AltDown())
def _on_left_up(self, event):
self._input_handler.left_mouse_up()
def _on_motion(self, evt):
self.DisplayBalloons(evt)
self._main_frame.DisplayStatus(self.GetTimelineInfoText(evt))
self.SetCursorShape(evt)
self._input_handler.mouse_moved(self._get_cursor(evt),
self._get_keyboard(evt))
def _on_dragscroll(self, event):
self._input_handler.dragscroll_timer_fired()
def _on_middle_down(self, evt):
self.CenterAtCursor(evt)
def _on_mousewheel(self, evt):
self._main_frame.main_panel.save_time_period()
keyboard = self._get_keyboard(evt)
methods = MethodContainer(
[
(Keyboard.CTRL, self.ZoomHorizontallyOnMouseWheel),
(Keyboard.SHIFT + Keyboard.CTRL, self.SpecialScrollVerticallyOnMouseWheel),
(Keyboard.SHIFT, self.ScrollVerticallyOnMouseWheel),
(Keyboard.ALT, self.ZoomVerticallyOnMouseWheel),
], default_method=self.ScrollHorizontallyOnMouseWheel
)
methods.select(keyboard.keys_combination)(evt)
[docs] def start_dragscroll_timer(self, milliseconds=-1, oneShot=False):
self.dragscroll_timer.Start(milliseconds, oneShot)
[docs] def stop_dragscroll_timer(self):
self.dragscroll_timer.Stop()
[docs] def toggle_event_selection(self, cursor, keyboard):
def toggle_event_selection_when_event_is_hit(event):
selected = not self.IsEventSelected(event)
if keyboard.ctrl:
self.SetEventSelected(event, selected)
else:
self.ClearSelectedEvents()
self.SetEventSelected(event, selected)
event = self.GetEventAt(cursor, keyboard.alt)
if event:
toggle_event_selection_when_event_is_hit(event)
else:
self.ClearSelectedEvents()
[docs] def format_current_pos_time_string(self, x):
tm = self.GetTimeAt(x)
return self.GetTimeType().format_period(TimePeriod(tm, tm))
[docs] def toggle_balloon_stickyness(self, event_with_balloon):
stick = not self.EventHasStickyBalloon(event_with_balloon)
self.SetEventStickyBalloon(event_with_balloon, stick)
if stick:
self.Redraw()
else:
if self.GetAppearance().get_balloons_visible():
self.SetHoveredEvent(event_with_balloon)
else:
self.SetHoveredEvent(None)
[docs] def hit_resize_handle(self, cursor, keyboard):
try:
event, hit_info = self.GetEventWithHitInfoAt(cursor)
if event.get_locked():
return None
if event.is_milestone():
return None
if not self.IsEventSelected(event):
return None
if hit_info == LEFT_RESIZE_HANDLE:
return wx.LEFT
if hit_info == RIGHT_RESIZE_HANDLE:
return wx.RIGHT
return None
except:
return None
[docs] def hit_move_handle(self, cursor):
event_and_hit_info = self.GetEventWithHitInfoAt(cursor)
if event_and_hit_info is None:
return False
(event, hit_info) = event_and_hit_info
if event.get_locked():
return False
if not self.IsEventSelected(event):
return False
return hit_info == MOVE_HANDLE
[docs]def step_function(x_value):
y_value = 0
if x_value < 0:
y_value = -1
elif x_value > 0:
y_value = 1
return y_value