7 import util.misc as misc_utils
10 _moduleLogger = logging.getLogger(__name__)
13 class StreamPresenter(object):
17 BUTTON_STATE_PLAY = "play"
18 BUTTON_STATE_PAUSE = "pause"
19 BUTTON_STATE_NEXT = "next"
20 BUTTON_STATE_BACK = "back"
21 BUTTON_STATE_UP = "up"
22 BUTTON_STATE_CANCEL = "cancel"
27 BUTTON_STATE_PLAY: "play.png",
28 BUTTON_STATE_PAUSE: "pause.png",
29 BUTTON_STATE_NEXT: "next.png",
30 BUTTON_STATE_BACK: "prev.png",
33 def __init__(self, player, store):
37 self._player.connect("state-change", self._on_player_state_change)
38 self._player.connect("navigate-change", self._on_player_nav_change)
39 self._player.connect("title-change", self._on_player_title_change)
41 self._image = gtk.DrawingArea()
42 self._image.connect("expose_event", self._on_expose)
43 self._imageEvents = gtk.EventBox()
44 self._imageEvents.connect("motion_notify_event", self._on_motion_notify)
45 self._imageEvents.connect("button_press_event", self._on_button_press)
46 self._imageEvents.connect("button_release_event", self._on_button_release)
47 self._imageEvents.add(self._image)
49 self._isPortrait = True
51 self._canNavigate = True
52 self._clickPosition = self._NO_POSITION
53 self._potentialButtonState = self.BUTTON_STATE_PLAY
54 self._currentButtonState = self.BUTTON_STATE_PLAY
56 imagePath = self._store.STORE_LOOKUP[self._player.background]
57 self._backgroundImage = self._store.get_surface_from_store(imagePath)
58 imagePath = self._STATE_TO_IMAGE[self._currentButtonState]
59 self._buttonImage = self._store.get_surface_from_store(imagePath)
62 backWidth = self._backgroundImage.get_width()
63 backHeight = self._backgroundImage.get_height()
65 backHeight = self._backgroundImage.get_width()
66 backWidth = self._backgroundImage.get_height()
67 self._image.set_size_request(backWidth, backHeight)
71 return self._imageEvents
73 def set_orientation(self, orientation):
74 if orientation == gtk.ORIENTATION_VERTICAL:
75 self._isPortrait = True
76 elif orientation == gtk.ORIENTATION_HORIZONTAL:
77 self._isPortrait = False
79 raise NotImplementedError(orientation)
81 cairoContext = self._image.window.cairo_create()
82 if not self._isPortrait:
83 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
84 self._draw_presenter(cairoContext, self._currentButtonState)
86 @misc_utils.log_exception(_moduleLogger)
87 def _on_player_state_change(self, player, newState):
88 if newState == "play":
89 newState = self.BUTTON_STATE_PLAY
90 elif newState == "pause":
91 newState = self.BUTTON_STATE_PAUSE
92 elif newState == "stop":
93 newState = self.BUTTON_STATE_PAUSE
95 newState = self._currentButtonState
97 if newState != self._currentButtonState:
98 self._currentButtonState = newState
99 if self._clickPosition == self._NO_POSITION:
100 cairoContext = self._image.window.cairo_create()
101 if not self._isPortrait:
102 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
103 self._draw_state(cairoContext, self._currentButtonState)
105 @misc_utils.log_exception(_moduleLogger)
106 def _on_player_nav_change(self, player, newState):
107 canNavigate = self._player.can_navigate
108 newPotState = self._potentialButtonState
109 if self._canNavigate != canNavigate:
110 self._canNavigate = canNavigate
111 if self._potentialButtonState in (self.BUTTON_STATE_NEXT, self.BUTTON_STATE_BACK):
112 if self._currentButtonState == self.BUTTON_STATE_PLAY:
113 newPotState = self.BUTTON_STATE_PAUSE
115 newPotState = self.BUTTON_STATE_PLAY
117 if newPotState != self._potentialButtonState:
118 self._potentialButtonState = newPotState
119 if self._clickPosition == self._NO_POSITION:
120 cairoContext = self._image.window.cairo_create()
121 if not self._isPortrait:
122 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
123 self._draw_state(cairoContext, self._potentialButtonState)
125 @misc_utils.log_exception(_moduleLogger)
126 def _on_player_title_change(self, player, newState):
128 backWidth = self._backgroundImage.get_width()
129 backHeight = self._backgroundImage.get_height()
131 backHeight = self._backgroundImage.get_width()
132 backWidth = self._backgroundImage.get_height()
133 self._image.set_size_request(backWidth, backHeight)
135 imagePath = self._store.STORE_LOOKUP[self._player.background]
136 self._backgroundImage = self._store.get_surface_from_store(imagePath)
137 if self._clickPosition == self._NO_POSITION:
138 cairoContext = self._image.window.cairo_create()
139 if not self._isPortrait:
140 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
141 self._draw_presenter(cairoContext, self._currentButtonState)
143 cairoContext = self._image.window.cairo_create()
144 if not self._isPortrait:
145 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
146 self._draw_presenter(cairoContext, self._potentialButtonState)
148 @misc_utils.log_exception(_moduleLogger)
149 def _on_button_press(self, widget, event):
150 self._clickPosition = event.get_coords()
151 if self._currentButtonState == self.BUTTON_STATE_PLAY:
152 newState = self.BUTTON_STATE_PAUSE
154 newState = self.BUTTON_STATE_PLAY
155 self._potentialButtonState = newState
156 cairoContext = self._image.window.cairo_create()
157 if not self._isPortrait:
158 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
159 self._draw_state(cairoContext, self._potentialButtonState)
161 @misc_utils.log_exception(_moduleLogger)
162 def _on_button_release(self, widget, event):
164 mousePosition = event.get_coords()
165 newState = self._calculate_state(mousePosition)
166 if newState == self.BUTTON_STATE_PLAY:
168 elif newState == self.BUTTON_STATE_PAUSE:
170 elif newState == self.BUTTON_STATE_NEXT:
172 elif newState == self.BUTTON_STATE_BACK:
174 elif newState == self.BUTTON_STATE_UP:
175 raise NotImplementedError("Drag-down not implemented yet")
176 elif newState == self.BUTTON_STATE_CANCEL:
179 if self._player.state == "play":
180 newState = self.BUTTON_STATE_PLAY
182 newState = self.BUTTON_STATE_PAUSE
183 self._potentialButtonState = newState
184 cairoContext = self._image.window.cairo_create()
185 if not self._isPortrait:
186 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
187 self._draw_state(cairoContext, self._potentialButtonState)
188 self._clickPosition = self._NO_POSITION
190 @misc_utils.log_exception(_moduleLogger)
191 def _on_motion_notify(self, widget, event):
192 if self._clickPosition == self._NO_POSITION:
195 mousePosition = event.get_coords()
196 newState = self._calculate_state(mousePosition)
197 if newState != self._potentialButtonState:
198 self._potentialButtonState = newState
199 cairoContext = self._image.window.cairo_create()
200 if not self._isPortrait:
201 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
202 self._draw_state(cairoContext, self._potentialButtonState)
204 def _calculate_state(self, newCoord):
205 assert self._clickPosition != self._NO_POSITION
209 newCoord[0] - self._clickPosition[0],
210 - (newCoord[1] - self._clickPosition[1])
214 newCoord[1] - self._clickPosition[1],
215 - (newCoord[0] - self._clickPosition[0])
217 absDelta = (abs(delta[0]), abs(delta[1]))
218 if max(*absDelta) < self.MINIMUM_MOVEMENT or not self._canNavigate:
219 if self._currentButtonState == self.BUTTON_STATE_PLAY:
220 return self.BUTTON_STATE_PAUSE
222 return self.BUTTON_STATE_PLAY
224 if absDelta[0] < absDelta[1]:
226 return self.BUTTON_STATE_CANCEL
228 return self.BUTTON_STATE_UP
231 return self.BUTTON_STATE_BACK
233 return self.BUTTON_STATE_NEXT
235 @misc_utils.log_exception(_moduleLogger)
236 def _on_expose(self, widget, event):
237 self._potentialButtonState = self._player.state
238 cairoContext = self._image.window.cairo_create()
239 if not self._isPortrait:
240 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
241 self._draw_presenter(cairoContext, self._player.state)
243 def _draw_presenter(self, cairoContext, state):
244 assert state in (self._currentButtonState, self._potentialButtonState)
247 rect = self._image.get_allocation()
248 cairoContext.rectangle(
254 cairoContext.set_source_rgb(0, 0, 0)
259 cairoContext.set_source_surface(
260 self._backgroundImage,
267 if self._player.title:
268 _moduleLogger.info("Displaying text")
269 backWidth = self._backgroundImage.get_width()
270 backHeight = self._backgroundImage.get_height()
272 pangoContext = self._image.create_pango_context()
273 textLayout = pango.Layout(pangoContext)
274 textLayout.set_markup(self._player.title)
276 textWidth, textHeight = textLayout.get_pixel_size()
277 textX = backWidth / 2 - textWidth / 2
278 textY = backHeight - textHeight - self._buttonImage.get_height()
280 cairoContext.move_to(textX, textY)
281 cairoContext.set_source_rgb(0, 0, 0)
282 cairoContext.show_layout(textLayout)
284 self._draw_state(cairoContext, state)
286 def _draw_state(self, cairoContext, state):
287 assert state in (self._currentButtonState, self._potentialButtonState)
288 if state == self.BUTTON_STATE_CANCEL:
289 state = self._currentButtonState
291 backWidth = self._backgroundImage.get_width()
292 backHeight = self._backgroundImage.get_height()
294 imagePath = self._STATE_TO_IMAGE[state]
295 self._buttonImage = self._store.get_surface_from_store(imagePath)
296 cairoContext.set_source_surface(
298 backWidth / 2 - self._buttonImage.get_width() / 2,
299 backHeight - self._buttonImage.get_height() + 5,