2
# Copyright (C) 2006 Adam Olsen <arolsen@gmail.com>
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 along
15
# with this program; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
import gtk, gobject, pango
20
import xl.plugins as plugins
22
PLUGIN_NAME = "Mini Mode"
23
PLUGIN_AUTHORS = ['Adam Olsen <arolsen@gmail.com>']
24
PLUGIN_VERSION = '0.4.4'
25
PLUGIN_DESCRIPTION = r"""Super groovy mini mode window!\n\nMini Mode is activated
26
by pressing CTRL+ALT+M\n\nYou can move the window in most cases by
28
PLUGIN_ENABLED = False
36
CONS = plugins.SignalContainer()
38
def toggle_minimode(*e):
43
if not PLUGIN.get_property("visible"):
51
print "Minimode toggled"
55
Configuration for mini mode
58
settings = exaile.settings
60
dialog = plugins.PluginConfigDialog(exaile.window, PLUGIN_NAME)
63
on_top = settings.get_boolean('on_top', plugin=plugins.name(__file__), default=False)
64
no_taskbar = settings.get_boolean('no_taskbar',
65
plugin=plugins.name(__file__), default=False)
66
decoration = settings.get_boolean('decoration',
67
plugin=plugins.name(__file__), default=False)
69
on_top_box = gtk.CheckButton('Always on top')
70
no_taskbar_box = gtk.CheckButton('Skip taskbar')
71
decoration_box = gtk.CheckButton('Window decoration')
73
on_top_box.set_active(on_top)
74
no_taskbar_box.set_active(no_taskbar)
75
decoration_box.set_active(decoration)
77
box.pack_start(on_top_box)
78
box.pack_start(no_taskbar_box)
79
box.pack_start(decoration_box)
85
settings.set_boolean('on_top', on_top_box.get_active(), plugin=plugins.name(__file__))
86
settings.set_boolean('no_taskbar', no_taskbar_box.get_active(), plugin=plugins.name(__file__))
87
settings.set_boolean('decoration', decoration_box.get_active(), plugin=plugins.name(__file__))
89
class MiniWindow(gtk.Window):
91
The main minimode window
95
Initializes the minimode window
97
gtk.Window.__init__(self)
99
self.set_title("Exaile")
100
self.set_icon(APP.window.get_icon())
101
self.tips = gtk.Tooltips()
106
main.set_border_width(3)
107
self.tlabel = gtk.Label("")
108
self.tlabel.set_size_request(120, 10)
109
self.tlabel.set_alignment(0.0, 0.5)
114
prev = self.create_button('gtk-media-previous', self.on_prev,
116
bbox.pack_start(prev, False)
118
self.play = self.create_button('gtk-media-play', self.on_play,
120
bbox.pack_start(self.play, False)
122
self.next = self.create_button('gtk-media-next', self.on_next, 'Next')
123
bbox.pack_start(self.next)
125
self.model = gtk.ListStore(str, object)
126
self.title_box = gtk.ComboBox(self.model)
127
cell = gtk.CellRendererText()
128
cell.set_property('font-desc', pango.FontDescription('Normal 8'))
129
cell.set_property('ellipsize', pango.ELLIPSIZE_END)
131
self.title_box.pack_start(cell, True)
132
self.title_box.set_size_request(170, 26)
133
self.title_box.add_attribute(cell, 'text', 0)
135
self.title_box.connect('changed', self.change_track)
136
bbox.pack_start(self.title_box, False)
138
self.seeker = gtk.HScale(gtk.Adjustment(0, 0, 100, 1, 5, 0))
139
self.seeker.set_draw_value(False)
140
self.seeker.set_size_request(150, -1)
141
self.seeker_id = self.seeker.connect('change-value', self.seek)
142
bbox.pack_start(self.seeker, False)
144
self.pos_label = gtk.Label("0:00")
145
self.pos_label.set_size_request(40, -1)
146
self.pos_label.set_alignment(0.0, .5)
147
bbox.pack_start(self.pos_label)
149
mm = self.create_button('gtk-fullscreen', toggle_minimode, 'Restore'
151
bbox.pack_start(mm, False)
153
main.pack_start(bbox)
157
self.connect('configure-event', self.on_move)
160
def seek(self, range, scroll, value):
162
Seeks in the current track
166
gobject.source_remove(self.seek_id)
170
self.seek_id = gobject.timeout_add(250, self._seek_cb, range)
173
def _seek_cb(self, range):
175
duration = APP.player.current.duration
176
real = long(range.get_value() * duration / 100)
177
APP.player.seek(real)
178
APP.player.current.submitted = True
179
APP.emit('seek', real)
182
def change_track(self, combo):
184
Called when the user uses the title combo to pick a new song
186
iter = self.title_box.get_active_iter()
188
song = self.model.get_value(iter, 1)
190
APP.player.play_track(song)
192
def setup_title_box(self):
194
Populates the title box and selects the currently playing track
196
The combobox will be populated with all the tracks in the current
197
playlist, UNLESS there are more than 50 songs in the playlist. In
198
that case, only the current song and the next 50 upcoming tracks
201
blank = gtk.ListStore(str, object)
202
self.title_box.set_model(blank)
204
count = 0; select = -1
205
current = APP.player.current
210
current = APP.songs[0]
212
# if there are more than 50 songs in the current playlist, then only
213
# display the next 50 tracks
214
if len(APP.songs) > 50:
217
self.model.append([current.title, current])
222
next = APP.tracks.get_next_track(next)
224
self.model.append([next.title, next])
226
if count >= 50: break
228
# otherwise, display all songs in the current playlist
230
for song in APP.songs:
231
if song == current and APP.player.current:
233
self.model.append([song.title, song])
236
self.title_box.set_model(self.model)
237
self.title_box.disconnect(self.title_id)
238
if select > -1: self.title_box.set_active(select)
239
self.title_id = self.title_box.connect('changed',
241
self.title_box.set_sensitive(len(self.model) > 0)
243
def on_move(self, *e):
245
Saves the position of the minimode window if it is moved
247
(x, y) = self.get_position()
248
settings = APP.settings
249
settings.set_int('x', x, plugin=plugins.name(__file__))
250
settings.set_int('y', y, plugin=plugins.name(__file__))
252
def show_window(self):
254
Gets the last position from the settings, and then
255
displays the mimimode window
264
settings = APP.settings
265
x = settings.get_int("x", plugin=plugins.name(__file__),
267
y = settings.get_int("y", plugin=plugins.name(__file__),
270
self.setup_title_box()
273
if APP.settings.get_boolean('on_top', plugin=plugins.name(__file__),
275
self.set_keep_above(True)
277
self.set_keep_above(False)
279
if APP.settings.get_boolean('no_taskbar',
280
plugin=plugins.name(__file__), default=False):
281
self.set_property('skip-taskbar-hint', True)
283
self.set_property('skip-taskbar-hint', False)
285
if APP.settings.get_boolean('decoration',
286
plugin=plugins.name(__file__), default=False):
287
self.set_decorated(True)
289
self.set_decorated(False)
291
def on_prev(self, button):
293
Called when the user presses the previous button
296
APP.player.previous()
299
def on_play(self, button):
301
Called when the user clicks the play button
304
APP.player.toggle_pause()
307
def on_stop(self, button=None):
309
Called when the user clicks the stop button
312
if button: APP.player.stop(True)
314
self.play.set_image(APP.get_play_image(gtk.ICON_SIZE_MENU))
315
self.setup_title_box()
316
self.set_title(APP.window.get_title())
318
def on_next(self, button):
320
Called when the user clicks the next button
326
def create_button(self, stock_id, func, tip):
328
Creates a little button
330
button = gtk.Button()
331
button.connect('clicked', func)
333
image.set_from_stock(stock_id, gtk.ICON_SIZE_MENU)
334
button.set_image(image)
335
button.set_size_request(26, 26)
336
self.tips.set_tip(button, tip)
340
def pause_toggled(self):
342
Called when pause is toggled
345
track = APP.player.current
347
self.play.set_image(APP.get_play_image(gtk.ICON_SIZE_MENU))
349
if APP.player.is_paused():
350
self.play.set_image(APP.get_play_image(gtk.ICON_SIZE_MENU))
352
self.play.set_image(APP.get_pause_image(gtk.ICON_SIZE_MENU))
353
self.set_title(APP.window.get_title())
355
def timeout_cb(self):
356
text = APP.new_progressbar.get_text()
360
self.pos_label.set_label(text)
361
self.seeker.set_value(APP.new_progressbar.get_fraction() * 100)
365
def pause_toggled(exaile, track):
366
PLUGIN.pause_toggled()
368
def play_track(exaile, track):
369
PLUGIN.pause_toggled()
370
PLUGIN.setup_title_box()
372
def stop_track(exaile, track):
375
def toggle_hide(*args):
376
if not MM_ACTIVE: return False
378
if PLUGIN.get_property("visible"):
380
else: PLUGIN.show_window()
384
def tray_toggled(app, enabled):
386
CONS.connect(app.tray_icon, 'toggle-hide', toggle_hide)
388
CONS.disconnect_object(app.tray_icon)
390
def pass_func(*args):
392
if PLUGIN.get_property("visible"):
399
global TIMER_ID, PLUGIN, ACCEL_GROUP, MENU_ITEM
401
PLUGIN = MiniWindow()
402
TIMER_ID = gobject.timeout_add(1000, PLUGIN.timeout_cb)
403
ACCEL_GROUP = gtk.AccelGroup()
404
key, mod = gtk.accelerator_parse("<Control><Alt>M")
405
ACCEL_GROUP.connect_group(key, mod, gtk.ACCEL_VISIBLE, pass_func)
407
APP.window.add_accel_group(ACCEL_GROUP)
408
MENU_ITEM = gtk.MenuItem("Mini Mode")
409
MENU_ITEM.connect('activate', toggle_minimode)
410
MENU_ITEM.add_accelerator('activate', ACCEL_GROUP, key, mod,
412
APP.view_menu.get_submenu().append(MENU_ITEM)
414
PLUGIN.add_accel_group(ACCEL_GROUP)
415
CONS.connect(APP.player, 'play-track', play_track)
416
CONS.connect(APP.player, 'stop-track', stop_track)
417
CONS.connect(APP.player, 'pause-toggled', pause_toggled)
420
CONS.connect(APP.tray_icon, 'toggle-hide', toggle_hide)
421
CONS.connect(APP, 'tray-icon-toggled', tray_toggled)
425
global PLUGIN, MENU_ITEM, ACCEL_GROUP, MENU_ITEM, TIMER_ID
427
CONS.disconnect_all()
430
gobject.source_remove(TIMER_ID)
438
APP.view_menu.get_submenu().remove(MENU_ITEM)
442
APP.window.remove_accel_group(ACCEL_GROUP)