7 import util.misc as misc_utils
10 _moduleLogger = logging.getLogger(__name__)
13 class NavigationBox(gobject.GObject):
17 gobject.SIGNAL_RUN_LAST,
19 (gobject.TYPE_STRING, ),
22 gobject.SIGNAL_RUN_LAST,
24 (gobject.TYPE_STRING, ),
33 gobject.GObject.__init__(self)
34 self._eventBox = gtk.EventBox()
35 self._eventBox.connect("button_press_event", self._on_button_press)
36 self._eventBox.connect("button_release_event", self._on_button_release)
37 self._eventBox.connect("motion_notify_event", self._on_motion_notify)
39 self._isPortrait = True
40 self._clickPosition = self._NO_POSITION
46 def set_orientation(self, orientation):
47 if orientation == gtk.ORIENTATION_VERTICAL:
48 self._isPortrait = True
49 elif orientation == gtk.ORIENTATION_HORIZONTAL:
50 self._isPortrait = False
52 raise NotImplementedError(orientation)
55 return self._clickPosition != self._NO_POSITION
57 def get_state(self, newCoord):
58 if self._clickPosition == self._NO_POSITION:
62 newCoord[0] - self._clickPosition[0],
63 - (newCoord[1] - self._clickPosition[1])
65 absDelta = (abs(delta[0]), abs(delta[1]))
66 if max(*absDelta) < self.MINIMUM_MOVEMENT:
69 if absDelta[0] < absDelta[1]:
80 @misc_utils.log_exception(_moduleLogger)
81 def _on_button_press(self, widget, event):
82 if self._clickPosition != self._NO_POSITION:
83 _moduleLogger.debug("Ignoring double click")
84 self._clickPosition = event.get_coords()
86 self.emit("navigating", "clicking")
88 @misc_utils.log_exception(_moduleLogger)
89 def _on_button_release(self, widget, event):
90 assert self._clickPosition != self._NO_POSITION
92 mousePosition = event.get_coords()
93 state = self.get_state(mousePosition)
96 self._clickPosition = self._NO_POSITION
97 self.emit("action", state)
99 @misc_utils.log_exception(_moduleLogger)
100 def _on_motion_notify(self, widget, event):
101 if self._clickPosition == self._NO_POSITION:
104 mousePosition = event.get_coords()
105 newState = self.get_state(mousePosition)
106 self.emit("navigating", newState)
109 gobject.type_register(NavigationBox)
112 class StreamPresenter(object):
114 def __init__(self, store):
117 self._image = gtk.DrawingArea()
118 self._image.connect("expose_event", self._on_expose)
120 self._isPortrait = True
122 self._backgroundImage = None
125 self._buttonImage = None
133 def set_orientation(self, orientation):
134 if orientation == gtk.ORIENTATION_VERTICAL:
135 self._isPortrait = True
136 elif orientation == gtk.ORIENTATION_HORIZONTAL:
137 self._isPortrait = False
139 raise NotImplementedError(orientation)
141 self._image.queue_draw()
143 def set_state(self, stateImage):
144 if stateImage == self._imageName:
146 self._imageName = stateImage
147 self._buttonImage = self._store.get_surface_from_store(stateImage)
149 self._image.queue_draw()
151 def set_context(self, backgroundImage, title, subtitle):
152 self._backgroundImage = self._store.get_surface_from_store(backgroundImage)
154 self._subtitle = subtitle
156 backWidth = self._backgroundImage.get_width()
157 backHeight = self._backgroundImage.get_height()
158 self._image.set_size_request(backWidth, backHeight)
160 self._image.queue_draw()
162 @misc_utils.log_exception(_moduleLogger)
163 def _on_expose(self, widget, event):
164 cairoContext = self._image.window.cairo_create()
165 self._draw_presenter(cairoContext)
167 def _draw_presenter(self, cairoContext):
168 rect = self._image.get_allocation()
169 self._dims = rect.width, rect.height
172 cairoContext.rectangle(
178 cairoContext.set_source_rgb(0, 0, 0)
182 if self._backgroundImage is not None:
183 cairoContext.set_source_surface(
184 self._backgroundImage,
190 pangoContext = self._image.create_pango_context()
192 titleLayout = pango.Layout(pangoContext)
193 titleLayout.set_markup("<i>%s</i>" % self._subtitle)
194 textWidth, textHeight = titleLayout.get_pixel_size()
195 subtitleTextX = self._dims[0] / 2 - textWidth / 2
196 subtitleTextY = self._dims[1] - textHeight - self._buttonImage.get_height() + 10
198 subtitleLayout = pango.Layout(pangoContext)
199 subtitleLayout.set_markup("<b>%s</b>" % self._title)
200 textWidth, textHeight = subtitleLayout.get_pixel_size()
201 textX = self._dims[0] / 2 - textWidth / 2
202 textY = subtitleTextY - textHeight
204 xPadding = min((self._dims[0] - textWidth) / 2 - 5, 5)
206 startContent = xPadding, textY - yPadding
207 endContent = self._dims[0] - xPadding, self._dims[1] - yPadding
210 cairoContext.rectangle(
213 endContent[0] - startContent[0],
214 endContent[1] - startContent[1],
216 cairoContext.set_source_rgba(0.9, 0.9, 0.9, 0.75)
220 if self._title or self._subtitle:
221 cairoContext.move_to(subtitleTextX, subtitleTextY)
222 cairoContext.set_source_rgb(0, 0, 0)
223 cairoContext.show_layout(titleLayout)
225 cairoContext.move_to(textX, textY)
226 cairoContext.set_source_rgb(0, 0, 0)
227 cairoContext.show_layout(subtitleLayout)
229 self._draw_state(cairoContext)
231 def _draw_state(self, cairoContext):
232 if self._backgroundImage is None or self._buttonImage is None:
234 cairoContext.set_source_surface(
236 self._dims[0] / 2 - self._buttonImage.get_width() / 2,
237 self._dims[1] - self._buttonImage.get_height() + 5,
242 class StreamMiniPresenter(object):
244 def __init__(self, store):
247 self._button = gtk.Image()
253 def set_orientation(self, orientation):
256 def set_state(self, stateImage):
257 self._store.set_image_from_store(self._button, stateImage)
259 def set_context(self, backgroundImage, title, subtitle):