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