1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the OpenSSL
22
# You must obey the GNU General Public License in all respects for all of
23
# the code used other than OpenSSL. If you modify file(s) with this
24
# exception, you may extend this exception to your version of the file(s),
25
# but you are not obligated to do so. If you do not wish to do so, delete
26
# this exception statement from your version. If you delete this exception
27
# statement from all source files in the program, then also delete it here.
29
"""miro.plat.frontends.widgets.customcontrol -- CustomControl handlers. """
32
from Foundation import *
33
from objc import YES, NO, nil
35
from miro.plat.frontends.widgets import wrappermap
36
from miro.plat.frontends.widgets.base import Widget, SimpleBin, FlippedView
37
from miro.plat.frontends.widgets import drawing
38
from miro.plat.frontends.widgets.layoutmanager import LayoutManager
40
class DrawableButtonCell(NSButtonCell):
41
def startTrackingAt_inView_(self, point, view):
42
view.setState_(NSOnState)
45
def continueTracking_at_inView_(self, lastPoint, at, view):
46
view.setState_(NSOnState)
49
def stopTracking_at_inView_mouseIsUp_(self, lastPoint, at, view, mouseIsUp):
51
view.setState_(NSOffState)
53
class DrawableButton(NSButton):
55
self = super(DrawableButton, self).init()
56
self.layout_manager = LayoutManager()
57
self.tracking_rect = None
58
self.mouse_inside = False
61
def remove_tracking_rect(self):
62
if self.tracking_rect is not None:
63
self.removeTrackingRect_(self.tracking_rect)
64
self.tracking_rect = None
66
def viewDidMoveToWindow(self):
67
self.reset_tracking_rect()
69
def setFrame_(self, rect):
70
NSButton.setFrame_(self, rect)
71
self.reset_tracking_rect()
73
def setBounds_(self, rect):
74
NSButton.setBounds_(self, rect)
75
self.reset_tracking_rect()
77
def reset_tracking_rect(self):
78
self.remove_tracking_rect()
79
if self.window() is not None:
80
self.tracking_rect = self.addTrackingRect_owner_userData_assumeInside_(
81
self.bounds(), self, 0, NO)
83
def mouseEntered_(self, event):
84
window = self.window()
85
if window is not nil and window.isMainWindow():
86
self.mouse_inside = True
87
self.setNeedsDisplay_(YES)
89
def mouseExited_(self, event):
90
window = self.window()
91
if window is not nil and window.isMainWindow():
92
self.mouse_inside = False
93
self.setNeedsDisplay_(YES)
96
return wrappermap.wrapper(self).is_opaque()
98
def drawRect_(self, rect):
99
context = drawing.DrawingContext(self, self.bounds(), rect)
100
context.style = drawing.DrawingStyle()
101
wrapper = wrappermap.wrapper(self)
102
if self.state() == NSOnState:
103
wrapper.state = 'pressed'
104
elif self.mouse_inside:
105
wrapper.state = 'hover'
107
wrapper.state = 'normal'
109
wrappermap.wrapper(self).draw(context, self.layout_manager)
110
self.layout_manager.reset()
112
def sendAction_to_(self, action, to):
113
# We override the Cocoa machinery here and just send it to our wrapper
115
wrappermap.wrapper(self).emit('clicked')
117
DrawableButton.setCellClass_(DrawableButtonCell)
119
class ContinousButtonCell(DrawableButtonCell):
120
def stopTracking_at_inView_mouseIsUp_(self, lastPoint, at, view, mouseIsUp):
121
view.onStopTracking(at)
122
NSButtonCell.stopTracking_at_inView_mouseIsUp_(self, lastPoint, at,
125
class ContinuousDrawableButton(DrawableButton):
127
self = super(ContinuousDrawableButton, self).init()
128
self.setContinuous_(YES)
131
def mouseDown_(self, event):
132
self.releaseInbounds = self.stopTracking = self.firedOnce = False
133
self.cell().trackMouse_inRect_ofView_untilMouseUp_(event,
134
self.bounds(), self, YES)
135
if self.releaseInbounds:
137
wrappermap.wrapper(self).emit('released')
139
wrappermap.wrapper(self).emit('clicked')
141
def sendAction_to_(self, action, to):
142
if self.stopTracking:
144
self.firedOnce = True
145
wrappermap.wrapper(self).emit('held-down')
148
def onStopTracking(self, mouseLocation):
149
self.releaseInbounds = NSPointInRect(mouseLocation, self.bounds())
150
self.stopTracking = True
151
ContinuousDrawableButton.setCellClass_(ContinousButtonCell)
153
class CustomSliderCell(NSSliderCell):
154
def calc_slider_amount(self, view, pos, size):
155
slider_size = wrappermap.wrapper(view).slider_size()
156
pos -= slider_size / 2
158
return max(0, min(1, float(pos) / size))
160
def startTrackingAt_inView_(self, at, view):
161
wrappermap.wrapper(view).emit('pressed')
162
return self.continueTracking_at_inView_(at, at, view)
164
def continueTracking_at_inView_(self, lastPoint, at, view):
165
if view.isVertical():
167
size = view.bounds().size.height
170
size = view.bounds().size.width
171
slider_amount = self.calc_slider_amount(view, pos, size)
172
value = (self.maxValue() - self.minValue()) * slider_amount
173
self.setFloatValue_(value)
174
wrappermap.wrapper(view).emit('moved', value)
175
if self.isContinuous():
176
wrappermap.wrapper(view).emit('changed', value)
179
def stopTracking_at_inView_mouseIsUp_(self, lastPoint, at, view, mouseUp):
180
wrappermap.wrapper(view).emit('released')
182
class CustomSliderView(NSSlider):
184
self = super(CustomSliderView, self).init()
185
self.layout_manager = LayoutManager()
189
return wrappermap.wrapper(self).is_opaque()
191
def knobThickness(self):
192
return wrappermap.wrapper(self).slider_size()
194
def isVertical(self):
195
return not wrappermap.wrapper(self).is_horizontal()
197
def drawRect_(self, rect):
198
context = drawing.DrawingContext(self, self.bounds(), rect)
199
context.style = drawing.DrawingStyle()
200
wrappermap.wrapper(self).draw(context, self.layout_manager)
201
self.layout_manager.reset()
203
def sendAction_to_(self, action, to):
204
# We override the Cocoa machinery here and just send it to our wrapper
206
wrappermap.wrapper(self).emit('changed', self.floatValue())
208
CustomSliderView.setCellClass_(CustomSliderCell)
210
class CustomButton(drawing.DrawingMixin, Widget):
211
"""See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
213
Widget.__init__(self)
214
self.create_signal('clicked')
215
self.view = DrawableButton.alloc().init()
216
self.view.setRefusesFirstResponder_(NO)
220
self.view.setEnabled_(True)
224
self.view.setEnabled_(False)
226
def remove_viewport(self):
227
self.view.remove_tracking_rect()
228
Widget.remove_viewport(self)
230
class ContinuousCustomButton(CustomButton):
231
"""See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
233
CustomButton.__init__(self)
234
self.create_signal('held-down')
235
self.create_signal('released')
236
self.view = ContinuousDrawableButton.alloc().init()
237
self.view.setRefusesFirstResponder_(NO)
239
def set_delays(self, initial, repeat):
240
self.view.cell().setPeriodicDelay_interval_(initial, repeat)
242
class CustomSlider(drawing.DrawingMixin, Widget):
243
"""See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
245
Widget.__init__(self)
246
self.create_signal('pressed')
247
self.create_signal('released')
248
self.create_signal('changed')
249
self.create_signal('moved')
250
self.view = CustomSliderView.alloc().init()
251
self.view.setRefusesFirstResponder_(NO)
252
if self.is_continuous():
253
self.view.setContinuous_(YES)
255
self.view.setContinuous_(NO)
257
def viewport_created(self):
258
self.view.cell().setKnobThickness_(self.slider_size())
261
return self.view.floatValue()
263
def set_value(self, value):
264
self.view.setFloatValue_(value)
267
return self.view.minValue(), self.view.maxValue()
269
def set_range(self, min_value, max_value):
270
self.view.setMinValue_(min_value)
271
self.view.setMaxValue_(max_value)
273
def set_increments(self, increment, big_increment):
278
self.view.setEnabled_(True)
282
self.view.setEnabled_(False)