Example #1
0
class TutorialTimelineCreator(object):

    def __init__(self):
        self.db = MemoryDB()
        from timelinelib.time.gregoriantime import GregorianTimeType
        self.db.time_type = GregorianTimeType()
        now = gregorian.from_time(self.db.time_type.now())
        self.start = self.get_time(now.year, now.month, 1)
        self.end = self.start + self.get_days_delta(30)
        self.db._set_displayed_period(TimePeriod(self.db.get_time_type(),
                                                 self.start, self.end))
        self.last_cat = None

    def add_category(self, name, color, font_color, make_last_added_parent=False):
        if make_last_added_parent:
            parent = self.last_cat
        else:
            parent = None
        self.prev_cat = self.last_cat 
        self.last_cat = Category(name, color, font_color, True, parent)
        self.db.save_category(self.last_cat)

    def add_event(self, text, description, start_add, end_add=None):
        start, end = self.calc_start_end(start_add, end_add)
        evt = Event(self.db.get_time_type(), start, end, text, self.last_cat)
        if description:
            evt.set_data("description", description)
        self.db.save_event(evt)

    def add_container(self, text, description, start_add, end_add=None):
        start, end = self.calc_start_end(start_add, end_add)
        container = Container(self.db.get_time_type(), start, end, text, self.prev_cat)
        self.db.save_event(container)
        return container

    def add_subevent(self, container, text, description, start_add, end_add=None):
        start, end = self.calc_start_end(start_add, end_add)
        evt = Subevent(self.db.get_time_type(), start, end, text, self.last_cat)
        if description:
            evt.set_data("description", description)
        self.db.save_event(evt)
        container.register_subevent(evt)
        
    def calc_start_end(self, start_add, end_add=None):
        start = self.start + start_add
        end = start
        if end_add is not None:
            end = self.start + end_add
        return (start, end)
    
    def get_db(self):
        return self.db

    def get_days_delta(self, days):
        if self.db.get_time_type().get_name() == u"gregoriantime":
            return delta_from_days(days)

    def get_time(self, year, month, day):
        if self.db.get_time_type().get_name() == u"gregoriantime":
            return Gregorian(year, month, day, 0, 0, 0).to_time()
Example #2
0
class TimelineViewSpec(unittest.TestCase):

    def test_initializes_displayed_period_from_db(self):
        self.init_view_with_db_with_period("1 Aug 2010", "2 Aug 2010")
        self.assert_displays_period("1 Aug 2010", "2 Aug 2010")

    def test_scrolls_timeline_when_dragging_mouse(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(10, "2 Aug 2010")
        self.init_view_with_db_with_period("11 Aug 2010", "31 Aug 2010")
        self.simulate_mouse_down_move_up((0, ANY_Y), (10, ANY_Y))
        self.assert_displays_period("10 Aug 2010", "30 Aug 2010")

    def test_zooms_timeline_when_shift_dragging_mouse(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(20, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((0, ANY_Y), (20, ANY_Y), shift_down=True)
        self.assert_displays_period("1 Aug 2010", "3 Aug 2010")

    def test_displays_zoom_intstructions_in_status_bar(self):
        self.init_view_with_db()
        self.controller.left_mouse_down(0, 0, ctrl_down=False, shift_down=True)
        self.assert_displays_status_text(_("Select region to zoom into"))

    def test_displays_period_to_short_message_when_zooming(self):
        self.given_time_at_x_is(0, "1 Aug 2010 00:00")
        self.given_time_at_x_is(1, "1 Aug 2010 00:01")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.assert_displays_status_text(_("Region too short"))

    def test_displays_nothing_if_period_ok_when_zooming(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(1, "2 Aug 2010")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.assert_displays_status_text("")

    def test_displays_period_to_long_message_when_zooming(self):
        self.given_time_at_x_is(0, "1 Aug 2000")
        self.given_time_at_x_is(200, "1 Aug 4000")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(200)
        self.assert_displays_status_text(_("Region too long"))

    def test_removes_zoom_instructions_when_zoom_done(self):
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((0, ANY_Y), (20, ANY_Y), shift_down=True)
        self.assert_displays_status_text("")

    def test_hightlights_selected_region_while_zooming(self):
        self.given_time_at_x_is(0, "1 Jan 2010")
        self.given_time_at_x_is(1, "1 Jan 2011")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.assert_highlights_region(("1 Jan 2010", "1 Jan 2011"))

    def test_highlights_last_valid_region_while_zooming(self):
        self.given_time_at_x_is(0, "1 Jan 2010")
        self.given_time_at_x_is(1, "1 Jan 2011")
        self.given_time_at_x_is(2000, "1 Jan 4010")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.move_mouse_to_x(2000)
        self.assert_highlights_region(("1 Jan 2010", "1 Jan 2011"))

    def test_highlights_no_region_when_zooming_is_completed(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(20, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((0, ANY_Y), (20, ANY_Y), shift_down=True)
        self.assert_highlights_region(None)

    def test_zooms_to_last_valid_selection(self):
        self.given_time_at_x_is(0, "1 Jan 2010")
        self.given_time_at_x_is(1, "1 Jan 2011")
        self.given_time_at_x_is(2000, "1 Jan 4010")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.move_mouse_to_x(2000)
        self.release_mouse()
        self.assert_displays_period("1 Jan 2010", "1 Jan 2011")

    def test_centers_displayed_period_around_middle_click_position(self):
        self.given_time_at_x_is(150, "15 Aug 2010")
        self.init_view_with_db_with_period("1 Aug 2010", "11 Aug 2010")
        self.controller.middle_mouse_clicked(150)
        self.assert_displays_period("10 Aug 2010", "20 Aug 2010")

    def test_zooms_timeline_by_10_percent_on_each_side_when_scrolling_while_holding_down_ctrl(self):
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.controller.mouse_wheel_moved(
            1, ctrl_down=True, shift_down=False, x=self.middle_x)
        self.assert_displays_period("3 Aug 2010", "19 Aug 2010")

    def test_displays_balloon_for_event_with_description(self):
        event = self.given_event_with(description="any description", pos=(40, 60), size=(20, 10))
        self.init_view_with_db()
        self.controller.mouse_moved(50, 65)
        self.fire_balloon_show_timer()
        self.assert_balloon_drawn_for_event(event)

    def test_hides_balloon_when_leaving_event(self):
        event = self.given_event_with(description="any description", pos=(40, 60), size=(20, 10))
        self.init_view_with_db()
        self.controller.mouse_moved(50, 65)
        self.fire_balloon_show_timer()
        self.assert_balloon_drawn_for_event(event)
        self.controller.mouse_moved(0, ANY_Y)
        self.fire_balloon_hide_timer()
        self.assert_balloon_drawn_for_event(None)

    def test_creates_event_when_ctrl_dragging_mouse(self):
        self.given_time_at_x_is(10, "1 Aug 2010")
        self.given_time_at_x_is(30, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((10, ANY_Y), (30, ANY_Y), ctrl_down=True)
        self.assert_created_event_with_period("1 Aug 2010", "3 Aug 2010")
        self.assert_timeline_redrawn()

    def test_displays_event_info_in_status_bar_when_hovering_event(self):
        event = self.given_event_with(text="Period event", pos=(40, 60), size=(20, 10))
        self.init_view_with_db()
        self.simulate_mouse_move(50, 65)
        self.assertTrue("Period event" in self.get_status_text())

    def test_removes_event_info_from_status_bar_when_un_hovering_event(self):
        self.init_view_with_db()
        self.simulate_mouse_move(0, ANY_Y)
        self.assertEquals("", self.get_status_text())

    def test_displays_hidden_event_count_in_status_bar(self):
        self.mock_drawer.hidden_event_count = 3
        self.init_view_with_db()
        self.assertTrue("3" in self.get_hidden_event_count_text())

    def test_displays_error_in_status_bar_when_scrolling_too_far_left(self):
        def navigate(time_period):
            raise TimeOutOfRangeLeftError()
        self.init_view_with_db()
        self.controller.navigate_timeline(navigate)
        self.assert_displays_status_text(_("Can't scroll more to the left"))

    def test_displays_error_in_status_bar_when_scrolling_too_far_right(self):
        def navigate(time_period):
            raise TimeOutOfRangeRightError()
        self.init_view_with_db()
        self.controller.navigate_timeline(navigate)
        self.assert_displays_status_text(_("Can't scroll more to the right"))

    def test_creates_event_when_double_clicking_surface(self):
        self.given_time_at_x_is(30, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_double_click(30, ANY_Y)
        self.assert_created_event_with_period("3 Aug 2010", "3 Aug 2010")
        self.assert_timeline_redrawn()

    def test_edits_event_when_double_clicking_it(self):
        event = self.given_event_with(pos=(40, 60), size=(20, 10))
        self.init_view_with_db()
        self.simulate_mouse_double_click(50, 65)
        self.view.open_event_editor_for.assert_called_with(event)
        self.assert_timeline_redrawn()

    def test_selects_and_deselects_event_when_clicking_on_it(self):
        event = self.given_event_with(pos=(30, 60), size=(50, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(40, 65)
        self.assert_is_selected(event)
        self.simulate_mouse_click(40, 65)
        self.assert_is_not_selected(event)

    def test_deselects_event_when_clicking_outside_of_it(self):
        event = self.given_event_with(pos=(30, 60), size=(50, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 65)
        self.assert_is_selected(event)
        self.simulate_mouse_click(0, ANY_Y)
        self.assert_is_not_selected(event)

    def test_selects_multiple_events_when_clicked_if_ctrl_is_pressed(self):
        period_event = self.given_event_with(pos=(30, 60), size=(50, 10))
        point_event = self.given_event_with(pos=(130, 30), size=(50, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 65)
        self.simulate_mouse_click(140, 35, ctrl_down=True)
        self.assert_is_selected(period_event)
        self.assert_is_selected(point_event)

    def test_displays_move_cursor_when_hovering_move_icon_on_event(self):
        event = self.given_event_with(pos=(0, 60), size=(30, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(10, 65)
        self.simulate_mouse_move(10, 65)
        self.assertTrue(self.view.set_move_cursor.called)

    def test_displays_resize_cursor_when_hovering_resize_icons_on_event(self):
        event = self.given_event_with(pos=(30, 60), size=(60, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 65)
        self.simulate_mouse_move(31, 65)
        self.simulate_mouse_move(89, 65)
        self.assertEquals(2, self.view.set_size_cursor.call_count)

    def test_resizes_event_when_dragging_right_drag_icon_on_event(self):
        event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 55), size=(60, 10))
        self.given_time_at_x_is(89, "4 Aug 2010")
        self.given_time_at_x_is(109, "11 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_click(50, 60)
        self.simulate_mouse_down_move_up((89, 60), (109, 60))
        self.assert_event_has_period(event, "4 Aug 2010", "11 Aug 2010")
        self.assert_timeline_redrawn()

    def test_resizes_event_when_dragging_left_drag_icon_on_event(self):
        event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 55), size=(60, 10))
        self.given_time_at_x_is(31, "4 Aug 2010")
        self.given_time_at_x_is(20, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_click(50, 60)
        self.simulate_mouse_down_move_up((31, 60), (20, 60))
        self.assert_event_has_period(event, "3 Aug 2010", "10 Aug 2010")
        self.assert_timeline_redrawn()

    def test_snaps_event_edge_when_resizing_event(self):
        self.given_time_at_x_is(89, "10 Aug 2010")
        self.given_time_at_x_is(120, "13 Aug 2010")
        self.mock_drawer.setup_snap("13 Aug 2010", "27 Aug 2010")
        event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 55), size=(60, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 60)
        self.simulate_mouse_down_move_up((89, 60), (120, 60))
        self.assert_event_has_period(event, "4 Aug 2010", "27 Aug 2010")
        self.assert_timeline_redrawn()

    def test_snaps_event_when_moving_event(self):
        self.given_time_at_x_is(31, "4 Aug 2010")
        self.given_time_at_x_is(10, "2 Aug 2010")
        self.mock_drawer.setup_snap("2 Aug 2010", "28 Jul 2010")
        event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 55), size=(60, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(55, 60)
        self.simulate_mouse_down_move_up((31, 60), (10, 60))
        self.assert_event_has_period(event, "28 Jul 2010", "10 Aug 2010")
        self.assert_timeline_redrawn()

    def test_scrolls_timeline_by_10_percent_when_moving_event(self):
        event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 55), size=(60, 10))
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.simulate_mouse_click(50, 60)
        self.controller.left_mouse_down(65, 60, ctrl_down=False, shift_down=False)
        self.controller.mouse_moved(199, 60)
        self.assertTrue(self.view.start_dragscroll_timer.called)
        self.controller.dragscroll_timer_fired()
        self.controller.left_mouse_up()
        self.assert_displays_period("3 Aug 2010", "23 Aug 2010")
        self.assert_timeline_redrawn()

    def test_scrolls_timeline_by_10_percent_when_resizing_event(self):
        event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 55), size=(60, 10))
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.simulate_mouse_click(50, 60)
        self.controller.left_mouse_down(89, 60, ctrl_down=False, shift_down=False)
        self.controller.mouse_moved(199, 60)
        self.assertTrue(self.view.start_dragscroll_timer.called)
        self.controller.dragscroll_timer_fired()
        self.controller.left_mouse_up()
        self.assert_displays_period("3 Aug 2010", "23 Aug 2010")
        self.assert_timeline_redrawn()

    def test_scrolls_with_10_percent_when_using_mouse_wheel(self):
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.controller.mouse_wheel_moved(
            -1, ctrl_down=False, shift_down=False, x=self.middle_x)
        self.assert_displays_period("3 Aug 2010", "23 Aug 2010")
        self.assert_timeline_redrawn()
        self.controller.mouse_wheel_moved(
            1, ctrl_down=False, shift_down=False, x=self.middle_x)
        self.assert_displays_period("1 Aug 2010", "21 Aug 2010")
        self.assert_timeline_redrawn()

    def test_deletes_selected_events_when_pressing_del_and_answering_yes_in_dialog(self):
        period_event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 60-5), size=(60, 10))
        point_event = self.given_event_with(start="15 Aug 2010", end="15 Aug 2010", pos=(130, 30-5), size=(50, 10))
        self.init_view_with_db()
        self.view.ask_question.return_value = wx.YES
        self.simulate_mouse_click(50, 60)
        self.controller.key_down(wx.WXK_DELETE, False)
        self.assertEquals([point_event], self.db.get_all_events())

    def test_deletes_no_selected_events_when_pressing_del_and_answering_no_in_dialog(self):
        period_event = self.given_event_with(start="4 Aug 2010", end="10 Aug 2010", pos=(30, 60-5), size=(60, 10))
        point_event = self.given_event_with(start="15 Aug 2010", end="15 Aug 2010", pos=(130, 30-5), size=(50, 10))
        self.init_view_with_db()
        self.view.ask_question.return_value = wx.NO
        self.simulate_mouse_click(50, 60)
        self.controller.key_down(wx.WXK_DELETE, False)
        self.assertTrue(period_event in self.db.get_all_events())
        self.assertTrue(point_event in self.db.get_all_events())

    def test_shift_scroll_changes_divider_line_value_and_redraws(self):
        self.init_view_with_db()
        self.controller.mouse_wheel_moved(
            1, ctrl_down=False, shift_down=True, x=self.middle_x)
        self.assertTrue(self.divider_line_slider.SetValue.called)
        self.assert_timeline_redrawn()

    def test_disables_view_if_no_timeline_set(self):
        self.controller.set_timeline(None)
        self.view.Disable.assert_called_with()

    def setUp(self):
        self.db = MemoryDB()
        self.view = Mock(DrawingAreaPanel)
        self.width = 10
        self.middle_x = self.width / 2
        self.view.GetSizeTuple.return_value = (self.width, 10)
        self.status_bar_adapter = Mock(StatusBarAdapter)
        self.config = Mock(Config)
        self.mock_drawer = MockDrawer()
        self.divider_line_slider = Mock()
        self.divider_line_slider.GetValue.return_value = 50
        self.fn_handle_db_error = Mock()
        self.controller = DrawingArea(
            self.view,
            self.status_bar_adapter,
            self.config,
            self.mock_drawer,
            self.divider_line_slider,
            self.fn_handle_db_error)

    def given_event_with(self, start="4 Aug 2010", end="10 Aug 2010",
                         text="Text", description=None,
                         pos=(0, 0), size=(0, 0)):
        event = Event(self.db.get_time_type(), human_time_to_py(start), human_time_to_py(end), text)
        if description is not None:
            event.set_data("description", description)
        self.db.save_event(event)
        self.mock_drawer.events_and_rects.append((event, wx.Rect(pos[0], pos[1], size[0], size[1])))
        return event

    def given_time_at_x_is(self, x, time):
        self.mock_drawer.setup_get_time_call(x, human_time_to_py(time))

    def init_view_with_db_with_period(self, start, end):
        self.db._set_displayed_period(py_period(start, end))
        self.init_view_with_db()

    def init_view_with_db(self):
        self.controller.set_timeline(self.db)

    def fire_balloon_show_timer(self):
        self.assertTrue(self.view.start_balloon_show_timer.called)
        self.controller.balloon_show_timer_fired()

    def fire_balloon_hide_timer(self):
        self.assertTrue(self.view.start_balloon_hide_timer.called)
        self.controller.balloon_hide_timer_fired()

    def start_shift_drag_at_x(self, x):
        ctrl_down = False
        shift_down = True
        self.controller.left_mouse_down(x, ANY_Y, ctrl_down, shift_down)

    def simulate_mouse_double_click(self, x, y):
        self.simulate_mouse_click(x, y)
        self.controller.left_mouse_dclick(x, y, ctrl_down=False)

    def simulate_mouse_click(self, x, y, ctrl_down=False):
        self.controller.left_mouse_down(x, y, ctrl_down=ctrl_down, shift_down=False)
        self.controller.left_mouse_up()

    def simulate_mouse_down_move_up(self, from_, to, ctrl_down=False, shift_down=False):
        x1, y1 = from_
        x2, y2 = to
        self.controller.left_mouse_down(x1, y1, ctrl_down, shift_down)
        self.controller.mouse_moved(x2, y2)
        self.controller.config.use_inertial_scrolling = False
        self.controller.left_mouse_up()

    def simulate_mouse_move(self, x, y):
        self.controller.mouse_moved(x, y)

    def move_mouse_to_x(self, x):
        self.controller.mouse_moved(x, ANY_Y)

    def release_mouse(self):
        self.controller.left_mouse_up()

    def get_status_text(self):
        self.assertTrue(self.status_bar_adapter.set_text.called)
        text = self.status_bar_adapter.set_text.call_args[0][0]
        return text

    def get_hidden_event_count_text(self):
        self.assertTrue(self.status_bar_adapter.set_hidden_event_count_text.called)
        text = self.status_bar_adapter.set_hidden_event_count_text.call_args[0][0]
        return text

    def assert_event_has_period(self, event, start, end):
        self.assertEquals(py_period(start, end), event.time_period)

    def assert_balloon_drawn_for_event(self, event):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertEquals(event, view_properties.hovered_event)

    def assert_highlights_region(self, start_end):
        if start_end is not None:
            start_end = (human_time_to_py(start_end[0]), human_time_to_py(start_end[1]))
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertEquals(start_end, view_properties.period_selection)

    def assert_displays_period(self, start, end):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertEquals(
            py_period(start, end), view_properties.displayed_period)

    def assert_timeline_redrawn(self):
        self.assertTrue(self.view.redraw_surface.called)

    def assert_created_event_with_period(self, start, end):
        self.view.open_create_event_editor.assert_called_with(
            human_time_to_py(start), human_time_to_py(end))

    def assert_is_selected(self, event):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertTrue(view_properties.is_selected(event))

    def assert_is_not_selected(self, event):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertFalse(view_properties.is_selected(event))

    def assert_displays_status_text(self, text):
        self.assertEquals(text, self.get_status_text())

    def get_view_properties_used_when_drawing(self):
        self.assertTrue(self.view.redraw_surface.called)
        draw_fn = self.view.redraw_surface.call_args[0][0]
        draw_fn(Mock())
        return self.mock_drawer.draw_view_properties
Example #3
0
class TimelineViewSpec(unittest.TestCase):
    def test_initializes_displayed_period_from_db(self):
        self.init_view_with_db_with_period("1 Aug 2010", "2 Aug 2010")
        self.assert_displays_period("1 Aug 2010", "2 Aug 2010")

    def test_scrolls_timeline_when_dragging_mouse(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(10, "2 Aug 2010")
        self.init_view_with_db_with_period("11 Aug 2010", "31 Aug 2010")
        self.simulate_mouse_down_move_up((0, ANY_Y), (10, ANY_Y))
        self.assert_displays_period("10 Aug 2010", "30 Aug 2010")

    def test_zooms_timeline_when_shift_dragging_mouse(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(20, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((0, ANY_Y), (20, ANY_Y),
                                         shift_down=True)
        self.assert_displays_period("1 Aug 2010", "3 Aug 2010")

    def test_displays_zoom_intstructions_in_status_bar(self):
        self.init_view_with_db()
        self.controller.left_mouse_down(0, 0, ctrl_down=False, shift_down=True)
        self.assert_displays_status_text(_("Select region to zoom into"))

    def test_displays_period_to_short_message_when_zooming(self):
        self.given_time_at_x_is(0, "1 Aug 2010 00:00")
        self.given_time_at_x_is(1, "1 Aug 2010 00:01")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.assert_displays_status_text(_("Region too short"))

    def test_displays_nothing_if_period_ok_when_zooming(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(1, "2 Aug 2010")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.assert_displays_status_text("")

    def test_displays_period_to_long_message_when_zooming(self):
        self.given_time_at_x_is(0, "1 Aug 2000")
        self.given_time_at_x_is(200, "1 Aug 4000")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(200)
        self.assert_displays_status_text(_("Region too long"))

    def test_removes_zoom_instructions_when_zoom_done(self):
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((0, ANY_Y), (20, ANY_Y),
                                         shift_down=True)
        self.assert_displays_status_text("")

    def test_hightlights_selected_region_while_zooming(self):
        self.given_time_at_x_is(0, "1 Jan 2010")
        self.given_time_at_x_is(1, "1 Jan 2011")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.assert_highlights_region(("1 Jan 2010", "1 Jan 2011"))

    def test_highlights_last_valid_region_while_zooming(self):
        self.given_time_at_x_is(0, "1 Jan 2010")
        self.given_time_at_x_is(1, "1 Jan 2011")
        self.given_time_at_x_is(2000, "1 Jan 4010")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.move_mouse_to_x(2000)
        self.assert_highlights_region(("1 Jan 2010", "1 Jan 2011"))

    def test_highlights_no_region_when_zooming_is_completed(self):
        self.given_time_at_x_is(0, "1 Aug 2010")
        self.given_time_at_x_is(20, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((0, ANY_Y), (20, ANY_Y),
                                         shift_down=True)
        self.assert_highlights_region(None)

    def test_zooms_to_last_valid_selection(self):
        self.given_time_at_x_is(0, "1 Jan 2010")
        self.given_time_at_x_is(1, "1 Jan 2011")
        self.given_time_at_x_is(2000, "1 Jan 4010")
        self.init_view_with_db()
        self.start_shift_drag_at_x(0)
        self.move_mouse_to_x(1)
        self.move_mouse_to_x(2000)
        self.release_mouse()
        self.assert_displays_period("1 Jan 2010", "1 Jan 2011")

    def test_centers_displayed_period_around_middle_click_position(self):
        self.given_time_at_x_is(150, "15 Aug 2010")
        self.init_view_with_db_with_period("1 Aug 2010", "11 Aug 2010")
        self.controller.middle_mouse_clicked(150)
        self.assert_displays_period("10 Aug 2010", "20 Aug 2010")

    def test_zooms_timeline_by_10_percent_on_each_side_when_scrolling_while_holding_down_ctrl(
            self):
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.controller.mouse_wheel_moved(1,
                                          ctrl_down=True,
                                          shift_down=False,
                                          x=self.middle_x)
        self.assert_displays_period("3 Aug 2010", "19 Aug 2010")

    def test_displays_balloon_for_event_with_description(self):
        event = self.given_event_with(description="any description",
                                      pos=(40, 60),
                                      size=(20, 10))
        self.init_view_with_db()
        self.controller.mouse_moved(50, 65)
        self.fire_balloon_show_timer()
        self.assert_balloon_drawn_for_event(event)

    def test_hides_balloon_when_leaving_event(self):
        event = self.given_event_with(description="any description",
                                      pos=(40, 60),
                                      size=(20, 10))
        self.init_view_with_db()
        self.controller.mouse_moved(50, 65)
        self.fire_balloon_show_timer()
        self.assert_balloon_drawn_for_event(event)
        self.controller.mouse_moved(0, ANY_Y)
        self.fire_balloon_hide_timer()
        self.assert_balloon_drawn_for_event(None)

    def test_creates_event_when_ctrl_dragging_mouse(self):
        self.given_time_at_x_is(10, "1 Aug 2010")
        self.given_time_at_x_is(30, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_down_move_up((10, ANY_Y), (30, ANY_Y),
                                         ctrl_down=True)
        self.assert_created_event_with_period("1 Aug 2010", "3 Aug 2010")
        self.assert_timeline_redrawn()

    def test_displays_event_info_in_status_bar_when_hovering_event(self):
        event = self.given_event_with(text="Period event",
                                      pos=(40, 60),
                                      size=(20, 10))
        self.init_view_with_db()
        self.simulate_mouse_move(50, 65)
        self.assertTrue("Period event" in self.get_status_text())

    def test_removes_event_info_from_status_bar_when_un_hovering_event(self):
        self.init_view_with_db()
        self.simulate_mouse_move(0, ANY_Y)
        self.assertEquals("", self.get_status_text())

    def test_displays_hidden_event_count_in_status_bar(self):
        self.mock_drawer.hidden_event_count = 3
        self.init_view_with_db()
        self.assertTrue("3" in self.get_hidden_event_count_text())

    def test_displays_error_in_status_bar_when_scrolling_too_far_left(self):
        def navigate(time_period):
            raise TimeOutOfRangeLeftError()

        self.init_view_with_db()
        self.controller.navigate_timeline(navigate)
        self.assert_displays_status_text(_("Can't scroll more to the left"))

    def test_displays_error_in_status_bar_when_scrolling_too_far_right(self):
        def navigate(time_period):
            raise TimeOutOfRangeRightError()

        self.init_view_with_db()
        self.controller.navigate_timeline(navigate)
        self.assert_displays_status_text(_("Can't scroll more to the right"))

    def test_creates_event_when_double_clicking_surface(self):
        self.given_time_at_x_is(30, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_double_click(30, ANY_Y)
        self.assert_created_event_with_period("3 Aug 2010", "3 Aug 2010")
        self.assert_timeline_redrawn()

    def test_edits_event_when_double_clicking_it(self):
        event = self.given_event_with(pos=(40, 60), size=(20, 10))
        self.init_view_with_db()
        self.simulate_mouse_double_click(50, 65)
        self.view.open_event_editor_for.assert_called_with(event)
        self.assert_timeline_redrawn()

    def test_selects_and_deselects_event_when_clicking_on_it(self):
        event = self.given_event_with(pos=(30, 60), size=(50, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(40, 65)
        self.assert_is_selected(event)
        self.simulate_mouse_click(40, 65)
        self.assert_is_not_selected(event)

    def test_deselects_event_when_clicking_outside_of_it(self):
        event = self.given_event_with(pos=(30, 60), size=(50, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 65)
        self.assert_is_selected(event)
        self.simulate_mouse_click(0, ANY_Y)
        self.assert_is_not_selected(event)

    def test_selects_multiple_events_when_clicked_if_ctrl_is_pressed(self):
        period_event = self.given_event_with(pos=(30, 60), size=(50, 10))
        point_event = self.given_event_with(pos=(130, 30), size=(50, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 65)
        self.simulate_mouse_click(140, 35, ctrl_down=True)
        self.assert_is_selected(period_event)
        self.assert_is_selected(point_event)

    def test_displays_move_cursor_when_hovering_move_icon_on_event(self):
        event = self.given_event_with(pos=(0, 60), size=(30, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(10, 65)
        self.simulate_mouse_move(10, 65)
        self.assertTrue(self.view.set_move_cursor.called)

    def test_displays_resize_cursor_when_hovering_resize_icons_on_event(self):
        event = self.given_event_with(pos=(30, 60), size=(60, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 65)
        self.simulate_mouse_move(31, 65)
        self.simulate_mouse_move(89, 65)
        self.assertEquals(2, self.view.set_size_cursor.call_count)

    def test_resizes_event_when_dragging_right_drag_icon_on_event(self):
        event = self.given_event_with(start="4 Aug 2010",
                                      end="10 Aug 2010",
                                      pos=(30, 55),
                                      size=(60, 10))
        self.given_time_at_x_is(89, "4 Aug 2010")
        self.given_time_at_x_is(109, "11 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_click(50, 60)
        self.simulate_mouse_down_move_up((89, 60), (109, 60))
        self.assert_event_has_period(event, "4 Aug 2010", "11 Aug 2010")
        self.assert_timeline_redrawn()

    def test_resizes_event_when_dragging_left_drag_icon_on_event(self):
        event = self.given_event_with(start="4 Aug 2010",
                                      end="10 Aug 2010",
                                      pos=(30, 55),
                                      size=(60, 10))
        self.given_time_at_x_is(31, "4 Aug 2010")
        self.given_time_at_x_is(20, "3 Aug 2010")
        self.init_view_with_db()
        self.simulate_mouse_click(50, 60)
        self.simulate_mouse_down_move_up((31, 60), (20, 60))
        self.assert_event_has_period(event, "3 Aug 2010", "10 Aug 2010")
        self.assert_timeline_redrawn()

    def test_snaps_event_edge_when_resizing_event(self):
        self.given_time_at_x_is(89, "10 Aug 2010")
        self.given_time_at_x_is(120, "13 Aug 2010")
        self.mock_drawer.setup_snap("13 Aug 2010", "27 Aug 2010")
        event = self.given_event_with(start="4 Aug 2010",
                                      end="10 Aug 2010",
                                      pos=(30, 55),
                                      size=(60, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(50, 60)
        self.simulate_mouse_down_move_up((89, 60), (120, 60))
        self.assert_event_has_period(event, "4 Aug 2010", "27 Aug 2010")
        self.assert_timeline_redrawn()

    def test_snaps_event_when_moving_event(self):
        self.given_time_at_x_is(31, "4 Aug 2010")
        self.given_time_at_x_is(10, "2 Aug 2010")
        self.mock_drawer.setup_snap("2 Aug 2010", "28 Jul 2010")
        event = self.given_event_with(start="4 Aug 2010",
                                      end="10 Aug 2010",
                                      pos=(30, 55),
                                      size=(60, 10))
        self.init_view_with_db()
        self.simulate_mouse_click(55, 60)
        self.simulate_mouse_down_move_up((31, 60), (10, 60))
        self.assert_event_has_period(event, "28 Jul 2010", "10 Aug 2010")
        self.assert_timeline_redrawn()

    def test_scrolls_timeline_by_10_percent_when_moving_event(self):
        event = self.given_event_with(start="4 Aug 2010",
                                      end="10 Aug 2010",
                                      pos=(30, 55),
                                      size=(60, 10))
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.simulate_mouse_click(50, 60)
        self.controller.left_mouse_down(65,
                                        60,
                                        ctrl_down=False,
                                        shift_down=False)
        self.controller.mouse_moved(199, 60)
        self.assertTrue(self.view.start_dragscroll_timer.called)
        self.controller.dragscroll_timer_fired()
        self.controller.left_mouse_up()
        self.assert_displays_period("3 Aug 2010", "23 Aug 2010")
        self.assert_timeline_redrawn()

    def test_scrolls_timeline_by_10_percent_when_resizing_event(self):
        event = self.given_event_with(start="4 Aug 2010",
                                      end="10 Aug 2010",
                                      pos=(30, 55),
                                      size=(60, 10))
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.simulate_mouse_click(50, 60)
        self.controller.left_mouse_down(89,
                                        60,
                                        ctrl_down=False,
                                        shift_down=False)
        self.controller.mouse_moved(199, 60)
        self.assertTrue(self.view.start_dragscroll_timer.called)
        self.controller.dragscroll_timer_fired()
        self.controller.left_mouse_up()
        self.assert_displays_period("3 Aug 2010", "23 Aug 2010")
        self.assert_timeline_redrawn()

    def test_scrolls_with_10_percent_when_using_mouse_wheel(self):
        self.init_view_with_db_with_period("1 Aug 2010", "21 Aug 2010")
        self.controller.mouse_wheel_moved(-1,
                                          ctrl_down=False,
                                          shift_down=False,
                                          x=self.middle_x)
        self.assert_displays_period("3 Aug 2010", "23 Aug 2010")
        self.assert_timeline_redrawn()
        self.controller.mouse_wheel_moved(1,
                                          ctrl_down=False,
                                          shift_down=False,
                                          x=self.middle_x)
        self.assert_displays_period("1 Aug 2010", "21 Aug 2010")
        self.assert_timeline_redrawn()

    def test_deletes_selected_events_when_pressing_del_and_answering_yes_in_dialog(
            self):
        period_event = self.given_event_with(start="4 Aug 2010",
                                             end="10 Aug 2010",
                                             pos=(30, 60 - 5),
                                             size=(60, 10))
        point_event = self.given_event_with(start="15 Aug 2010",
                                            end="15 Aug 2010",
                                            pos=(130, 30 - 5),
                                            size=(50, 10))
        self.init_view_with_db()
        self.view.ask_question.return_value = wx.YES
        self.simulate_mouse_click(50, 60)
        self.controller.key_down(wx.WXK_DELETE, False)
        self.assertEquals([point_event], self.db.get_all_events())

    def test_deletes_no_selected_events_when_pressing_del_and_answering_no_in_dialog(
            self):
        period_event = self.given_event_with(start="4 Aug 2010",
                                             end="10 Aug 2010",
                                             pos=(30, 60 - 5),
                                             size=(60, 10))
        point_event = self.given_event_with(start="15 Aug 2010",
                                            end="15 Aug 2010",
                                            pos=(130, 30 - 5),
                                            size=(50, 10))
        self.init_view_with_db()
        self.view.ask_question.return_value = wx.NO
        self.simulate_mouse_click(50, 60)
        self.controller.key_down(wx.WXK_DELETE, False)
        self.assertTrue(period_event in self.db.get_all_events())
        self.assertTrue(point_event in self.db.get_all_events())

    def test_shift_scroll_changes_divider_line_value_and_redraws(self):
        self.init_view_with_db()
        self.controller.mouse_wheel_moved(1,
                                          ctrl_down=False,
                                          shift_down=True,
                                          x=self.middle_x)
        self.assertTrue(self.divider_line_slider.SetValue.called)
        self.assert_timeline_redrawn()

    def test_disables_view_if_no_timeline_set(self):
        self.controller.set_timeline(None)
        self.view.Disable.assert_called_with()

    def setUp(self):
        self.db = MemoryDB()
        self.view = Mock(DrawingAreaPanel)
        self.width = 10
        self.middle_x = self.width / 2
        self.view.GetSizeTuple.return_value = (self.width, 10)
        self.status_bar_adapter = Mock(StatusBarAdapter)
        self.config = Mock(Config)
        self.mock_drawer = MockDrawer()
        self.divider_line_slider = Mock()
        self.divider_line_slider.GetValue.return_value = 50
        self.fn_handle_db_error = Mock()
        self.controller = DrawingArea(self.view, self.status_bar_adapter,
                                      self.config, self.mock_drawer,
                                      self.divider_line_slider,
                                      self.fn_handle_db_error)

    def given_event_with(self,
                         start="4 Aug 2010",
                         end="10 Aug 2010",
                         text="Text",
                         description=None,
                         pos=(0, 0),
                         size=(0, 0)):
        event = Event(self.db.get_time_type(), human_time_to_py(start),
                      human_time_to_py(end), text)
        if description is not None:
            event.set_data("description", description)
        self.db.save_event(event)
        self.mock_drawer.events_and_rects.append(
            (event, wx.Rect(pos[0], pos[1], size[0], size[1])))
        return event

    def given_time_at_x_is(self, x, time):
        self.mock_drawer.setup_get_time_call(x, human_time_to_py(time))

    def init_view_with_db_with_period(self, start, end):
        self.db._set_displayed_period(py_period(start, end))
        self.init_view_with_db()

    def init_view_with_db(self):
        self.controller.set_timeline(self.db)

    def fire_balloon_show_timer(self):
        self.assertTrue(self.view.start_balloon_show_timer.called)
        self.controller.balloon_show_timer_fired()

    def fire_balloon_hide_timer(self):
        self.assertTrue(self.view.start_balloon_hide_timer.called)
        self.controller.balloon_hide_timer_fired()

    def start_shift_drag_at_x(self, x):
        ctrl_down = False
        shift_down = True
        self.controller.left_mouse_down(x, ANY_Y, ctrl_down, shift_down)

    def simulate_mouse_double_click(self, x, y):
        self.simulate_mouse_click(x, y)
        self.controller.left_mouse_dclick(x, y, ctrl_down=False)

    def simulate_mouse_click(self, x, y, ctrl_down=False):
        self.controller.left_mouse_down(x,
                                        y,
                                        ctrl_down=ctrl_down,
                                        shift_down=False)
        self.controller.left_mouse_up()

    def simulate_mouse_down_move_up(self,
                                    from_,
                                    to,
                                    ctrl_down=False,
                                    shift_down=False):
        x1, y1 = from_
        x2, y2 = to
        self.controller.left_mouse_down(x1, y1, ctrl_down, shift_down)
        self.controller.mouse_moved(x2, y2)
        self.controller.config.use_inertial_scrolling = False
        self.controller.left_mouse_up()

    def simulate_mouse_move(self, x, y):
        self.controller.mouse_moved(x, y)

    def move_mouse_to_x(self, x):
        self.controller.mouse_moved(x, ANY_Y)

    def release_mouse(self):
        self.controller.left_mouse_up()

    def get_status_text(self):
        self.assertTrue(self.status_bar_adapter.set_text.called)
        text = self.status_bar_adapter.set_text.call_args[0][0]
        return text

    def get_hidden_event_count_text(self):
        self.assertTrue(
            self.status_bar_adapter.set_hidden_event_count_text.called)
        text = self.status_bar_adapter.set_hidden_event_count_text.call_args[
            0][0]
        return text

    def assert_event_has_period(self, event, start, end):
        self.assertEquals(py_period(start, end), event.time_period)

    def assert_balloon_drawn_for_event(self, event):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertEquals(event, view_properties.hovered_event)

    def assert_highlights_region(self, start_end):
        if start_end is not None:
            start_end = (human_time_to_py(start_end[0]),
                         human_time_to_py(start_end[1]))
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertEquals(start_end, view_properties.period_selection)

    def assert_displays_period(self, start, end):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertEquals(py_period(start, end),
                          view_properties.displayed_period)

    def assert_timeline_redrawn(self):
        self.assertTrue(self.view.redraw_surface.called)

    def assert_created_event_with_period(self, start, end):
        self.view.open_create_event_editor.assert_called_with(
            human_time_to_py(start), human_time_to_py(end))

    def assert_is_selected(self, event):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertTrue(view_properties.is_selected(event))

    def assert_is_not_selected(self, event):
        view_properties = self.get_view_properties_used_when_drawing()
        self.assertFalse(view_properties.is_selected(event))

    def assert_displays_status_text(self, text):
        self.assertEquals(text, self.get_status_text())

    def get_view_properties_used_when_drawing(self):
        self.assertTrue(self.view.redraw_surface.called)
        draw_fn = self.view.redraw_surface.call_args[0][0]
        draw_fn(Mock())
        return self.mock_drawer.draw_view_properties