Package Gnumed :: Package timelinelib :: Package canvas :: Module timelinecanvascontroller
[frames] | no frames]

Source Code for Module Gnumed.timelinelib.canvas.timelinecanvascontroller

  1  # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018  Rickard Lindberg, Roger Lindberg 
  2  # 
  3  # This file is part of Timeline. 
  4  # 
  5  # Timeline is free software: you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation, either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Timeline is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with Timeline.  If not, see <http://www.gnu.org/licenses/>. 
 17   
 18   
 19  import wx 
 20   
 21  from timelinelib.canvas.appearance import Appearance 
 22  from timelinelib.canvas.backgrounddrawers.defaultbgdrawer import DefaultBackgroundDrawer 
 23  from timelinelib.canvas.drawing import get_drawer 
 24  from timelinelib.canvas.drawing.viewproperties import ViewProperties 
 25  from timelinelib.canvas.eventboxdrawers.defaulteventboxdrawer import DefaultEventBoxDrawer 
 26  from timelinelib.canvas.events import create_timeline_redrawn_event 
 27  from timelinelib import DEBUG_ENABLED 
 28  from timelinelib.monitoring import Monitoring 
 29  from timelinelib.wxgui.components.font import Font 
30 31 32 -class TimelineCanvasController(object):
33
34 - def __init__(self, view, drawer=None):
35 """ 36 The purpose of the drawer argument is make testing easier. A test can 37 mock a drawer and use the mock by sending it in the drawer argument. 38 Normally the drawer is collected with the get_drawer() method. 39 """ 40 self.appearance = None 41 self.monitoring = Monitoring() 42 self.view = view 43 self._set_drawing_algorithm(drawer) 44 self.timeline = None 45 self.set_appearance(Appearance()) 46 self.set_event_box_drawer(DefaultEventBoxDrawer()) 47 self.set_background_drawer(self.get_saved_background_drawer()) 48 self._fast_draw = False 49 self._set_initial_values_to_member_variables() 50 self._set_colors_and_styles()
51 52 @property
53 - def scene(self):
54 return self.drawing_algorithm.scene
55
56 - def get_appearance(self):
57 return self.appearance
58
59 - def set_appearance(self, appearance):
60 if self.appearance is not None: 61 self.appearance.unlisten(self._redraw_timeline) 62 self.appearance = appearance 63 self.appearance.listen_for_any(self._redraw_timeline) 64 self.redraw_timeline()
65 68
69 - def set_event_box_drawer(self, event_box_drawer):
70 self.drawing_algorithm.set_event_box_drawer(event_box_drawer)
71
72 - def set_background_drawer(self, drawer):
73 self.drawing_algorithm.set_background_drawer(drawer)
74
75 - def get_timeline(self):
76 return self.timeline
77
78 - def get_view_properties(self):
79 return self.view_properties
80
81 - def set_timeline(self, timeline):
82 """Inform what timeline to draw.""" 83 self._unregister_timeline(self.timeline) 84 if timeline is None: 85 self._set_null_timeline() 86 else: 87 self._set_non_null_timeline(timeline)
88
89 - def use_fast_draw(self, value):
90 self._fast_draw = value
91
92 - def navigate(self, navigation_fn):
93 old_period = self.view_properties.displayed_period 94 new_period = navigation_fn(old_period) 95 MIN_ZOOM_DELTA, min_zoom_error_text = self.time_type.get_min_zoom_delta() 96 if new_period.delta() < MIN_ZOOM_DELTA: 97 raise ValueError(min_zoom_error_text) 98 min_time = self.time_type.get_min_time() 99 if min_time is not None and new_period.start_time < min_time: 100 raise ValueError(_("Can't scroll more to the left")) 101 max_time = self.time_type.get_max_time() 102 if max_time is not None and new_period.end_time > max_time: 103 raise ValueError(_("Can't scroll more to the right")) 104 self.view_properties.displayed_period = new_period 105 self.redraw_timeline()
106
107 - def _set_null_timeline(self):
108 self.timeline = None 109 self.time_type = None 110 self.view.Disable()
111
112 - def _set_non_null_timeline(self, timeline):
113 self.timeline = timeline 114 self.time_type = timeline.get_time_type() 115 self.timeline.register(self._timeline_changed) 116 self.view_properties.unlisten(self._redraw_timeline) 117 properties_loaded = self._load_view_properties() 118 if properties_loaded: 119 self.view_properties.listen_for_any(self._redraw_timeline) 120 self._redraw_timeline() 121 self.view.Enable() 122 self.view.SetFocus()
123
124 - def _load_view_properties(self):
125 properties_loaded = True 126 try: 127 self.view_properties.clear_db_specific() 128 self.timeline.load_view_properties(self.view_properties) 129 if self.view_properties.displayed_period is None: 130 default_tp = self.time_type.get_default_time_period() 131 self.view_properties.displayed_period = default_tp 132 self.view_properties.hscroll_amount = 0 133 except Exception: 134 properties_loaded = False 135 return properties_loaded
136
137 - def _unregister_timeline(self, timeline):
138 if timeline is not None: 139 timeline.unregister(self._timeline_changed)
140
141 - def get_time_period(self):
142 """Return currently displayed time period.""" 143 if self.timeline is None: 144 raise Exception(_("No timeline set")) 145 return self.view_properties.displayed_period
146
147 - def redraw_timeline(self):
148 self._redraw_timeline()
149
150 - def window_resized(self):
151 self._redraw_timeline()
152
154 selected_event_ids = self.view_properties.get_selected_event_ids() 155 nbr_of_selected_event_ids = len(selected_event_ids) 156 return nbr_of_selected_event_ids == 1
157
159 selected_event_ids = self.view_properties.get_selected_event_ids() 160 if len(selected_event_ids) > 0: 161 event_id = selected_event_ids[0] 162 return self.timeline.find_event_with_id(event_id) 163 return None
164
165 - def get_time(self, x):
166 return self.drawing_algorithm.get_time(x)
167
168 - def event_with_rect_at(self, x, y, alt_down=False):
169 return self.drawing_algorithm.event_with_rect_at(x, y, alt_down)
170
171 - def event_at(self, x, y, alt_down=False):
172 return self.drawing_algorithm.event_at(x, y, alt_down)
173
174 - def set_selected(self, event, is_selected):
175 self.view_properties.set_selected(event, is_selected)
176
177 - def clear_selected(self):
178 self.view_properties.clear_selected()
179
180 - def select_all_events(self):
181 self.view_properties.select_all_events()
182
183 - def is_selected(self, event):
184 return self.view_properties.is_selected(event)
185
186 - def set_hovered_event(self, event):
187 self.view_properties.change_hovered_event(event)
188
189 - def get_hovered_event(self):
190 return self.view_properties.hovered_event
191
192 - def set_selection_rect(self, cursor):
193 self.view_properties.set_selection_rect(cursor.rect) 194 self._fast_draw = True 195 self.redraw_timeline()
196
197 - def remove_selection_rect(self):
198 self.view_properties.set_selection_rect(None) 199 self._fast_draw = True 200 self.redraw_timeline()
201
202 - def get_hscroll_amount(self):
203 return self.view_properties.hscroll_amount
204
205 - def set_hscroll_amount(self, amount):
206 self.view_properties.hscroll_amount = amount
207
208 - def set_period_selection(self, period):
209 if period is None: 210 self.view_properties.period_selection = None 211 else: 212 self.view_properties.period_selection = (period.start_time, period.end_time) 213 self._redraw_timeline()
214
215 - def select_events_in_rect(self, rect):
216 self.view_properties.set_all_selected(self.get_events_in_rect(rect))
217
218 - def event_has_sticky_balloon(self, event):
219 return self.view_properties.event_has_sticky_balloon(event)
220
221 - def set_event_sticky_balloon(self, event, is_sticky):
222 self.view_properties.set_event_has_sticky_balloon(event, is_sticky) 223 self.redraw_timeline()
224
225 - def add_highlight(self, event, clear):
226 self.view_properties.add_highlight(event, clear)
227
228 - def tick_highlights(self):
229 self.view_properties.tick_highlights(limit=15)
230
231 - def has_higlights(self):
232 return self.view_properties.has_higlights()
233
234 - def filter_events(self, events):
235 return self.view_properties.filter_events(events)
236
237 - def event_is_period(self, event):
238 return self.drawing_algorithm.event_is_period(event.get_time_period())
239
240 - def snap(self, time):
241 return self.drawing_algorithm.snap(time)
242
243 - def get_selected_events(self):
244 return self.timeline.find_event_with_ids( 245 self.view_properties.get_selected_event_ids() 246 )
247
248 - def get_events_in_rect(self, rect):
249 return self.drawing_algorithm.get_events_in_rect(rect)
250
251 - def get_hidden_event_count(self):
252 return self.drawing_algorithm.get_hidden_event_count()
253
254 - def increment_font_size(self):
255 self.drawing_algorithm.increment_font_size() 256 self._redraw_timeline()
257
258 - def decrement_font_size(self):
259 self.drawing_algorithm.decrement_font_size() 260 self._redraw_timeline()
261
262 - def get_closest_overlapping_event(self, event, up):
263 return self.drawing_algorithm.get_closest_overlapping_event(event, up=up)
264
265 - def balloon_at(self, cursor):
266 return self.drawing_algorithm.balloon_at(*cursor.pos)
267
268 - def _timeline_changed(self, state_change):
269 self._redraw_timeline()
270
272 self.timeline = None 273 self.view_properties = ViewProperties() 274 self.dragscroll_timer_running = False
275
276 - def _set_colors_and_styles(self):
277 """Define the look and feel of the drawing area.""" 278 self.view.SetBackgroundColour(wx.WHITE) 279 self.view.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) 280 self.view.set_default_cursor() 281 self.view.Disable()
282
283 - def _redraw_timeline(self):
284 285 def display_monitor_result(dc): 286 (width, height) = self.view.GetSizeTuple() 287 redraw_time = self.monitoring.timer_elapsed_ms 288 self.monitoring.count_timeline_redraw() 289 dc.SetTextForeground((255, 0, 0)) 290 dc.SetFont(Font(12, weight=wx.FONTWEIGHT_BOLD)) 291 index, is_in_transaction, history = self.timeline.transactions_status() 292 dc.DrawText("Undo buffer size: %d" % len(history), width - 300, height - 100) 293 dc.DrawText("Undo buffer pos: %d" % index, width - 300, height - 80) 294 dc.DrawText("Redraw count: %d" % self.monitoring._timeline_redraw_count, width - 300, height - 60) 295 dc.DrawText("Last redraw time: %.3f ms" % redraw_time, width - 300, height - 40)
296 297 def fn_draw(dc): 298 self.monitoring.timer_start() 299 self.drawing_algorithm.draw(dc, self.timeline, self.view_properties, self.appearance, fast_draw=self._fast_draw) 300 self.monitoring.timer_end() 301 if DEBUG_ENABLED: 302 display_monitor_result(dc) 303 self._fast_draw = False
304 305 if self.timeline and self.view_properties.displayed_period: 306 self.view_properties.divider_position = (float(self.view.GetDividerPosition()) / 100.0) 307 self.view.redraw_surface(fn_draw) 308 self.view.PostEvent(create_timeline_redrawn_event()) 309
310 - def _set_drawing_algorithm(self, drawer):
311 """ 312 The drawer interface: 313 methods: 314 draw(d, t, p, a, f) 315 set_event_box_drawer(d) 316 set_background_drawer(d) 317 get_time(x) 318 event_with_rect_at(x, y, k) 319 event_at(x, y, k) 320 event_is_period(p) 321 snap(t) 322 get_events_in_rect(r) 323 get_hidden_event_count() 324 increment_font_size() 325 decrement_font_size() 326 get_closest_overlapping_event(...) 327 balloon_at(c) 328 properties: 329 scene 330 """ 331 if drawer is not None: 332 self.drawing_algorithm = drawer 333 else: 334 self.drawing_algorithm = get_drawer()
335