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

« back to all changes in this revision

Viewing changes to platform/osx/plat/frontends/widgets/overlay.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
 
import time
30
 
 
31
 
from objc import nil, YES, NO, IBOutlet
32
 
from AppKit import *
33
 
from Foundation import *
34
 
from PyObjCTools import AppHelper
35
 
 
36
 
from miro import app
37
 
from miro import messages
38
 
from miro.gtcache import gettext as _
39
 
from miro.frontends.widgets.widgetconst import MAX_VOLUME
40
 
from miro.plat import resources
41
 
from miro.plat.frontends.widgets import threads
42
 
from miro.plat.frontends.widgets import drawing
43
 
from miro.plat.frontends.widgets import osxmenus
44
 
 
45
 
###############################################################################
46
 
 
47
 
class OverlayPaletteWindow (NSWindow):
48
 
 
49
 
    def initWithContentRect_styleMask_backing_defer_(self, rect, style, backing, defer):
50
 
        self = super(OverlayPaletteWindow, self).initWithContentRect_styleMask_backing_defer_(
51
 
            rect,
52
 
            NSBorderlessWindowMask,
53
 
            backing,
54
 
            defer )
55
 
        self.setBackgroundColor_(NSColor.clearColor())
56
 
        self.setAlphaValue_(1.0)
57
 
        self.setOpaque_(NO)
58
 
        return self
59
 
 
60
 
    def canBecomeKeyWindow(self):
61
 
        return NO
62
 
 
63
 
    def canBecomeMainWindow(self):
64
 
        return NO
65
 
 
66
 
###############################################################################
67
 
 
68
 
overlay = None
69
 
 
70
 
class OverlayPalette (NSWindowController):
71
 
    
72
 
    titleLabel          = IBOutlet('titleLabel')
73
 
    feedLabel           = IBOutlet('feedLabel')
74
 
    shareButton         = IBOutlet('shareButton')
75
 
    keepButton          = IBOutlet('keepButton')
76
 
    deleteButton        = IBOutlet('deleteButton')
77
 
    fsButton            = IBOutlet('fsButton')
78
 
    popInOutButton      = IBOutlet('popInOutButton')
79
 
    popInOutLabel       = IBOutlet('popInOutLabel')
80
 
    
81
 
    playbackControls    = IBOutlet('playbackControls')
82
 
    playPauseButton     = IBOutlet('playPauseButton')
83
 
    progressSlider      = IBOutlet('progressSlider')
84
 
    seekBackwardButton  = IBOutlet('seekBackwardButton')
85
 
    skipBackwardButton  = IBOutlet('skipBackwardButton')
86
 
    seekForwardButton   = IBOutlet('seekForwardButton')
87
 
    skipForwardButton   = IBOutlet('skipForwardButton')
88
 
    timeIndicator       = IBOutlet('timeIndicator')
89
 
    volumeSlider        = IBOutlet('volumeSlider')
90
 
    
91
 
    HOLD_TIME = 2
92
 
 
93
 
    @classmethod
94
 
    def get_instance(cls):
95
 
        global overlay
96
 
        if overlay is None:
97
 
            overlay = OverlayPalette.alloc().init()
98
 
        return overlay
99
 
 
100
 
    def init(self):
101
 
        self = super(OverlayPalette, self).initWithWindowNibName_owner_('OverlayPalette', self)
102
 
        self.item_info = None
103
 
        self.autoHidingTimer = nil
104
 
        self.updateTimer = nil
105
 
        self.holdStartTime = 0.0
106
 
        self.renderer = None
107
 
        self.wasPlaying = False
108
 
        self.revealing = False
109
 
        self.hiding = False
110
 
        self.anim = None
111
 
        self.in_fullscreen = False
112
 
 
113
 
        app.playback_manager.connect('will-play', self.video_will_play)
114
 
        app.playback_manager.connect('will-pause', self.video_will_pause)
115
 
 
116
 
        return self
117
 
 
118
 
    def awakeFromNib(self):
119
 
        self.shareButton.setImage_(getOverlayButtonImage(self.shareButton.bounds().size))
120
 
        self.shareButton.setAlternateImage_(getOverlayButtonAlternateImage(self.shareButton.bounds().size))
121
 
        self.shareButton.setTitle_(_("Share"))
122
 
 
123
 
        self.keepButton.setImage_(getOverlayButtonImage(self.keepButton.bounds().size))
124
 
        self.keepButton.setAlternateImage_(getOverlayButtonAlternateImage(self.keepButton.bounds().size))
125
 
        self.keepButton.setTitle_(_("Keep"))
126
 
 
127
 
        self.deleteButton.setImage_(getOverlayButtonImage(self.deleteButton.bounds().size))
128
 
        self.deleteButton.setAlternateImage_(getOverlayButtonAlternateImage(self.deleteButton.bounds().size))
129
 
        self.deleteButton.setTitle_(_("Delete"))
130
 
 
131
 
        self.seekForwardButton.setCell_(SkipSeekButtonCell.cellFromButtonCell_direction_delay_(self.seekForwardButton.cell(), 1, 0.0))
132
 
        self.seekForwardButton.cell().setAllowsSkipping(False)
133
 
        self.seekBackwardButton.setCell_(SkipSeekButtonCell.cellFromButtonCell_direction_delay_(self.seekBackwardButton.cell(), -1, 0.0))
134
 
        self.seekBackwardButton.cell().setAllowsSkipping(False)
135
 
 
136
 
        self.progressSlider.cursor = NSImage.imageNamed_(u'fs-progress-slider')
137
 
        self.progressSlider.sliderWasClicked = self.progressSliderWasClicked
138
 
        self.progressSlider.sliderWasDragged = self.progressSliderWasDragged
139
 
        self.progressSlider.sliderWasReleased = self.progressSliderWasReleased
140
 
        self.progressSlider.setShowCursor_(True)
141
 
 
142
 
        self.volumeSlider.cursor = NSImage.imageNamed_(u'fs-volume-slider')
143
 
        self.volumeSlider.sliderWasClicked = self.volumeSliderWasClicked
144
 
        self.volumeSlider.sliderWasDragged = self.volumeSliderWasDragged
145
 
        self.volumeSlider.setShowCursor_(True)
146
 
 
147
 
    def setup(self, item_info, renderer, video_window):
148
 
        from miro.frontends.widgets import widgetutil
149
 
        self.item_info = item_info
150
 
        self.renderer = renderer
151
 
        self.titleLabel.setStringValue_(item_info.name)
152
 
        try:
153
 
            self.feedLabel.setStringValue_(widgetutil.get_feed_info(item_info.feed_id).name)
154
 
        except:
155
 
            self.feedLabel.setStringValue_("")
156
 
        self.keepButton.setEnabled_(item_info.can_be_saved)
157
 
        self.shareButton.setEnabled_(item_info.has_sharable_url)
158
 
        self.adjustContent(video_window, False)
159
 
        self.update_(nil)
160
 
        self.suspendAutoHiding()
161
 
        self.reveal(video_window)
162
 
 
163
 
    def on_items_changed(self, changed):
164
 
        for item_info in changed:
165
 
            if item_info.id == self.item_info.id:
166
 
                self.keepButton.setEnabled_(item_info.can_be_saved)
167
 
                self.shareButton.setEnabled_(item_info.has_sharable_url)
168
 
                self.update_(nil)
169
 
                
170
 
    def enter_fullscreen(self, videoWindow):
171
 
        self.in_fullscreen = True
172
 
        if self.window().isVisible():
173
 
            self.adjustContent(videoWindow, True)
174
 
        else:
175
 
            NSCursor.setHiddenUntilMouseMoves_(YES)
176
 
 
177
 
    def exit_fullscreen(self, videoWindow):
178
 
        self.in_fullscreen = False
179
 
        if self.window().isVisible():
180
 
            self.adjustContent(videoWindow, True)
181
 
 
182
 
    def showSubtitlesMenu_(self, sender):
183
 
        menu = NSMenu.alloc().init()
184
 
        menu.setAutoenablesItems_(NO)
185
 
        subtitles_tracks = app.playback_manager.player.get_subtitle_tracks()
186
 
        osxmenus.populate_subtitles_menu(menu, subtitles_tracks)
187
 
        NSMenu.popUpContextMenu_withEvent_forView_(menu, NSApp().currentEvent(), self.window().contentView())
188
 
    
189
 
    def getHorizontalPosition(self, videoWindow, width):
190
 
        parentFrame = videoWindow.frame()
191
 
        return parentFrame.origin.x + ((parentFrame.size.width - width) / 2.0)
192
 
 
193
 
    def adjustPosition(self, videoWindow):
194
 
        parentFrame = videoWindow.frame()
195
 
        x = self.getHorizontalPosition(videoWindow, self.window().frame().size.width)
196
 
        y = parentFrame.origin.y + 60
197
 
        self.window().setFrameOrigin_(NSPoint(x, y))
198
 
 
199
 
    def adjustContent(self, videoWindow, animate):
200
 
        if videoWindow.is_fullscreen:
201
 
            self.popInOutButton.setHidden_(YES)
202
 
            self.popInOutLabel.setHidden_(YES)
203
 
            self.fsButton.setImage_(NSImage.imageNamed_('fs-button-exitfullscreen'))
204
 
            self.fsButton.setAlternateImage_(NSImage.imageNamed_('fs-button-exitfullscreen-alt'))
205
 
        else:
206
 
            if app.playback_manager.detached_window is None:
207
 
                image_path = resources.path('images/popout.png')
208
 
                label = _('Pop Out')
209
 
            else:
210
 
                image_path = resources.path('images/popin.png')
211
 
                label = _('Pop In')
212
 
            self.popInOutButton.setImage_(NSImage.alloc().initWithContentsOfFile_(image_path))
213
 
            self.popInOutButton.setHidden_(NO)
214
 
            self.popInOutLabel.setHidden_(NO)
215
 
            self.popInOutLabel.setStringValue_(label)
216
 
            self.fsButton.setImage_(NSImage.imageNamed_('fs-button-enterfullscreen'))
217
 
            self.fsButton.setAlternateImage_(NSImage.imageNamed_('fs-button-enterfullscreen-alt'))
218
 
 
219
 
        newFrame = self.window().frame() 
220
 
        if videoWindow.is_fullscreen or app.playback_manager.detached_window is not None: 
221
 
            self.titleLabel.setHidden_(NO)
222
 
            self.feedLabel.setHidden_(NO)
223
 
            newFrame.size.height = 198 
224
 
        else: 
225
 
            self.titleLabel.setHidden_(YES)
226
 
            self.feedLabel.setHidden_(YES)
227
 
            newFrame.size.height = 144
228
 
        newFrame.origin.x = self.getHorizontalPosition(videoWindow, newFrame.size.width)
229
 
        self.window().setFrame_display_animate_(newFrame, YES, animate)
230
 
        self.playbackControls.setNeedsDisplay_(YES)
231
 
 
232
 
    def fit_in_video_window(self, video_window):
233
 
        return self.window().frame().size.width <= video_window.frame().size.width
234
 
 
235
 
    def reveal(self, videoWindow):
236
 
        threads.warn_if_not_on_main_thread('OverlayPalette.reveal')
237
 
        self.resetAutoHiding()
238
 
        if (not self.window().isVisible() and not self.revealing) or (self.window().isVisible() and self.hiding):
239
 
            self.update_(nil)
240
 
            if self.renderer.movie is not None:
241
 
                self.set_volume(self.renderer.movie.volume())
242
 
 
243
 
            self.adjustPosition(videoWindow)
244
 
            self.adjustContent(videoWindow, False)
245
 
 
246
 
            if self.hiding and self.anim is not None:
247
 
                self.anim.stopAnimation()
248
 
                self.hiding = False
249
 
            else:
250
 
                self.window().setAlphaValue_(0.0)
251
 
 
252
 
            self.window().orderFront_(nil)
253
 
            videoWindow.parentWindow().addChildWindow_ordered_(self.window(), NSWindowAbove)
254
 
 
255
 
            self.revealing = True
256
 
            params = {NSViewAnimationTargetKey: self.window(), NSViewAnimationEffectKey: NSViewAnimationFadeInEffect}
257
 
            self.animate(params, 0.3)
258
 
        else:
259
 
            self.resumeAutoHiding()
260
 
    
261
 
    def hide(self):
262
 
        threads.warn_if_not_on_main_thread('OverlayPalette.hide')
263
 
        if not self.hiding:
264
 
            if self.autoHidingTimer is not nil:
265
 
                self.autoHidingTimer.invalidate()
266
 
                self.autoHidingTimer = nil
267
 
 
268
 
            if self.revealing and self.anim is not None:
269
 
                self.anim.stopAnimation()
270
 
                self.revealing = False
271
 
 
272
 
            self.hiding = True
273
 
            params = {NSViewAnimationTargetKey: self.window(), NSViewAnimationEffectKey: NSViewAnimationFadeOutEffect}
274
 
            self.animate(params, 0.5)
275
 
    
276
 
    def hideAfterDelay_(self, timer):
277
 
        if time.time() - self.holdStartTime > self.HOLD_TIME:
278
 
            self.hide()
279
 
    
280
 
    def resetAutoHiding(self):
281
 
        self.holdStartTime = time.time()
282
 
    
283
 
    def suspendAutoHiding(self):
284
 
        if self.autoHidingTimer is not nil:
285
 
            self.autoHidingTimer.invalidate()
286
 
            self.autoHidingTimer = nil
287
 
    
288
 
    def resumeAutoHiding(self):
289
 
        self.resetAutoHiding()
290
 
        if self.autoHidingTimer is None:
291
 
            self.autoHidingTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
292
 
                1.0, self, 'hideAfterDelay:', nil, YES)
293
 
    
294
 
    def animate(self, params, duration):
295
 
        self.anim = NSViewAnimation.alloc().initWithDuration_animationCurve_(duration, 0)
296
 
        self.anim.setViewAnimations_(NSArray.arrayWithObject_(params))
297
 
        self.anim.setDelegate_(self)
298
 
        self.anim.startAnimation()
299
 
 
300
 
    def animationDidEnd_(self, anim):
301
 
        parent = self.window().parentWindow()
302
 
        if self.revealing:
303
 
            self.resumeAutoHiding()
304
 
            self.updateTimer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
305
 
                0.5, self, 'update:', nil, YES)
306
 
            NSRunLoop.currentRunLoop().addTimer_forMode_(self.updateTimer, NSEventTrackingRunLoopMode)
307
 
            self.update_(nil)
308
 
            if parent is not None and self.in_fullscreen:
309
 
                NSCursor.setHiddenUntilMouseMoves_(NO)
310
 
        elif self.hiding:
311
 
            if parent is not None and self.in_fullscreen:
312
 
                NSCursor.setHiddenUntilMouseMoves_(YES)
313
 
            self.remove()
314
 
 
315
 
        self.anim = None
316
 
        self.revealing = False
317
 
        self.hiding = False
318
 
    
319
 
    def set_volume(self, volume):
320
 
        self.volumeSlider.setFloatValue_(volume / MAX_VOLUME)
321
 
    
322
 
    def keep_(self, sender):
323
 
        messages.KeepVideo(self.item_info.id).send_to_backend()
324
 
    
325
 
    def expireNow_(self, sender):
326
 
        item_info = self.item_info
327
 
        app.playback_manager.on_movie_finished()
328
 
        app.widgetapp.remove_items([item_info])
329
 
        
330
 
    def share_(self, sender):
331
 
        item_info = self.item_info
332
 
        app.widgetapp.share_item(item_info)
333
 
    
334
 
    def handleShareItem_(self, sender):
335
 
        pass
336
 
 
337
 
    def toggleFullScreen_(self, sender):
338
 
        app.playback_manager.toggle_fullscreen()
339
 
 
340
 
    def toggleAttachedDetached_(self, sender):
341
 
        app.playback_manager.toggle_detached_mode()
342
 
 
343
 
    def skipBackward_(self, sender):
344
 
        app.playback_manager.play_prev_movie()
345
 
 
346
 
    def fastBackward_(self, sender):
347
 
        self.fastSeek(-1)
348
 
 
349
 
    def skipForward_(self, sender):
350
 
        app.playback_manager.play_next_movie()
351
 
 
352
 
    def fastForward_(self, sender):
353
 
        self.fastSeek(1)
354
 
 
355
 
    def fastSeek(self, direction):
356
 
        rate = 3 * direction
357
 
        app.playback_manager.set_playback_rate(rate)
358
 
        self.suspendAutoHiding()
359
 
 
360
 
    def stopSeeking(self):
361
 
        rate = 1.0
362
 
        if app.playback_manager.is_paused:
363
 
            rate = 0.0
364
 
        app.playback_manager.set_playback_rate(rate)
365
 
        self.resumeAutoHiding()
366
 
 
367
 
    def stop_(self, sender):
368
 
        self.remove()
369
 
        app.playback_manager.stop()
370
 
 
371
 
    def playPause_(self, sender):
372
 
        app.playback_manager.play_pause()
373
 
 
374
 
    def update_(self, timer):
375
 
        if self.renderer.movie is None:
376
 
            return
377
 
        elapsed = self.renderer.get_elapsed_playback_time()
378
 
        total = self.renderer.get_total_playback_time()
379
 
        progress = u"%d:%02d" % divmod(int(round(elapsed)), 60)
380
 
        self.timeIndicator.setStringValue_(progress)
381
 
        self.progressSlider.setFloatValue_(elapsed / total)
382
 
            
383
 
    def progressSliderWasClicked(self, slider):
384
 
        if app.playback_manager.is_playing:
385
 
            self.wasPlaying = True
386
 
            self.renderer.pause()
387
 
        app.playback_manager.seek_to(slider.floatValue())
388
 
        self.resetAutoHiding()
389
 
        
390
 
    def progressSliderWasDragged(self, slider):
391
 
        app.playback_manager.seek_to(slider.floatValue())
392
 
        self.resetAutoHiding()
393
 
        
394
 
    def progressSliderWasReleased(self, slider):
395
 
        if self.wasPlaying:
396
 
            self.wasPlaying = False
397
 
            self.renderer.play()
398
 
 
399
 
    def volumeSliderWasDragged(self, slider):
400
 
        volume = slider.floatValue() * MAX_VOLUME
401
 
        app.playback_manager.set_volume(volume)
402
 
        app.widgetapp.window.videobox.volume_slider.set_value(volume)
403
 
        self.resetAutoHiding()
404
 
 
405
 
    def volumeSliderWasClicked(self, slider):
406
 
        self.volumeSliderWasDragged(slider)
407
 
 
408
 
    def video_will_play(self, obj, duration):
409
 
        self.playPauseButton.setImage_(NSImage.imageNamed_(u'fs-button-pause'))
410
 
        self.playPauseButton.setAlternateImage_(NSImage.imageNamed_(u'fs-button-pause-alt'))
411
 
 
412
 
    def video_will_pause(self, obj):
413
 
        self.playPauseButton.setImage_(NSImage.imageNamed_(u'fs-button-play'))
414
 
        self.playPauseButton.setAlternateImage_(NSImage.imageNamed_(u'fs-button-play-alt'))
415
 
 
416
 
    def remove(self):
417
 
        threads.warn_if_not_on_main_thread('OverlayPalette.remove')
418
 
        self.suspendAutoHiding()
419
 
        if self.updateTimer is not nil:
420
 
            self.updateTimer.invalidate()
421
 
            self.updateTimer = nil
422
 
        if self.window().parentWindow() is not nil:
423
 
            self.window().parentWindow().removeChildWindow_(self.window())
424
 
        self.window().orderOut_(nil)
425
 
 
426
 
###############################################################################
427
 
 
428
 
class OverlayPaletteView (NSView):
429
 
 
430
 
    def drawRect_(self, rect):
431
 
        radius = 8
432
 
        lineWidth = 2
433
 
        rect = NSInsetRect(self.frame(), radius+lineWidth, radius+lineWidth)
434
 
        
435
 
        path = NSBezierPath.bezierPath()
436
 
        path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(NSPoint(NSMinX(rect), NSMinY(rect)), radius, 180.0, 270.0)
437
 
        path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(NSPoint(NSMaxX(rect), NSMinY(rect)), radius, 270.0, 360.0)
438
 
        path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(NSPoint(NSMaxX(rect), NSMaxY(rect)), radius,   0.0,  90.0)
439
 
        path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(NSPoint(NSMinX(rect), NSMaxY(rect)), radius,  90.0, 180.0)
440
 
        path.closePath()
441
 
        
442
 
        transform = NSAffineTransform.transform()
443
 
        transform.translateXBy_yBy_(0.5, 0.5)
444
 
        path.transformUsingAffineTransform_(transform)
445
 
        
446
 
        NSColor.colorWithDeviceWhite_alpha_(0.0, 0.6).set()
447
 
        path.fill()
448
 
        
449
 
        NSColor.colorWithDeviceWhite_alpha_(1.0, 0.6).set()
450
 
        path.setLineWidth_(lineWidth)
451
 
        path.stroke()
452
 
 
453
 
###############################################################################
454
 
 
455
 
class OverlayPaletteControlsView (NSView):
456
 
 
457
 
    def drawRect_(self, rect):
458
 
        bounds = self.bounds()
459
 
        NSColor.colorWithDeviceWhite_alpha_(1.0, 0.6).set()
460
 
 
461
 
        path = NSBezierPath.bezierPath()
462
 
        path.moveToPoint_(NSPoint(0.5, 0.5))
463
 
        path.relativeLineToPoint_(NSPoint(bounds.size.width, 0))
464
 
        path.setLineWidth_(2)
465
 
        path.stroke()
466
 
 
467
 
        if app.playback_manager.is_fullscreen or app.playback_manager.detached_window is not None:
468
 
            path = NSBezierPath.bezierPath()
469
 
            path.moveToPoint_(NSPoint(0.5, bounds.size.height-1.5))
470
 
            path.relativeLineToPoint_(NSPoint(bounds.size.width, 0))
471
 
            path.setLineWidth_(2)
472
 
            path.stroke()
473
 
        
474
 
    def hitTest_(self, point):
475
 
        # Our buttons have transparent parts, but we still want mouse clicks
476
 
        # to be detected if they happen there, so we override hit testing and
477
 
        # simply test for button frames.
478
 
        for subview in self.subviews():
479
 
            if NSPointInRect(self.convertPoint_fromView_(point, nil), subview.frame()):
480
 
                return subview
481
 
        return self
482
 
 
483
 
###############################################################################
484
 
 
485
 
class Slider (NSView):
486
 
 
487
 
    def initWithFrame_(self, frame):
488
 
        self = super(Slider, self).initWithFrame_(frame)
489
 
        self.value = 0.0
490
 
        self.showCursor = False
491
 
        self.dragging = False
492
 
        self.sliderWasClicked = None
493
 
        self.sliderWasDragged = None
494
 
        self.sliderWasReleased = None
495
 
        return self
496
 
 
497
 
    def setFloatValue_(self, value):
498
 
        threads.warn_if_not_on_main_thread('Slider.setFloatValue_')
499
 
        self.value = value
500
 
        self.setNeedsDisplay_(YES)
501
 
        
502
 
    def floatValue(self):
503
 
        return self.value
504
 
 
505
 
    def setShowCursor_(self, showCursor):
506
 
        self.showCursor = showCursor
507
 
 
508
 
    def drawRect_(self, rect):
509
 
        if self.showCursor:
510
 
            self.drawTrack()
511
 
            self.drawCursor()
512
 
 
513
 
    def drawTrack(self):
514
 
        pass
515
 
 
516
 
    def drawCursor(self):
517
 
        x = self.getCursorPosition()
518
 
        self.cursor.compositeToPoint_operation_((abs(x)+0.5, 0), NSCompositeSourceOver)
519
 
 
520
 
    def getCursorPosition(self):
521
 
        return (self.bounds().size.width - self.cursor.size().width) * self.value
522
 
 
523
 
    def mouseDown_(self, event):
524
 
        if self.showCursor:
525
 
            location = self.convertPoint_fromView_(event.locationInWindow(), nil)
526
 
            if NSPointInRect(location, self.bounds()):
527
 
                self.dragging = True
528
 
                self.setFloatValue_(self.getValueForClickLocation(location))
529
 
                if self.sliderWasClicked is not None:
530
 
                    self.sliderWasClicked(self)
531
 
 
532
 
    def mouseDragged_(self, event):
533
 
        if self.showCursor and self.dragging:
534
 
            location = self.convertPoint_fromView_(event.locationInWindow(), nil)
535
 
            self.setFloatValue_(self.getValueForClickLocation(location))
536
 
            if self.sliderWasDragged is not None:
537
 
                self.sliderWasDragged(self)
538
 
 
539
 
    def mouseUp_(self, event):
540
 
        if self.showCursor:
541
 
            self.dragging = False
542
 
            if self.sliderWasReleased is not None:
543
 
                self.sliderWasReleased(self)
544
 
            self.setNeedsDisplay_(YES)
545
 
 
546
 
    def getValueForClickLocation(self, location):
547
 
        min = self.cursor.size().width / 2.0
548
 
        max = self.bounds().size.width - min
549
 
        span = max - min
550
 
        offset = location.x
551
 
        if offset < min:
552
 
            offset = min
553
 
        elif offset > max:
554
 
            offset = max
555
 
        return (offset - min) / span
556
 
 
557
 
class OverlayPaletteSlider (Slider):
558
 
 
559
 
    def drawTrack(self):
560
 
        from miro.frontends.widgets import widgetutil
561
 
        rect = self.bounds()
562
 
        ctx = drawing.DrawingContext(self, rect, rect)
563
 
        ctx.set_color((1,1,1), 0.4)
564
 
        widgetutil.circular_rect(ctx, 0, 2, rect.size.width, rect.size.height - 4)
565
 
        ctx.fill()
566
 
 
567
 
###############################################################################
568
 
 
569
 
class SkipSeekButtonCell (NSButtonCell):
570
 
 
571
 
    @classmethod
572
 
    def cellFromButtonCell_direction_delay_(self, cell, direction, delay):
573
 
        newCell = SkipSeekButtonCell.alloc().initWithPrimaryAction_direction_delay_(cell.action(), direction, delay)
574
 
        newCell.setType_(cell.type())
575
 
        newCell.setBezeled_(cell.isBezeled())
576
 
        newCell.setBezelStyle_(cell.bezelStyle())
577
 
        newCell.setBordered_(cell.isBordered())
578
 
        newCell.setTransparent_(cell.isTransparent())
579
 
        newCell.setImage_(cell.image())
580
 
        newCell.setAlternateImage_(cell.alternateImage())
581
 
        newCell.setState_(cell.state())
582
 
        newCell.setHighlightsBy_(cell.highlightsBy())
583
 
        newCell.setShowsStateBy_(cell.showsStateBy())
584
 
        newCell.setEnabled_(cell.isEnabled())
585
 
        newCell.setTarget_(cell.target())
586
 
        newCell.setAction_(nil)
587
 
        return newCell
588
 
    
589
 
    def initWithPrimaryAction_direction_delay_(self, action, direction, delay):
590
 
        self = super(SkipSeekButtonCell, self).init()
591
 
        self.primaryAction = action
592
 
        self.direction = direction
593
 
        self.seekTimer = nil
594
 
        self.seekDelay = delay
595
 
        self.allowSkipping = True
596
 
        self.allowSeeking = True
597
 
        return self
598
 
    
599
 
    def setAllowsFastSeeking(self, allow):
600
 
        self.allowSeeking = allow
601
 
    
602
 
    def setAllowsSkipping(self, allow):
603
 
        self.allowSkipping = allow
604
 
    
605
 
    def trackMouse_inRect_ofView_untilMouseUp_(self, event, frame, control, untilMouseUp):
606
 
        if self.allowSeeking:
607
 
            if self.seekDelay > 0.0:
608
 
                self.seekTimer = NSTimer.timerWithTimeInterval_target_selector_userInfo_repeats_(self.seekDelay, self, 'fastSeek:', nil, NO)
609
 
                NSRunLoop.currentRunLoop().addTimer_forMode_(self.seekTimer, NSEventTrackingRunLoopMode)
610
 
            else:
611
 
                self.fastSeek_(nil)
612
 
 
613
 
        mouseIsUp = NSButtonCell.trackMouse_inRect_ofView_untilMouseUp_(self, event, frame, control, YES)
614
 
 
615
 
        if self.seekTimer is not nil or not self.allowSeeking:
616
 
            self.resetSeekTimer()
617
 
            control.sendAction_to_(self.primaryAction, self.target())
618
 
        else:
619
 
            self.target().stopSeeking()
620
 
            
621
 
        return mouseIsUp
622
 
 
623
 
    def fastSeek_(self, timer):
624
 
        self.target().fastSeek(self.direction)
625
 
        self.resetSeekTimer()
626
 
    
627
 
    def resetSeekTimer(self):
628
 
        if self.seekTimer is not nil:
629
 
            self.seekTimer.invalidate()
630
 
            self.seekTimer = nil
631
 
 
632
 
###############################################################################
633
 
 
634
 
def getOverlayButtonImage(size):
635
 
    fillColor = NSColor.colorWithDeviceWhite_alpha_(190.0/255.0, 0.8)
636
 
    strokeColor = NSColor.colorWithDeviceWhite_alpha_(76.0/255.0, 0.8)
637
 
    return makeOverlayButtonImage(size, fillColor, strokeColor)
638
 
 
639
 
def getOverlayButtonAlternateImage(size):
640
 
    fillColor = NSColor.colorWithDeviceWhite_alpha_(220.0/255.0, 0.8)
641
 
    strokeColor = NSColor.colorWithDeviceWhite_alpha_(106.0/255.0, 0.8)
642
 
    return makeOverlayButtonImage(size, fillColor, strokeColor)
643
 
 
644
 
def makeOverlayButtonImage(size, fillColor, strokeColor):
645
 
    radius = (size.height-1) / 2.0
646
 
    path = NSBezierPath.bezierPath()
647
 
    path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(NSPoint(radius+1.5, radius+0.5), radius, 90.0, 270.0)
648
 
    path.appendBezierPathWithArcWithCenter_radius_startAngle_endAngle_(NSPoint(size.width - radius - 1.5, radius+0.5), radius, 270.0, 90.0)
649
 
    path.closePath()
650
 
 
651
 
    image = NSImage.alloc().initWithSize_(size)
652
 
    image.lockFocus()
653
 
    
654
 
    fillColor.set()
655
 
    path.fill()
656
 
    strokeColor.set()
657
 
    path.stroke()
658
 
    
659
 
    image.unlockFocus()
660
 
    return image