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("leave_notify_event", self._on_leave_notify)
46 #self._imageEvents.connect("proximity_in_event", self._on_motion_notify)
47 #self._imageEvents.connect("proximity_out_event", self._on_leave_notify)
48 self._imageEvents.connect("button_press_event", self._on_button_press)
49 self._imageEvents.connect("button_release_event", self._on_button_release)
50 self._imageEvents.add(self._image)
52 self._isPortrait = True
54 self._canNavigate = True
55 self._clickPosition = self._NO_POSITION
56 self._potentialButtonState = self.BUTTON_STATE_PLAY
57 self._currentButtonState = self.BUTTON_STATE_PLAY
59 imagePath = self._store.STORE_LOOKUP[self._player.background]
60 self._backgroundImage = self._store.get_surface_from_store(imagePath)
61 imagePath = self._STATE_TO_IMAGE[self._currentButtonState]
62 self._buttonImage = self._store.get_surface_from_store(imagePath)
65 backWidth = self._backgroundImage.get_width()
66 backHeight = self._backgroundImage.get_height()
68 backHeight = self._backgroundImage.get_width()
69 backWidth = self._backgroundImage.get_height()
70 self._image.set_size_request(backWidth, backHeight)
74 return self._imageEvents
76 def set_orientation(self, orientation):
77 if orientation == gtk.ORIENTATION_VERTICAL:
78 self._isPortrait = True
79 elif orientation == gtk.ORIENTATION_HORIZONTAL:
80 self._isPortrait = False
82 raise NotImplementedError(orientation)
84 cairoContext = self._image.window.cairo_create()
85 if not self._isPortrait:
86 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
87 self._draw_presenter(cairoContext, self._currentButtonState)
89 @misc_utils.log_exception(_moduleLogger)
90 def _on_player_state_change(self, player, newState):
91 if newState == "play":
92 newState = self.BUTTON_STATE_PLAY
93 elif newState == "pause":
94 newState = self.BUTTON_STATE_PAUSE
95 elif newState == "stop":
96 newState = self.BUTTON_STATE_PAUSE
98 newState = self._currentButtonState
100 if newState != self._currentButtonState:
101 self._currentButtonState = newState
102 if self._clickPosition == self._NO_POSITION:
103 cairoContext = self._image.window.cairo_create()
104 if not self._isPortrait:
105 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
106 self._draw_state(cairoContext, self._currentButtonState)
108 @misc_utils.log_exception(_moduleLogger)
109 def _on_player_nav_change(self, player, newState):
110 canNavigate = self._player.can_navigate
111 newPotState = self._potentialButtonState
112 if self._canNavigate != canNavigate:
113 self._canNavigate = canNavigate
114 if self._potentialButtonState in (self.BUTTON_STATE_NEXT, self.BUTTON_STATE_BACK):
115 if self._currentButtonState == self.BUTTON_STATE_PLAY:
116 newPotState = self.BUTTON_STATE_PAUSE
118 newPotState = self.BUTTON_STATE_PLAY
120 if newPotState != self._potentialButtonState:
121 self._potentialButtonState = newPotState
122 if self._clickPosition == self._NO_POSITION:
123 cairoContext = self._image.window.cairo_create()
124 if not self._isPortrait:
125 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
126 self._draw_state(cairoContext, self._potentialButtonState)
128 @misc_utils.log_exception(_moduleLogger)
129 def _on_player_title_change(self, player, newState):
131 backWidth = self._backgroundImage.get_width()
132 backHeight = self._backgroundImage.get_height()
134 backHeight = self._backgroundImage.get_width()
135 backWidth = self._backgroundImage.get_height()
136 self._image.set_size_request(backWidth, backHeight)
138 imagePath = self._store.STORE_LOOKUP[self._player.background]
139 self._backgroundImage = self._store.get_surface_from_store(imagePath)
140 if self._clickPosition == self._NO_POSITION:
141 cairoContext = self._image.window.cairo_create()
142 if not self._isPortrait:
143 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
144 self._draw_presenter(cairoContext, self._currentButtonState)
146 cairoContext = self._image.window.cairo_create()
147 if not self._isPortrait:
148 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
149 self._draw_presenter(cairoContext, self._potentialButtonState)
151 @misc_utils.log_exception(_moduleLogger)
152 def _on_button_press(self, widget, event):
153 self._clickPosition = event.get_coords()
154 if self._currentButtonState == self.BUTTON_STATE_PLAY:
155 newState = self.BUTTON_STATE_PAUSE
157 newState = self.BUTTON_STATE_PLAY
158 self._potentialButtonState = newState
159 cairoContext = self._image.window.cairo_create()
160 if not self._isPortrait:
161 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
162 self._draw_state(cairoContext, self._potentialButtonState)
164 @misc_utils.log_exception(_moduleLogger)
165 def _on_button_release(self, widget, event):
167 mousePosition = event.get_coords()
168 newState = self._calculate_state(mousePosition)
169 if newState == self.BUTTON_STATE_PLAY:
171 elif newState == self.BUTTON_STATE_PAUSE:
173 elif newState == self.BUTTON_STATE_NEXT:
175 elif newState == self.BUTTON_STATE_BACK:
177 elif newState == self.BUTTON_STATE_UP:
178 raise NotImplementedError("Drag-down not implemented yet")
179 elif newState == self.BUTTON_STATE_CANCEL:
182 if self._player.state == "play":
183 newState = self.BUTTON_STATE_PLAY
185 newState = self.BUTTON_STATE_PAUSE
186 self._potentialButtonState = newState
187 cairoContext = self._image.window.cairo_create()
188 if not self._isPortrait:
189 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
190 self._draw_state(cairoContext, self._potentialButtonState)
191 self._clickPosition = self._NO_POSITION
193 @misc_utils.log_exception(_moduleLogger)
194 def _on_motion_notify(self, widget, event):
195 if self._clickPosition == self._NO_POSITION:
198 mousePosition = event.get_coords()
199 newState = self._calculate_state(mousePosition)
200 if newState != self._potentialButtonState:
201 self._potentialButtonState = newState
202 cairoContext = self._image.window.cairo_create()
203 if not self._isPortrait:
204 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
205 self._draw_state(cairoContext, self._potentialButtonState)
207 def _calculate_state(self, newCoord):
208 assert self._clickPosition != self._NO_POSITION
212 newCoord[0] - self._clickPosition[0],
213 - (newCoord[1] - self._clickPosition[1])
217 newCoord[1] - self._clickPosition[1],
218 - (newCoord[0] - self._clickPosition[0])
220 absDelta = (abs(delta[0]), abs(delta[1]))
221 if max(*absDelta) < self.MINIMUM_MOVEMENT or not self._canNavigate:
222 if self._currentButtonState == self.BUTTON_STATE_PLAY:
223 return self.BUTTON_STATE_PAUSE
225 return self.BUTTON_STATE_PLAY
227 if absDelta[0] < absDelta[1]:
229 return self.BUTTON_STATE_CANCEL
231 return self.BUTTON_STATE_UP
234 return self.BUTTON_STATE_BACK
236 return self.BUTTON_STATE_NEXT
238 @misc_utils.log_exception(_moduleLogger)
239 def _on_expose(self, widget, event):
240 self._potentialButtonState = self._player.state
241 cairoContext = self._image.window.cairo_create()
242 if not self._isPortrait:
243 cairoContext.transform(cairo.Matrix(0, 1, 1, 0, 0, 0))
244 self._draw_presenter(cairoContext, self._player.state)
246 def _draw_presenter(self, cairoContext, state):
247 assert state in (self._currentButtonState, self._potentialButtonState)
250 rect = self._image.get_allocation()
251 cairoContext.rectangle(
257 cairoContext.set_source_rgb(0, 0, 0)
262 cairoContext.set_source_surface(
263 self._backgroundImage,
270 if self._player.title:
271 _moduleLogger.info("Displaying text")
272 backWidth = self._backgroundImage.get_width()
273 backHeight = self._backgroundImage.get_height()
275 pangoContext = self._image.create_pango_context()
276 textLayout = pango.Layout(pangoContext)
277 textLayout.set_markup(self._player.title)
279 textWidth, textHeight = textLayout.get_pixel_size()
280 textX = backWidth / 2 - textWidth / 2
281 textY = backHeight - textHeight - self._buttonImage.get_height()
283 cairoContext.move_to(textX, textY)
284 cairoContext.set_source_rgb(0, 0, 0)
285 cairoContext.show_layout(textLayout)
287 self._draw_state(cairoContext, state)
289 def _draw_state(self, cairoContext, state):
290 assert state in (self._currentButtonState, self._potentialButtonState)
291 if state == self.BUTTON_STATE_CANCEL:
292 state = self._currentButtonState
294 backWidth = self._backgroundImage.get_width()
295 backHeight = self._backgroundImage.get_height()
297 imagePath = self._STATE_TO_IMAGE[state]
298 self._buttonImage = self._store.get_surface_from_store(imagePath)
299 cairoContext.set_source_surface(
301 backWidth / 2 - self._buttonImage.get_width() / 2,
302 backHeight - self._buttonImage.get_height() + 5,