~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to osx/plat/frontends/widgets/customcontrol.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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
 
17
#
 
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
 
20
# library.
 
21
#
 
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.
 
28
 
 
29
"""miro.plat.frontends.widgets.customcontrol -- CustomControl handlers.  """
 
30
 
 
31
from AppKit import *
 
32
from Foundation import *
 
33
from objc import YES, NO, nil
 
34
 
 
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
 
39
 
 
40
class DrawableButtonCell(NSButtonCell):
 
41
    def startTrackingAt_inView_(self, point, view):
 
42
        view.setState_(NSOnState)
 
43
        return YES
 
44
 
 
45
    def continueTracking_at_inView_(self, lastPoint, at, view):
 
46
        view.setState_(NSOnState)
 
47
        return YES
 
48
 
 
49
    def stopTracking_at_inView_mouseIsUp_(self, lastPoint, at, view, mouseIsUp):
 
50
        if not mouseIsUp:
 
51
            view.setState_(NSOffState)
 
52
 
 
53
class DrawableButton(NSButton):
 
54
    def init(self):
 
55
        self = super(DrawableButton, self).init()
 
56
        self.layout_manager = LayoutManager()
 
57
        self.tracking_rect = None
 
58
        self.mouse_inside = False
 
59
        return self
 
60
 
 
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
 
65
 
 
66
    def viewDidMoveToWindow(self):
 
67
        self.reset_tracking_rect()
 
68
 
 
69
    def setFrame_(self, rect):
 
70
        NSButton.setFrame_(self, rect)
 
71
        self.reset_tracking_rect()
 
72
 
 
73
    def setBounds_(self, rect):
 
74
        NSButton.setBounds_(self, rect)
 
75
        self.reset_tracking_rect()
 
76
 
 
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)
 
82
 
 
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)
 
88
 
 
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)
 
94
 
 
95
    def isOpaque(self):
 
96
        return wrappermap.wrapper(self).is_opaque()
 
97
 
 
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'
 
106
        else:
 
107
            wrapper.state = 'normal'
 
108
 
 
109
        wrappermap.wrapper(self).draw(context, self.layout_manager)
 
110
        self.layout_manager.reset()
 
111
 
 
112
    def sendAction_to_(self, action, to):
 
113
        # We override the Cocoa machinery here and just send it to our wrapper
 
114
        # widget.
 
115
        wrappermap.wrapper(self).emit('clicked')
 
116
        return YES
 
117
DrawableButton.setCellClass_(DrawableButtonCell)
 
118
 
 
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,
 
123
                view, mouseIsUp)
 
124
 
 
125
class ContinuousDrawableButton(DrawableButton):
 
126
    def init(self):
 
127
        self = super(ContinuousDrawableButton, self).init()
 
128
        self.setContinuous_(YES)
 
129
        return self
 
130
 
 
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:
 
136
            if self.firedOnce:
 
137
                wrappermap.wrapper(self).emit('released')
 
138
            else:
 
139
                wrappermap.wrapper(self).emit('clicked')
 
140
 
 
141
    def sendAction_to_(self, action, to):
 
142
        if self.stopTracking:
 
143
            return NO
 
144
        self.firedOnce = True
 
145
        wrappermap.wrapper(self).emit('held-down')
 
146
        return YES
 
147
 
 
148
    def onStopTracking(self, mouseLocation):
 
149
        self.releaseInbounds = NSPointInRect(mouseLocation, self.bounds())
 
150
        self.stopTracking = True
 
151
ContinuousDrawableButton.setCellClass_(ContinousButtonCell)
 
152
 
 
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
 
157
        size -= slider_size
 
158
        return max(0, min(1, float(pos) / size))
 
159
 
 
160
    def startTrackingAt_inView_(self, at, view):
 
161
        wrappermap.wrapper(view).emit('pressed')
 
162
        return self.continueTracking_at_inView_(at, at, view)
 
163
 
 
164
    def continueTracking_at_inView_(self, lastPoint, at, view):
 
165
        if view.isVertical():
 
166
            pos = at.y
 
167
            size = view.bounds().size.height
 
168
        else:
 
169
            pos = at.x
 
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)
 
177
        return YES
 
178
    
 
179
    def stopTracking_at_inView_mouseIsUp_(self, lastPoint, at, view, mouseUp):
 
180
        wrappermap.wrapper(view).emit('released')
 
181
 
 
182
class CustomSliderView(NSSlider):
 
183
    def init(self):
 
184
        self = super(CustomSliderView, self).init()
 
185
        self.layout_manager = LayoutManager()
 
186
        return self
 
187
 
 
188
    def isOpaque(self):
 
189
        return wrappermap.wrapper(self).is_opaque()
 
190
 
 
191
    def knobThickness(self):
 
192
        return wrappermap.wrapper(self).slider_size()
 
193
 
 
194
    def isVertical(self):
 
195
        return not wrappermap.wrapper(self).is_horizontal()
 
196
 
 
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()
 
202
 
 
203
    def sendAction_to_(self, action, to):
 
204
        # We override the Cocoa machinery here and just send it to our wrapper
 
205
        # widget.
 
206
        wrappermap.wrapper(self).emit('changed', self.floatValue())
 
207
        return YES
 
208
CustomSliderView.setCellClass_(CustomSliderCell)
 
209
 
 
210
class CustomButton(drawing.DrawingMixin, Widget):
 
211
    """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
 
212
    def __init__(self):
 
213
        Widget.__init__(self)
 
214
        self.create_signal('clicked')
 
215
        self.view = DrawableButton.alloc().init()
 
216
        self.view.setRefusesFirstResponder_(NO)
 
217
    
 
218
    def enable(self):
 
219
        Widget.enable(self)
 
220
        self.view.setEnabled_(True)
 
221
 
 
222
    def disable(self):
 
223
        Widget.disable(self)
 
224
        self.view.setEnabled_(False)
 
225
 
 
226
    def remove_viewport(self):
 
227
        self.view.remove_tracking_rect()
 
228
        Widget.remove_viewport(self)
 
229
 
 
230
class ContinuousCustomButton(CustomButton):
 
231
    """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
 
232
    def __init__(self):
 
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)
 
238
 
 
239
    def set_delays(self, initial, repeat):
 
240
        self.view.cell().setPeriodicDelay_interval_(initial, repeat)
 
241
 
 
242
class CustomSlider(drawing.DrawingMixin, Widget):
 
243
    """See https://develop.participatoryculture.org/index.php/WidgetAPI for a description of the API for this class."""
 
244
    def __init__(self):
 
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)
 
254
        else:
 
255
            self.view.setContinuous_(NO)
 
256
 
 
257
    def viewport_created(self):
 
258
        self.view.cell().setKnobThickness_(self.slider_size())
 
259
 
 
260
    def get_value(self):
 
261
        return self.view.floatValue()
 
262
 
 
263
    def set_value(self, value):
 
264
        self.view.setFloatValue_(value)
 
265
 
 
266
    def get_range(self):
 
267
        return self.view.minValue(), self.view.maxValue()
 
268
 
 
269
    def set_range(self, min_value, max_value):
 
270
        self.view.setMinValue_(min_value)
 
271
        self.view.setMaxValue_(max_value)
 
272
 
 
273
    def set_increments(self, increment, big_increment):
 
274
        pass
 
275
 
 
276
    def enable(self):
 
277
        Widget.enable(self)
 
278
        self.view.setEnabled_(True)
 
279
 
 
280
    def disable(self):
 
281
        Widget.disable(self)
 
282
        self.view.setEnabled_(False)