Removing dead code
[watersofshiloah] / src / presenter.py
1 import logging
2
3 import gobject
4 import pango
5 import gtk
6
7 import util.misc as misc_utils
8
9
10 _moduleLogger = logging.getLogger(__name__)
11
12
13 class NavigationBox(gobject.GObject):
14
15         __gsignals__ = {
16                 'action' : (
17                         gobject.SIGNAL_RUN_LAST,
18                         gobject.TYPE_NONE,
19                         (gobject.TYPE_STRING, ),
20                 ),
21                 'navigating' : (
22                         gobject.SIGNAL_RUN_LAST,
23                         gobject.TYPE_NONE,
24                         (gobject.TYPE_STRING, ),
25                 ),
26         }
27
28         MINIMUM_MOVEMENT = 32
29
30         _NO_POSITION = -1, -1
31
32         def __init__(self):
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)
38
39                 self._isPortrait = True
40                 self._clickPosition = self._NO_POSITION
41
42         @property
43         def toplevel(self):
44                 return self._eventBox
45
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
51                 else:
52                         raise NotImplementedError(orientation)
53
54         def is_active(self):
55                 return self._clickPosition != self._NO_POSITION
56
57         def get_state(self, newCoord):
58                 if self._clickPosition == self._NO_POSITION:
59                         return ""
60
61                 delta = (
62                         newCoord[0] - self._clickPosition[0],
63                         - (newCoord[1] - self._clickPosition[1])
64                 )
65                 absDelta = (abs(delta[0]), abs(delta[1]))
66                 if max(*absDelta) < self.MINIMUM_MOVEMENT:
67                         return "clicking"
68
69                 if absDelta[0] < absDelta[1]:
70                         if 0 < delta[1]:
71                                 return "up"
72                         else:
73                                 return "down"
74                 else:
75                         if 0 < delta[0]:
76                                 return "right"
77                         else:
78                                 return "left"
79
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()
85
86                 self.emit("navigating", "clicking")
87
88         @misc_utils.log_exception(_moduleLogger)
89         def _on_button_release(self, widget, event):
90                 assert self._clickPosition != self._NO_POSITION
91                 try:
92                         mousePosition = event.get_coords()
93                         state = self.get_state(mousePosition)
94                         assert state
95                 finally:
96                         self._clickPosition = self._NO_POSITION
97                 self.emit("action", state)
98
99         @misc_utils.log_exception(_moduleLogger)
100         def _on_motion_notify(self, widget, event):
101                 if self._clickPosition == self._NO_POSITION:
102                         return
103
104                 mousePosition = event.get_coords()
105                 newState = self.get_state(mousePosition)
106                 self.emit("navigating", newState)
107
108
109 gobject.type_register(NavigationBox)
110
111
112 class StreamPresenter(object):
113
114         def __init__(self, store):
115                 self._store = store
116
117                 self._image = gtk.DrawingArea()
118                 self._image.connect("expose_event", self._on_expose)
119
120                 self._isPortrait = True
121
122                 self._backgroundImage = None
123                 self._title = ""
124                 self._subtitle = ""
125                 self._buttonImage = None
126                 self._imageName = ""
127                 self._dims = 0, 0
128
129         @property
130         def toplevel(self):
131                 return self._image
132
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
138                 else:
139                         raise NotImplementedError(orientation)
140
141                 self._image.queue_draw()
142
143         def set_state(self, stateImage):
144                 if stateImage == self._imageName:
145                         return
146                 self._imageName = stateImage
147                 self._buttonImage = self._store.get_surface_from_store(stateImage)
148
149                 self._image.queue_draw()
150
151         def set_context(self, backgroundImage, title, subtitle):
152                 self._backgroundImage = self._store.get_surface_from_store(backgroundImage)
153                 self._title = title
154                 self._subtitle = subtitle
155
156                 backWidth = self._backgroundImage.get_width()
157                 backHeight = self._backgroundImage.get_height()
158                 self._image.set_size_request(backWidth, backHeight)
159
160                 self._image.queue_draw()
161
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)
166
167         def _draw_presenter(self, cairoContext):
168                 rect = self._image.get_allocation()
169                 self._dims = rect.width, rect.height
170
171                 # Blank things
172                 cairoContext.rectangle(
173                         0,
174                         0,
175                         rect.width,
176                         rect.height,
177                 )
178                 cairoContext.set_source_rgb(0, 0, 0)
179                 cairoContext.fill()
180
181                 # Draw Background
182                 if self._backgroundImage is not None:
183                         cairoContext.set_source_surface(
184                                 self._backgroundImage,
185                                 0,
186                                 0,
187                         )
188                         cairoContext.paint()
189
190                 pangoContext = self._image.create_pango_context()
191
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
197
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
203
204                 xPadding = min((self._dims[0] - textWidth) / 2 - 5, 5)
205                 yPadding = 5
206                 startContent = xPadding, textY - yPadding
207                 endContent = self._dims[0] - xPadding,  self._dims[1] - yPadding
208
209                 # Control background
210                 cairoContext.rectangle(
211                         startContent[0],
212                         startContent[1],
213                         endContent[0] - startContent[0],
214                         endContent[1] - startContent[1],
215                 )
216                 cairoContext.set_source_rgba(0.9, 0.9, 0.9, 0.75)
217                 cairoContext.fill()
218
219                 # title
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)
224
225                         cairoContext.move_to(textX, textY)
226                         cairoContext.set_source_rgb(0, 0, 0)
227                         cairoContext.show_layout(subtitleLayout)
228
229                 self._draw_state(cairoContext)
230
231         def _draw_state(self, cairoContext):
232                 if self._backgroundImage is None or self._buttonImage is None:
233                         return
234                 cairoContext.set_source_surface(
235                         self._buttonImage,
236                         self._dims[0] / 2 - self._buttonImage.get_width() / 2,
237                         self._dims[1] - self._buttonImage.get_height() + 5,
238                 )
239                 cairoContext.paint()
240
241
242 class StreamMiniPresenter(object):
243
244         def __init__(self, store):
245                 self._store = store
246
247                 self._button = gtk.Image()
248
249         @property
250         def toplevel(self):
251                 return self._button
252
253         def set_orientation(self, orientation):
254                 pass
255
256         def set_state(self, stateImage):
257                 self._store.set_image_from_store(self._button, stateImage)
258
259         def set_context(self, backgroundImage, title, subtitle):
260                 pass