1# -*- coding: utf-8 -*- 2# Pitivi video editor 3# Copyright (c) 2016, Lubosz Sarnecki <lubosz.sarnecki@collabora.co.uk> 4# 5# This program is free software; you can redistribute it and/or 6# modify it under the terms of the GNU Lesser General Public 7# License as published by the Free Software Foundation; either 8# version 2.1 of the License, or (at your option) any later version. 9# 10# This program 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 GNU 13# Lesser General Public License for more details. 14# 15# You should have received a copy of the GNU Lesser General Public 16# License along with this program; if not, write to the 17# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 18# Boston, MA 02110-1301, USA. 19import cairo 20import numpy 21 22from pitivi.viewer.overlay import Overlay 23 24 25class TitleOverlay(Overlay): 26 """Viewer overlays for GES.TitleSource.""" 27 28 def __init__(self, stack, source): 29 Overlay.__init__(self, stack, source) 30 self.__corners = [] 31 self.__position = numpy.array([0, 0]) 32 self.__size = None 33 self.__click_source_position = None 34 self.__click_window_position = None 35 self.update_from_source() 36 37 stack.app.project_manager.current_project.pipeline.connect("async-done", self.on_async_done) 38 39 def on_async_done(self, unused_pipeline): 40 # Only update on_async when we are not dragging 41 if isinstance(self.stack.click_position, numpy.ndarray): 42 return 43 self.update_from_source() 44 45 def __draw_rectangle(self, cr): 46 for corner in self.__corners: 47 cr.line_to(*corner.tolist()) 48 cr.line_to(*self.__position.tolist()) 49 50 def __get_source_position(self): 51 res_x, x = self._source.get_child_property("x-absolute") 52 res_y, y = self._source.get_child_property("y-absolute") 53 assert res_x and res_y 54 return numpy.array([x, y]) 55 56 def __get_text_position(self): 57 res_x, x = self._source.get_child_property("text-x") 58 res_y, y = self._source.get_child_property("text-y") 59 assert res_x and res_y 60 return numpy.array([x, y]) 61 62 def __get_text_size(self): 63 res_w, w = self._source.get_child_property("text-width") 64 res_h, h = self._source.get_child_property("text-height") 65 assert res_w and res_h 66 return numpy.array([w, h]) 67 68 def __set_source_position(self, position): 69 self._source.set_child_property("x-absolute", float(position[0])) 70 self._source.set_child_property("y-absolute", float(position[1])) 71 72 def __update_corners(self): 73 self.__corners = [ 74 self.__position, 75 self.__position + numpy.array([self.__size[0], 0]), 76 self.__position + self.__size, 77 self.__position + numpy.array([0, self.__size[1]]) 78 ] 79 80 def __update_from_motion(self, title_position): 81 self.__position = title_position 82 self.__update_corners() 83 84 def update_from_source(self): 85 position = self.__get_text_position() 86 size = self.__get_text_size() 87 88 self.__position = position * self.stack.window_size / self.project_size 89 self.__size = size * self.stack.window_size / self.project_size 90 self.__update_corners() 91 self.queue_draw() 92 93 def on_hover(self, cursor_position): 94 if (self.__position < cursor_position).all() and (cursor_position < self.__position + self.__size).all(): 95 if self._is_selected(): 96 self.stack.set_cursor("grab") 97 self._hover() 98 else: 99 self.unhover() 100 self.queue_draw() 101 return self._is_hovered() 102 103 def on_button_press(self): 104 self.__click_source_position = self.__get_source_position() 105 self.__click_window_position = self.__position 106 if self._is_hovered(): 107 self._select() 108 self.stack.set_cursor("grabbing") 109 self.stack.selected_overlay = self 110 elif self._is_selected(): 111 self._deselect() 112 113 def on_button_release(self, cursor_position): 114 self.__click_source_position = None 115 self.on_hover(cursor_position) 116 if self._is_hovered(): 117 self.stack.set_cursor("grab") 118 self.queue_draw() 119 120 def on_motion_notify(self, cursor_position): 121 if not isinstance(self.stack.click_position, numpy.ndarray): 122 return 123 124 self.__update_from_motion(self.__click_window_position + self.stack.get_drag_distance(cursor_position)) 125 self.queue_draw() 126 127 title_position = self.__position / (self.stack.window_size * (1 - self.__get_text_size() / self.project_size)) 128 129 self.__set_source_position(title_position) 130 self._commit() 131 132 def do_draw(self, cr): 133 if not self._is_selected() and not self._is_hovered(): 134 return 135 136 cr.save() 137 # clear background 138 cr.set_operator(cairo.OPERATOR_OVER) 139 cr.set_source_rgba(0.0, 0.0, 0.0, 0.0) 140 cr.paint() 141 142 if self._is_hovered(): 143 brightness = 0.65 144 else: 145 brightness = 0.3 146 147 # clip away outer mask 148 self.__draw_rectangle(cr) 149 cr.clip() 150 cr.set_source_rgba(brightness, brightness, brightness, 0.6) 151 self.__draw_rectangle(cr) 152 153 cr.set_line_width(16) 154 cr.stroke() 155 cr.restore() 156