PageRenderTime 588ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/quodlibet/quodlibet/qltk/controls.py

https://bitbucket.org/srobertson/quodlibet-testing
Python | 257 lines | 206 code | 40 blank | 11 comment | 31 complexity | c7809e0504038bac69060f1fcea6dfa3 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2004-2005 Joe Wreschnig, Michael Urman, I?igo Serna
  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 version 2 as
  6. # published by the Free Software Foundation
  7. #
  8. # $Id$
  9. import gobject
  10. import gtk
  11. from quodlibet import config
  12. from quodlibet import qltk
  13. from quodlibet.qltk import bookmarks
  14. from quodlibet import stock
  15. from quodlibet import util
  16. from quodlibet.qltk.ccb import ConfigCheckMenuItem
  17. from quodlibet.qltk.sliderbutton import HSlider, VSlider
  18. SIZE = gtk.ICON_SIZE_LARGE_TOOLBAR
  19. class SeekBar(HSlider):
  20. __lock = False
  21. __sig = None
  22. __seekable = True
  23. def __init__(self, player, library):
  24. hbox = gtk.HBox(spacing=3)
  25. l = gtk.Label("0:00")
  26. hbox.pack_start(l)
  27. hbox.pack_start(
  28. gtk.Arrow(gtk.ARROW_RIGHT, gtk.SHADOW_NONE), expand=False)
  29. super(SeekBar, self).__init__(hbox)
  30. self.scale.connect('button-press-event', self.__seek_lock)
  31. self.scale.connect('button-release-event', self.__seek_unlock, player)
  32. self.scale.connect('key-press-event', self.__seek_lock)
  33. self.scale.connect('key-release-event', self.__seek_unlock, player)
  34. self.connect('scroll-event', self.__scroll, player)
  35. self.scale.connect('value-changed', self.__update_time, l)
  36. m = gtk.Menu()
  37. c = ConfigCheckMenuItem(
  38. _("Display remaining time"), "player", "time_remaining")
  39. c.set_active(config.getboolean("player", "time_remaining"))
  40. c.connect_object('toggled', self.scale.emit, 'value-changed')
  41. self.__remaining = c
  42. m.append(c)
  43. m.append(gtk.SeparatorMenuItem())
  44. i = qltk.MenuItem(_("_Edit Bookmarks..."), gtk.STOCK_EDIT)
  45. i.connect_object(
  46. 'activate', bookmarks.EditBookmarks, self, library, player)
  47. m.append(i)
  48. m.show_all()
  49. self.child.connect_object(
  50. 'button-press-event', self.__check_menu, m, player)
  51. self.connect_object('popup-menu', self.__popup_menu, m, player,
  52. self.child.child)
  53. gobject.timeout_add(1000, self.__check_time, player)
  54. player.connect('song-started', self.__song_changed, l, m)
  55. player.connect('seek', self.__seeked)
  56. def __check_menu(self, menu, event, player):
  57. if event.button == 3:
  58. return self.__popup_menu(menu, player)
  59. def __popup_menu(self, menu, player, widget=None):
  60. for child in menu.get_children()[2:-1]:
  61. menu.remove(child)
  62. child.destroy()
  63. try: marks = player.song.bookmarks
  64. except AttributeError: pass # song is None
  65. else:
  66. items = qltk.bookmarks.MenuItems(marks, player, self.__seekable)
  67. items.reverse()
  68. for i in items: menu.insert(i, 2)
  69. time = gtk.get_current_event_time()
  70. if widget:
  71. return qltk.popup_menu_under_widget(menu, widget, 3, time)
  72. else:
  73. menu.popup(None, None, None, 3, time)
  74. return True
  75. def __seeked(self, player, song, ms):
  76. # If it's not paused, we'll grab it in our next update.
  77. if player.paused: self.scale.set_value(ms//1000)
  78. def __scroll(self, widget, event, player):
  79. self.__lock = True
  80. if self.__sig is not None: gobject.source_remove(self.__sig)
  81. self.__sig = gobject.timeout_add(100, self.__scroll_timeout, player)
  82. def __scroll_timeout(self, player):
  83. self.__lock = False
  84. if self.__seekable: player.seek(self.scale.get_value() * 1000)
  85. self.__sig = None
  86. def __seek_lock(self, scale, event): self.__lock = True
  87. def __seek_unlock(self, scale, event, player):
  88. self.__lock = False
  89. if self.__seekable: player.seek(self.scale.get_value() * 1000)
  90. def __check_time(self, player):
  91. # When the song is paused GStreamer returns < 1 for position
  92. # queries, so if it's paused just ignore it.
  93. if not (player.paused or self.__lock):
  94. position = player.get_position() // 1000
  95. if (not self.__seekable and
  96. position > self.scale.get_adjustment().upper):
  97. self.scale.set_range(0, position)
  98. self.scale.set_value(position)
  99. return True
  100. def __update_time(self, scale, timer):
  101. value = scale.get_value()
  102. max = scale.get_adjustment().upper
  103. value -= self.__remaining.get_active() * max
  104. timer.set_text(util.format_time(value))
  105. def __song_changed(self, player, song, label, menu):
  106. if song and song.get("~#length", 0) > 0:
  107. length = song["~#length"]
  108. self.scale.set_range(0, length)
  109. self.scale.set_value(0)
  110. self.__seekable = True
  111. else:
  112. self.scale.set_range(0, 1)
  113. self.scale.set_value(0)
  114. self.__seekable = False
  115. for child in menu.get_children()[2:-1]:
  116. menu.remove(child)
  117. child.destroy()
  118. menu.get_children()[-1].set_sensitive(self.__seekable)
  119. self.scale.emit('value-changed')
  120. class Volume(VSlider):
  121. def __init__(self, device):
  122. i = gtk.image_new_from_stock(stock.VOLUME_MAX, SIZE)
  123. super(type(self), self).__init__(i)
  124. self.scale.set_update_policy(gtk.UPDATE_CONTINUOUS)
  125. self.scale.set_inverted(True)
  126. self.get_value = self.scale.get_value
  127. self.scale.connect('value-changed', self.__volume_changed, device, i)
  128. self.set_value(config.getfloat("memory", "volume"))
  129. device.connect('notify::volume', self.__volume_notify)
  130. self.__volume_changed(self.scale, device, i)
  131. self.show_all()
  132. def set_value(self, v):
  133. self.scale.set_value(max(0.0, min(1.0, v)))
  134. def __iadd__(self, v):
  135. self.set_value(min(1.0, self.get_value() + v))
  136. return self
  137. def __isub__(self, v):
  138. self.set_value(max(0.0, self.get_value() - v))
  139. return self
  140. def __volume_changed(self, slider, device, image):
  141. val = slider.get_value()
  142. if val == 0: img = stock.VOLUME_OFF
  143. elif val < 0.33: img = stock.VOLUME_MIN
  144. elif val < 0.66: img = stock.VOLUME_MED
  145. else: img = stock.VOLUME_MAX
  146. image.set_from_stock(img, SIZE)
  147. device.volume = val
  148. config.set("memory", "volume", str(slider.get_value()))
  149. def __volume_notify(self, device, property):
  150. self.scale.set_value(device.props.volume)
  151. class StopAfterMenu(gtk.Menu):
  152. def __init__(self, player):
  153. super(StopAfterMenu, self).__init__()
  154. self.__item = gtk.CheckMenuItem(_("Stop after this song"))
  155. self.__item.set_active(False)
  156. self.append(self.__item)
  157. player.connect('paused', self.__paused)
  158. player.connect('song-ended', self.__ended)
  159. self.__item.show()
  160. def __paused(self, player):
  161. self.active = False
  162. def __ended(self, player, song, stopped):
  163. if self.active:
  164. player.paused = True
  165. self.active = False
  166. def __get_active(self):
  167. return self.__item.get_active()
  168. def __set_active(self, active):
  169. return self.__item.set_active(active)
  170. active = property(__get_active, __set_active)
  171. class PlayControls(gtk.VBox):
  172. def __init__(self, player, library):
  173. gtk.VBox.__init__(self, spacing=3)
  174. hbox = gtk.HBox(spacing=3)
  175. prev = gtk.Button()
  176. prev.add(gtk.image_new_from_stock(gtk.STOCK_MEDIA_PREVIOUS, SIZE))
  177. hbox.pack_start(prev)
  178. play = gtk.ToggleButton()
  179. play.add(gtk.image_new_from_stock(gtk.STOCK_MEDIA_PLAY, SIZE))
  180. hbox.pack_start(play)
  181. self.safter = StopAfterMenu(player)
  182. next = gtk.Button()
  183. next.add(gtk.image_new_from_stock(gtk.STOCK_MEDIA_NEXT, SIZE))
  184. hbox.pack_start(next)
  185. self.pack_start(hbox, expand=False, fill=False)
  186. hbox = gtk.HBox(spacing=3)
  187. self.volume = Volume(player)
  188. hbox.pack_start(self.volume, expand=False)
  189. hbox.pack_start(SeekBar(player, library))
  190. self.pack_start(hbox, expand=False, fill=False)
  191. prev.connect_object('clicked', self.__previous, player)
  192. play.connect('toggled', self.__playpause, player)
  193. play.connect('button-press-event', self.__play_button_press, self.safter)
  194. play.connect_object('popup-menu', self.__popup, self.safter, play.child)
  195. next.connect_object('clicked', self.__next, player)
  196. player.connect('song-started', self.__song_started, next)
  197. player.connect_object('paused', play.set_active, False)
  198. player.connect_object('unpaused', play.set_active, True)
  199. self.show_all()
  200. def __play_button_press(self, activator, event, safter):
  201. if event.button == 3:
  202. return self.__popup(safter, None, event.button, event.time)
  203. def __popup(self, safter, widget, button=3, time=None):
  204. time = time or gtk.get_current_event_time()
  205. if widget:
  206. return qltk.popup_menu_under_widget(safter, widget, button, time)
  207. else:
  208. safter.popup(None, None, None, button, time)
  209. return True
  210. def __song_started(self, player, song, next):
  211. next.set_sensitive(bool(song))
  212. def __playpause(self, button, player):
  213. if button.get_active() and player.song is None:
  214. player.reset()
  215. else: player.paused = not button.get_active()
  216. def __previous(self, player): player.previous()
  217. def __next(self, player): player.next()