# -*- coding: utf-8 -*- # # This file is part of the Rhythmbox Desktop Art plug-in # # Copyright © 2008 Mathias Nedrebø < mathias at nedrebo dot org > # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. from __future__ import division import rb import gtk, gtk.glade, gconf import gobject import sys, os import locale, datetime, time icon = 'appointment-soon' gconf_plugin_path = '/apps/rhythmbox/plugins/alarm-clock/' fade_steps = 50 ui_str = ''' ''' class AlarmPlugin(rb.Plugin): def __init__ (self): rb.Plugin.__init__ (self) self.timeout_ids = [] self.onoff_times = {'on': None, 'off': None} def activate (self, shell): data = dict() manager = shell.get_player().get_property('ui-manager') action = gtk.Action('AlarmClockDialog', _('_Alarm Clock'), _('Activate the alarm clock'), None); action.connect('activate', self.show_conf_dialog) data['action_group'] = gtk.ActionGroup('AlarmClockActions') data['action_group'].add_action(action) manager.insert_action_group(data['action_group'], 0) data['ui_id'] = manager.add_ui_from_string(ui_str) manager.ensure_update() shell.set_data('AlarmClockInfo', data) # Set menu icon manager.get_widget('/MenuBar/ToolsMenu/ToolsOps_1/AlarmClockMenuItem').set_image(gtk.image_new_from_icon_name(icon, gtk.ICON_SIZE_MENU)) # Create hidden button in tool bar self.toolbar = manager.get_widget('/ToolBar/') self.alarm_clock_button = gtk.ToolButton() self.alarm_clock_button.connect('clicked', self.show_conf_dialog) self.alarm_clock_button.set_icon_name(icon) self.alarm_clock_button.set_tooltip_text("Stop or restart the alarm clock") self.alarm_clock_button.set_is_important(True) self.toolbar.insert(self.alarm_clock_button, self.toolbar.get_n_items() - 1) self.separator = gtk.SeparatorToolItem() self.separator.set_expand(True) self.separator.set_draw(False) self.separator.show() self.toolbar.insert(self.separator, self.toolbar.get_n_items() - 1) self.player = shell.get_player() self.shell = shell def deactivate(self, shell): self.stop_timer() self.toolbar.remove(self.separator) self.toolbar.remove(self.alarm_clock_button) data = shell.get_data('AlarmClockInfo') manager = shell.get_player().get_property('ui-manager') manager.remove_ui(data['ui_id']) del self.player del self.separator del self.alarm_clock_button del self.toolbar del self.shell def timer(self, fade_time): if self.onoff_time['off'] and self.onoff_time['on']: first = self.onoff_time['off'] < self.onoff_time['on'] and 'off' or 'on' else: first = self.onoff_time['off'] and 'off' or 'on' self.alarm_clock_button.show() delta = self.onoff_time[first] - datetime.datetime.now() minutes = int(delta.days * 24 * 60 + delta.seconds / 60) sys.stderr.write(str()) if minutes >= 1: self.alarm_clock_button.set_label('Music %s in %d %s' % ( first == 'on' and 'starts' or 'stops', minutes, minutes == 1 and 'minute' or 'minutes')) elif delta.seconds >= 1 and delta.days == 0: self.alarm_clock_button.set_label('Music %s in %d %s' % ( first == 'on' and 'starts' or 'stops', delta.seconds, delta.seconds == 1 and'second' or 'seconds')) else: self.alarm_clock_button.set_label('Music is %s' % (first == 'on' and 'starting' or 'stoping')) if minutes >= 2: self.timeout_ids[0] = gobject.timeout_add(60000, self.timer, fade_time) elif delta.seconds >= 1 and delta.days == 0: self.timeout_ids[0] = gobject.timeout_add(1000, self.timer, fade_time) else: self.onoff_time[first] = None if self.onoff_time['off'] or self.onoff_time['on']: self.timeout_ids[0] = gobject.timeout_add(1000 * fade_time + 200, self.timer, fade_time) stop_timer = False else: stop_timer = True if first == 'on': self.timeout_ids.append(gobject.timeout_add(0, self.start_playing, stop_timer, fade_time)) else: self.timeout_ids.append(gobject.timeout_add(0, self.stop_playing, stop_timer, fade_time)) return False def stop_timer(self): self.alarm_clock_button.hide() for tid in self.timeout_ids: gobject.source_remove(tid) self.timeout_ids = [] def stop_playing(self, stop_timer, fade_time, rec = False): while not rec and self.fade_lock: gtk.main_iteration(False) if self.player.get_playing(): self.fade_lock = True if not rec: self.volume = self.player.get_volume() fade_step = self.volume / fade_steps step_time = fade_time / fade_steps if self.player.get_volume() > 0: self.player.set_volume_relative(-fade_step) self.timeout_ids.append(gobject.timeout_add(int(step_time * 1000), self.stop_playing, stop_timer, fade_time, True)) stop_timer = False else: self.player.playpause() self.player.set_volume(self.volume) self.fade_lock = False if(stop_timer): self.stop_timer() def start_playing(self, stop_timer, fade_time, rec = False): while not rec and self.fade_lock: gtk.main_iteration(False) if not self.player.get_playing() or rec: self.fade_lock = True if not rec: if self.start_volume: self.volume = self.start_volume else: self.volume = self.player.get_volume() self.player.set_volume(0) if self.start_source: self.player.set_playing_source(self.start_source) self.player.playpause() fade_step = self.volume / fade_steps step_time = fade_time / fade_steps if self.player.get_volume() < self.volume: self.player.set_volume_relative(fade_step) self.timeout_ids.append(gobject.timeout_add(int(step_time * 1000), self.start_playing, stop_timer, fade_time, True)) stop_timer = False else: self.fade_lock = False # This doesn't work for some reason # # elif self.start_source: # self.player.stop() # self.player.set_playing_source(self.start_source) # self.player.play() # if self.start_volume: # self.player.set_volume(self.start_volume) # if(stop_timer): self.stop_timer() def show_conf_dialog(self, action): self.n_ticks = 0 gladexml = gtk.glade.XML(self.find_file('alarmclock.glade')) widgets = {} widgets['main_dialog'] = gladexml.get_widget('main_dialog') widgets['abort_dialog'] = gladexml.get_widget('abort_dialog') widgets['abort_label1'] = gladexml.get_widget('abort_label1') widgets['abort_label2'] = gladexml.get_widget('abort_label2') widgets['error_date_time_dialog'] = gladexml.get_widget('error_date_time_dialog') widgets['error_date_format_dialog'] = gladexml.get_widget('error_date_format_dialog') widgets['ok_button'] = gladexml.get_widget('ok_button') widgets['source_treeview'] = gladexml.get_widget('source_treeview') # Fix proper Dialog placements widgets['main_dialog'].set_transient_for(self.shell.props.window) widgets['abort_dialog'].set_transient_for(self.shell.props.window) # Set icon dicon = gtk.image_new_from_icon_name(icon, gtk.ICON_SIZE_DIALOG) gladexml.get_widget('main_vbox').pack_end(dicon) dicon.show() # Set up source selection widgets['source_treeview'].set_property('show-expanders', 'true') column = gtk.TreeViewColumn('Main colum') renderer = gtk.CellRendererText() column.pack_end(renderer, False) column.add_attribute(renderer, 'text', 2) renderer = gtk.CellRendererPixbuf() column.pack_end(renderer, False) column.add_attribute(renderer, 'pixbuf', 1) widgets['source_treeview'].append_column(column) widgets['source_treeview'].set_expander_column(column) widgets['source_treeview'].set_headers_visible(False) widgets['source_treeview'].set_model(self.shell.get_property('sourcelist_model')) widgets['source_treeview'].expand_all() if self.timeout_ids: start = self.onoff_time['on'] stop = self.onoff_time['off'] # Sort labels by time if (start and stop) and (start > stop): l1, l2 = 2, 1 else: l1, l2 = 1, 2 if start: widgets['abort_label%d' % l1].set_text('Music starts: %s' % start.strftime(locale.nl_langinfo(locale.D_T_FMT))) widgets['abort_label%d' % l1].show() else: widgets['abort_label%d' % l1].hide() if stop: widgets['abort_label%d' % l2].set_text('Music stops: %s' % stop.strftime(locale.nl_langinfo(locale.D_T_FMT))) widgets['abort_label%d' % l2].show() else: widgets['abort_label%d' % l2].hide() abort_response = widgets['abort_dialog'].run() widgets['abort_dialog'].destroy() if abort_response == 1: return if abort_response == 2: self.stop_timer() return def show_button_menu_cb(button, widgets, type): if button.get_active(): button_alloc = button.get_allocation() window_pos = button.get_toplevel().get_position() widgets['calendar_popup'].move(window_pos[0] + button_alloc.x, window_pos[1] + button_alloc.y + button_alloc.height) widgets['calendar_popup'].show() now = datetime.datetime.now() widgets['calendar'].select_month(now.month - 1, now.year) widgets['calendar'].select_day(now.day) self.calendar_type = type else: widgets['calendar_popup'].hide() def hide_button_menu_cb(window, event, widgets): #bad bad hack gobject.timeout_add(150, widgets['day_%s_button' % self.calendar_type].set_active, False) def day_selected_cb(widget, widgets, day = None): widgets['calendar_popup'].hide() if day: date = datetime.datetime.now() if day == 2: date += datetime.timedelta(1) else: y,m,d = widget.get_date() date = datetime.datetime(y, m + 1, d) widgets['music_%s_date' % self.calendar_type].set_text(str(date.strftime(locale.nl_langinfo(locale.D_FMT)))) def widget_hider_cb(button, widget1, widget2 = None, widget3 = None): widget1.set_property('visible', button.get_active()) if widget2: widget2.set_property('visible', not button.get_active()) if widget3: self.n_ticks = max(0, self.n_ticks + (button.get_active() and 1 or -1)) widget3.set_property('sensitive', self.n_ticks > 0) def widget_sensitive_cb(button, widget): widget.set_sensitive(button.get_active()) def gconf_path(key): return '%s%s' % (gconf_plugin_path, key) def load_dialog_values(widgets, constants): gc = gconf.client_get_default() for onoff in 'on', 'off': widgets['music_%s_checkbutton' % onoff].set_active(gc.get_bool(gconf_path('music_%s_checkbutton' % onoff))) widgets['music_%s_combobox' % onoff].set_active(gc.get_int(gconf_path('music_%s_combobox' % onoff))) widgets['music_%s_hours' % onoff].set_value(gc.get_float(gconf_path('music_%s_hours' % onoff))) widgets['music_%s_minutes' % onoff].set_value(gc.get_float(gconf_path('music_%s_minutes' % onoff))) date = gc.get_string(gconf_path('music_%s_date' % onoff)) now = datetime.datetime.now() if date: try: hour = gc.get_float(gconf_path('music_%s_hour' % onoff)) minute = gc.get_float(gconf_path('music_%s_minute' % onoff)) # Fix so we work with Python < 2.5 as well a = time.strptime(date, locale.nl_langinfo(locale.D_FMT)) dt = datetime.datetime(a[0],a[1],a[2],a[3],a[4]) dt += datetime.timedelta(0, (hour * 60 + minute) * 60) if dt < now: # "Smart" date adjusting delta = datetime.timedelta((now - dt).days + 1) dt += delta date = dt.strftime(locale.nl_langinfo(locale.D_FMT)) except ValueError: date = None if not date: date = str(now.strftime(locale.nl_langinfo(locale.D_FMT))) hour = now.hour + 1 minute = now.minute widgets['music_%s_date' % onoff].set_text(date) widgets['music_%s_date' % onoff].set_width_chars(len(widgets['music_%s_date' % onoff].get_text())) widgets['music_%s_hour' % onoff].set_value(hour) widgets['music_%s_minute' % onoff].set_value(minute) widgets['volume_expander'].set_expanded(gc.get_bool(gconf_path('volume_expander'))) widgets['volume_adjust_radiobutton'].set_active(gc.get_bool(gconf_path('volume_adjust_radiobutton'))) widgets['source_expander'].set_expanded(gc.get_bool(gconf_path('source_expander'))) widgets['source_choose_radiobutton'].set_active(gc.get_bool(gconf_path('source_choose_radiobutton'))) widgets['volume_button'].set_value(gc.get_float(gconf_path('volume_button'))) constants['fade_time'] = gc.get_int(gconf_path('fade_time')) or 45 constants['source_path'] = eval(gc.get_string(gconf_path('source_path')) or '(0,)') widgets['source_treeview'].get_selection().select_path(constants['source_path']) def save_dialog_values(widgets, constants): gc = gconf.client_get_default() for onoff in 'on', 'off': gc.set_bool(gconf_path('music_%s_checkbutton' % onoff), widgets['music_%s_checkbutton' % onoff].get_active()) gc.set_int(gconf_path('music_%s_combobox' % onoff), widgets['music_%s_combobox' % onoff].get_active()) gc.set_float(gconf_path('music_%s_hours' % onoff), widgets['music_%s_hours' % onoff].get_value()) gc.set_float(gconf_path('music_%s_minutes' % onoff), widgets['music_%s_minutes' % onoff].get_value()) gc.set_string(gconf_path('music_%s_date' % onoff), widgets['music_%s_date' % onoff].get_text()) gc.set_float(gconf_path('music_%s_hour' % onoff), widgets['music_%s_hour' % onoff].get_value()) gc.set_float(gconf_path('music_%s_minute' % onoff), widgets['music_%s_minute' % onoff].get_value()) gc.set_bool(gconf_path('volume_expander'), widgets['volume_expander'].get_expanded()) gc.set_bool(gconf_path('volume_adjust_radiobutton'), widgets['volume_adjust_radiobutton'].get_active()) gc.set_float(gconf_path('volume_button'), widgets['volume_button'].get_value()) gc.set_bool(gconf_path('source_expander'), widgets['source_expander'].get_expanded()) gc.set_bool(gconf_path('source_choose_radiobutton'), widgets['source_choose_radiobutton'].get_active()) gc.set_int(gconf_path('fade_time'), constants['fade_time']) if constants['source_path']: gc.set_string(gconf_path('source_path'), str(constants['source_path'])) # Set up callbacks for widgets widgets['volume_expander'] = gladexml.get_widget('volume_expander') widgets['volume_button'] = gladexml.get_widget('volume_button') widgets['source_expander'] = gladexml.get_widget('source_expander') widgets['volume_adjust_radiobutton'] = gladexml.get_widget('volume_adjust_radiobutton') widgets['source_choose_radiobutton'] = gladexml.get_widget('source_choose_radiobutton') widgets['calendar_popup'] = gladexml.get_widget('calendar_popup') widgets['calendar_today_button'] = gladexml.get_widget('calendar_today_button') widgets['calendar_tomorrow_button'] = gladexml.get_widget('calendar_tomorrow_button') widgets['calendar'] = gladexml.get_widget('calendar') for onoff in 'on', 'off': widgets['music_%s_area' % onoff] = gladexml.get_widget('music_%s_area' % onoff) widgets['music_%s_checkbutton' % onoff] = gladexml.get_widget('music_%s_checkbutton' % onoff) widgets['music_%s_checkbutton' % onoff].connect('toggled', widget_hider_cb, widgets['music_%s_area' % onoff], None, widgets['ok_button']) widgets['music_%s_interval_box' % onoff] = gladexml.get_widget('music_%s_interval_box' % onoff) widgets['music_%s_time_box' % onoff] = gladexml.get_widget('music_%s_time_box' % onoff) widgets['music_%s_combobox' % onoff] = gladexml.get_widget('music_%s_combobox' % onoff) widgets['music_%s_combobox' % onoff].connect('changed', widget_hider_cb, widgets['music_%s_time_box' % onoff], widgets['music_%s_interval_box' % onoff]) widgets['music_%s_hours' % onoff] = gladexml.get_widget('music_%s_hours' % onoff) widgets['music_%s_minutes' % onoff] = gladexml.get_widget('music_%s_minutes' % onoff) widgets['music_%s_date' % onoff] = gladexml.get_widget('music_%s_date' % onoff) widgets['music_%s_hour' % onoff] = gladexml.get_widget('music_%s_hour' % onoff) widgets['music_%s_minute' % onoff] = gladexml.get_widget('music_%s_minute' % onoff) widgets['day_%s_button' % onoff] = gladexml.get_widget('%s_day_button' % onoff) widgets['day_%s_button' % onoff].connect('toggled', show_button_menu_cb, widgets, onoff) widgets['volume_adjust_radiobutton'].connect('toggled', widget_sensitive_cb, widgets['volume_button']) widgets['source_choose_radiobutton'].connect('toggled', widget_sensitive_cb, widgets['source_treeview']) widgets['calendar_popup'].connect('focus-out-event', hide_button_menu_cb, widgets) widgets['calendar'].connect('day_selected_double_click', day_selected_cb, widgets) widgets['calendar_today_button'].connect('clicked', day_selected_cb, widgets, 1) widgets['calendar_tomorrow_button'].connect('clicked', day_selected_cb, widgets, 2) constants = {} load_dialog_values(widgets, constants) valid_date_time_input = False valid_date_format_input = False while (not valid_date_time_input or not valid_date_format_input): onoff_time = {'on': None, 'off': None} valid_date_format_input = True if widgets['main_dialog'].run() == gtk.RESPONSE_OK: now = datetime.datetime.now() for onoff in 'on', 'off': if widgets['music_%s_checkbutton'% onoff].get_active(): if widgets['music_%s_combobox'% onoff].get_active(): date = widgets['music_%s_date' % onoff].get_text() try: a = time.strptime(date, locale.nl_langinfo(locale.D_FMT)) except: valid_date_format_input = False break dt = datetime.datetime(a[0],a[1],a[2],a[3],a[4]) dt = dt.replace(hour = int(widgets['music_%s_hour' % onoff].get_value())) dt = dt.replace(minute = int(widgets['music_%s_minute' % onoff].get_value())) onoff_time[onoff] = dt else: hours = int(widgets['music_%s_hours' % onoff].get_value()) minutes = int(widgets['music_%s_minutes' % onoff].get_value()) time_delta = datetime.timedelta(0, (hours * 60 + minutes) * 60) onoff_time[onoff] = datetime.datetime.now() + time_delta if not valid_date_format_input: widgets['error_date_format_dialog'].run() widgets['error_date_format_dialog'].hide() else: if widgets['volume_adjust_radiobutton'].get_active(): self.start_volume = widgets['volume_button'].get_value() else: self.start_volume = None if widgets['source_choose_radiobutton'].get_active(): iter = widgets['source_treeview'].get_selection().get_selected()[1] else: iter = None self.start_source = iter and widgets['source_treeview'].get_model().get_value(iter, 3) constants['source_path'] = iter and widgets['source_treeview'].get_model().get_path(iter) # Check if the dates and times chosen is in the future valid_date_time_input = (onoff_time['on'] == None or onoff_time['on'] > now) and (onoff_time['off'] == None or onoff_time['off'] > now) if valid_date_time_input: self.stop_timer() self.fade_lock = False save_dialog_values(widgets, constants) self.onoff_time = onoff_time self.timeout_ids.append(gobject.timeout_add(0, self.timer, constants['fade_time'])) else: widgets['error_date_time_dialog'].run() widgets['error_date_time_dialog'].hide() else: valid_date_time_input = valid_date_format_input = True widgets['main_dialog'].destroy() widgets['error_date_time_dialog'].destroy() widgets['error_date_format_dialog'].destroy()