class Alarm(object): def __init__(self, parent): self.parent = parent self.tray_hints = {} self.parent = parent self.tray_hints = {} self.gconf_client = Configuration() self.start() def start(self): start_delay = self.gconf_client.get('startup_delay') print start_delay showStartup = self.gconf_client.get('show_startup_notification') print showStartup if showStartup: timeout_add(start_delay, self.show_pay_notification) timeout_add(start_delay + 12000, self.verify_due) interval = self.gconf_client.get('interval') print interval if interval: timeout_add(interval, self.timer) def notification(self, title, body): notify = NotifyMessage(self.parent) notify.title = title notify.body = body notify.set_timeout(20) notify.set_default_action(self.__cb_launch_gui) notify.hints = self.tray_hints return notify def show_pay_notification(self): print "Got here 3" today = datetime.datetime.today() limit = self.gconf_client.get('notification_days_limit') print limit limit = datetime.timedelta(days=limit) end = today + limit if limit: records = self.parent.actions.get_interval_bills(end=end, paid=False) #'dueDate <= %s AND paid = 0' % (today + limit)) else: print "No limit" records = self.parent.actions.get_bills(paid=False) msg = ngettext('You have %s outstanding bill to pay!', 'You have %s outstanding bills to pay!', len(records)) % len(records) if msg and records: bubble = self.notification(common.APPNAME, msg) bubble.add_action("view", _("Show BillReminder"), self.__cb_launch_gui, None) #bubble.add_action("close", _("Cancel"), None) bubble.show() return False def verify_due(self, sum=0): print "Got here 2" showDueAlarm = self.gconf_client.get('show_due_alarm') print showDueAlarm if not showDueAlarm: print "Do not show due alarm" return today = datetime.datetime.today() if sum > 0: records = self.parent.actions.get_interval_bills( today, today, False) else: records = self.parent.actions.get_interval_bills(end=today, paid=False) i = 1 use_dialog = self.gconf_client.get('use_alert_dialog') print use_dialog # TODO: use only one dialog for all bills, if use_dialog == True for bill in records: if sum > 0: # date format string dtformat = locale.nl_langinfo(locale.D_FMT) # date from record in timestamp format dtstamp = bill.dueDate # record dictionary recDict = { 'bill': "<b>\"%s\"</b>" % bill.payee, 'day': "<b>\"%s\"</b>" % dtstamp.strftime(dtformat).encode('ASCII') } # TODO: calculate days timeout_add( i * 12000, self.show_bill_notification, bill, _("The bill %(bill)s will be due at %(day)s.") % recDict, use_dialog) else: timeout_add(i * 12000, self.show_bill_notification, bill, None, use_dialog) i += 1 def show_bill_notification(self, bill=None, msg=None, alert=False, timeout=None): if msg is None: msg = _('The bill %s is due.') % "<b>\"%s\"</b>" % bill.payee if not self.parent.actions.get_bills(id=bill.id)[0].paid: if alert: alert = Message().ShowBillInfo( text=msg, title=_("BillReminder Notifier")) if alert == RESPONSE_YES: self.__cb_mark_as_paid(None, (bill, )) elif alert == RESPONSE_NO: self.__cb_edit_bill(None, (bill, )) else: bubble = self.notification(common.APPNAME, msg) bubble.add_action("paid", _("Mark as paid"), self.__cb_mark_as_paid, bill) bubble.add_action("edit", _("Edit"), self.__cb_edit_bill, bill) #bubble.add_action("close", _("Cancel"), None) if timeout: bubble.set_timeout(timeout) bubble.show() return False def __cb_launch_gui(self, *arg): # If client is not running, launch it # Send DBus 'show_main_window' signal self.parent.dbus_server.show_main_window() if not self.parent.client_pid or \ not verify_pid(self.parent.client_pid): gui = Popen('billreminder', shell=True) self.parent.client_pid = gui.pid def __cb_mark_as_paid(self, *arg): record = arg[1][0] if record: record.paid = True try: # Edit bill to database self.parent.dbus_server.edit_bill(record) except Exception, e: print "Error #1" print str(e)
class AddDialog(object): """ Class used to generate dialog to allow user to enter/edit records. """ def __init__(self, title=None, parent=None, record=None, selectedDate=None): self.ui = gtk.Builder() self.ui.add_from_file( os.path.join(common.DEFAULT_CFG_PATH, "add_bill.ui")) self.window = self.ui.get_object("add_bill_dialog") self.window.set_icon_from_file(common.APP_ICON) if parent: self.window.set_transient_for(parent) # If we have a selected date, then set calendar to use it if not selectedDate: selectedDate = datetime.date.today() self.selectedDate = selectedDate self.gconf_client = Configuration() # Private copy of any record passed self.currentrecord = record self.alarm = [None, None] # TODO: This needs to be run BEFORE connecting the widgets self._set_currency() # Set up the UI self._initialize_dialog_widgets() self._populate_widgets() self.category_index_before = 0 self.ui.connect_signals(self) def _set_currency(self): self.decimal_sep = locale.localeconv()['mon_decimal_point'] self.thousands_sep = locale.localeconv()['mon_thousands_sep'] self.allowed_digts = [self.decimal_sep, self.thousands_sep] self.allowed_digts += [str(i) for i in range(10)] def _initialize_dialog_widgets(self): self.frequency = self.ui.get_object("frequency") self.dueDate = DatePicker() self.ui.get_object("due_date_box").add(self.dueDate) self.dueDate.connect('date_changed', self._on_datepicker_date_changed) self.endDate = DatePicker() self.ui.get_object("end_date_box").add(self.endDate) self.endDate.connect('date_changed', self._on_datepicker_date_changed) self.payee = self.ui.get_object("payee") self.payeecompletion = gtk.EntryCompletion() self.payee.child.set_completion(self.payeecompletion) self.amount = self.ui.get_object("amount") self.category = self.ui.get_object("category") px = gtk.CellRendererPixbuf() txt = gtk.CellRendererText() self.category.pack_start(px, False) self.category.pack_start(txt, False) self.category.add_attribute(px, "pixbuf", 0) self.category.add_attribute(txt, "text", 1) self.category.set_row_separator_func(self._determine_separator) self.categorybutton = self.ui.get_object("edit_categories") self.notes = self.ui.get_object("notes") self.txtbuffer = self.notes.get_buffer() self.alarmbutton = DateButton(self.window) self.alarmbutton.set_tooltip_text(_("Select Date and Time")) self.ui.get_object("alarm_button_box").add(self.alarmbutton) self.ui.get_object("alarm_label").set_mnemonic_widget(self.alarmbutton) self.window.show_all() def _populate_widgets(self): """ Populate dialog widgets so they can be used. """ self._populate_frequency() self._populate_payee() # Populate combobox with payee from db self._populate_category() # Populate combobox with category from db # If a record was passed, we're in edit mode if self.currentrecord: self._populate_widgets_with_record() #in edit mode we must disable repetition self.frequency.set_sensitive(False) self.endDate.set_sensitive(False) else: self.dueDate.set_date(self.selectedDate) self.endDate.set_date(self.selectedDate) # Use alarm values from preferences showalarm = self.gconf_client.get('show_alarm') atime = self.gconf_client.get('show_alarm_at_time') adays = self.gconf_client.get('show_alarm_before_days') if showalarm: alarmDate = scheduler.get_alarm_timestamp( adays, atime, self.selectedDate) self.alarmbutton.set_date(alarmDate) def _determine_separator(self, model, iter, data=None): return model.get_value(iter, 1) == "---" def _populate_widgets_with_record(self): # Format the amount field if self.currentrecord.amount: self.amount.set_text( utils.float_to_currency(self.currentrecord.amount)) else: self.amount.set_text("") # Format the dueDate field dt = self.currentrecord.dueDate self.dueDate.set_date(dt) utils.select_combo_text(self.payee, self.currentrecord.payee) if self.currentrecord.category: actions = Actions() cat_name = self.currentrecord.category.name records = actions.get_categories(name=cat_name) if records: categoryname = records[0].name utils.select_combo_text(self.category, categoryname, 1) else: self.category.set_active(0) if self.currentrecord.notes: self.txtbuffer.set_text(self.currentrecord.notes) #self.chkPaid.set_active(self.currentrecord.Paid) if self.currentrecord.alarmDate: self.alarmbutton.set_date(self.currentrecord.alarmDate) def _populate_payee(self): """ Populates combobox with existing payees """ # Connects to the database actions = Actions() # List of payees from database payees = [] records = actions.get_bills() for rec in records: if rec.payee not in payees: payees.append(rec.payee) store = gtk.ListStore(gobject.TYPE_STRING) for payee in payees: store.append([payee]) self.payee.set_model(store) self.payeecompletion.set_model(store) self.payee.set_text_column(0) self.payeecompletion.set_text_column(0) self.payeeEntry = self.payee.child self.selectedText = '' def _get_payee(self): """ Extracts information typed into comboboxentry """ if self.payee.get_active_iter() is not None: model = self.payee.get_model() iteration = self.payee.get_active_iter() if iteration: return model.get_value(iteration, 0) else: return self.payeeEntry.get_text() def _populate_frequency(self): """ Populates combobox with allowable frequency. """ store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT) self.frequency.set_model(store) cell = gtk.CellRendererText() self.frequency.pack_start(cell, True) self.frequency.add_attribute(cell, 'text', 0) for i, frequency in enumerate( [scheduler.SC_ONCE, scheduler.SC_MONTHLY, scheduler.SC_WEEKLY]): store.append([frequency, i]) # Set SC_ONCE as default self.frequency.set_active(0) self.on_frequency_changed(self.frequency) def _populate_category(self, categoryname=None): """ Populates combobox with existing categories """ # Connects to the database actions = Actions() # List of categories from database categories = [] records = actions.get_categories() ret = 0 empty_color = create_pixbuf() for rec in records: #if [rec['categoryname'], rec['id']] not in categories: #TODO: Better put color creation in a function color = rec.color and rec.color or '#000' categories.append( [create_pixbuf(color=color), rec.name, int(rec.id)]) if categoryname and categoryname == rec.name: ret = len(categories) + 1 store = gtk.ListStore(gtk.gdk.Pixbuf, str, int) self.category.set_model(store) store.append([empty_color, _("None"), 0]) store.append([None, "---", -1]) for category in categories: store.append(category) store.append([None, "---", -1]) store.append([empty_color, _("New Category"), -2]) self.category.set_active(ret) return ret def _get_category(self): """ Extracts information typed into comboboxentry """ actions = Actions() if self.category.get_active_iter() is not None: model = self.category.get_model() iteration = self.category.get_active_iter() if iteration: name = model.get_value(iteration, 1) else: name = None if not name or name == _("None"): return None records = actions.get_categories(name=name) if records: return records[0] else: return None def get_record(self): frequency = self.frequency.get_active_text() # Extracts the date off the calendar widget # Create datetime object selectedDate = self.dueDate.get_date() # End date if frequency != scheduler.SC_ONCE: endDate = self.endDate.get_date() # Notify user that the endDate is set in the past if endDate < selectedDate: endDate = selectedDate message = utils.Message() text = _( "The end date is set to a date prior to the start date. Setting it to match the start date." ) title = _("Date set in the past") message.ShowInfo(text=text, parentWindow=self.window, title=title) else: endDate = None #buffer = self.txtNotes.get_buffer() startiter, enditer = self.txtbuffer.get_bounds() sbuffer = self.txtbuffer.get_text(startiter, enditer) # Gets the payee payee = self._get_payee() # Gets the category category = self._get_category() # Gets the alarm date alarm = self.alarmbutton.get_date() or None # Validate form if not payee.strip(): return None if self.amount.get_text().strip(): amount = utils.currency_to_float(self.amount.get_text()) else: amount = None if self.currentrecord is None: # Verify how many bills will be inserted # this will only work for new bills records = [] # Figures out how many times we're repeating this bill days = scheduler.get_schedule_date(frequency, selectedDate, endDate) for day in days: if alarm: alarm = self.__get_alarm_date(day) rec = Bill(payee=payee, amount=amount, dueDate=day, alarmDate=alarm, notes=sbuffer, repeats=False) # Bill repeats... if len(days) > 1: rec.repeats = True # ... and has a category. if category: rec.category = category records.append(rec) return records else: # Edit existing bill self.currentrecord.payee = payee self.currentrecord.dueDate = selectedDate self.currentrecord.amount = amount self.currentrecord.notes = sbuffer self.currentrecord.alarmDate = alarm if category: self.currentrecord.category = category #return the bill return [self.currentrecord] def on_frequency_changed(self, widget): startDate = self.dueDate.get_date() endDate = self.endDate.get_date() frequency = widget.get_active_text() if frequency == scheduler.SC_ONCE: self.endDate.set_sensitive(False) else: self.endDate.set_sensitive(True) if startDate > endDate: self.endDate.set_date(self.dueDate.get_date()) def on_edit_categories_clicked(self, button, new=False): category = None # if new == True, a simpler categories dialog pops up self.window.category = self.category categories = CategoriesDialog(parent=self.window, new=new) ret = categories.run() if ret == gtk.RESPONSE_OK: #TODO: We should handle the saving in the dialog itself. # the category hasn't been saved yet... so save it. if new: categories._on_savebutton_clicked(None) category = categories.currentrecord categories.destroy() # Always re-populate the categories dropdown widget, regardless if # newer category was added. If something was returned, select it. if category: self._populate_category(category.name) else: self._populate_category() return ret def on_category_changed(self, combobox): index = self.category.get_active() model = self.category.get_model() if index == len(model) - 1: self.category.set_active(self.category_index_before) self.on_edit_categories_clicked(combobox, True) self.category_index_before = index def on_amount_insert_text(self, entry, string, len, position): for char in string: if char not in self.allowed_digts: print "Invalid Character: %s" % char entry.emit_stop_by_name("insert-text") gtk.gdk.beep() return def on_save_clicked(self, widget): message = utils.Message() startDate = self.dueDate.get_date() endDate = self.endDate.get_date() if not self._get_payee().strip() and \ not self.amount.get_text().strip(): message.ShowError(_("\"%s\" and \"%s\" are required fields.") \ % (_("Payee"), _("Amount")), self.window) self.payee.grab_focus() elif not self._get_payee().strip(): message.ShowError( _("\"%s\" is required field.") % _("Payee"), self.window) self.payee.grab_focus() elif not self.amount.get_text().strip(): message.ShowError( _("\"%s\" is required field.") % _("Amount"), self.window) self.amount.grab_focus() elif self.endDate.get_sensitive() and startDate > endDate: message.ShowError( _("The end date is set to a date prior to the start date."), self.window) else: self.window.response(gtk.RESPONSE_ACCEPT) def _on_datepicker_date_changed(self, widget, args): startDate = self.dueDate.get_date() endDate = self.endDate.get_date() if widget == self.dueDate: if self.endDate.get_sensitive() and startDate > endDate: # Update endDate to be equal to dueDate self.endDate.set_date(self.dueDate.get_date()) else: if self.endDate.get_sensitive(): if startDate > endDate: # Update endDate to be equal to dueDate self.endDate.set_date(self.dueDate.get_date()) if self.alarmbutton.get_date(): # Extracts the date off the datepicker widget alarmDate = self.__get_alarm_date(self.dueDate.get_date()) self.alarmbutton.set_date(alarmDate) def __get_alarm_date(self, date): # Use alarm values from preferences atime = self.gconf_client.get('show_alarm_at_time') adays = self.gconf_client.get('show_alarm_before_days') return scheduler.get_alarm_timestamp(adays, atime, date)
class MainDialog(gobject.GObject): search_text = "" _bullet_cache = {} __gsignals__ = { "bill-added": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), "bill-updated": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), "bill-removed": (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT,)), } def __init__(self): gobject.GObject.__init__(self) if exists(join(USER_CFG_PATH, CFG_NAME)): from lib.migrate_to_gconf import migrate migrate(join(USER_CFG_PATH, CFG_NAME)) self.gconf_client = Configuration() self.message = Message() # Connects to the database self.actions = Actions() self.ui = gtk.Builder() self.ui.add_from_file(os.path.join(DEFAULT_CFG_PATH, "main.ui")) self.window = self.ui.get_object("main_window") self.window.set_title("%s" % common.APPNAME) self.window.set_icon_from_file(common.APP_ICON) # ViewBill self.list = ViewBill() self.list.connect("cursor_changed", self._on_list_cursor_changed) self.list.connect("row_activated", self._on_list_row_activated) self.list.connect("button_press_event", self._on_list_button_press_event) self.ui.get_object("bill_box").add(self.list) # Toolbar self.toolbar = self.ui.get_object("toolbar") # Menubar self._populate_menubar() # Statusbar self.statusbar = Statusbar() self.ui.get_object("statusbar_box").add(self.statusbar) # Restore timeline zoom timeline_count = self.gconf_client.get("timeline_count") # Timeline self.timeline = Timeline(count=timeline_count, callback=self.on_timeline_cb) self.timeline.connect("range-changed", self._on_timeline_changed) self.timeline.connect("button-press-event", self._on_timeline_click) self.timeline.connect("cleared", self._on_timeline_cleared) self.ui.get_object("timeline_box").add(self.timeline) # Chart self.chart = ChartWidget() self.chart.set_border_width(10) self.ui.get_object("chart_box").add(self.chart) # Restore position and size of window width = self.gconf_client.get("window_width") height = self.gconf_client.get("window_height") x = self.gconf_client.get("window_position_x") y = self.gconf_client.get("window_position_y") if width and height: self.window.resize(width, height) if x and y: self.window.move(x, y) self.window.show_all() # Whether to display toolbar or not self.on_showToolbar_toggled(self.ui.get_object("showToolbar")) self.list.grab_focus() if self.gconf_client.get("start_in_tray"): self.window.hide() self.toggle_buttons() # Connects to the Daemon self.iface = None iface = get_dbus_interface(common.DBUS_INTERFACE, common.DBUS_PATH) if iface: iface.connect_to_signal("bill_edited", self.reloadTreeView) iface.connect_to_signal("bill_edited", self.reloadTimeline) iface.connect_to_signal("show_main_window", self.window.show) self.iface = iface gobject.timeout_add(2000, self._send_tray_hints) self.set_action_strings() self.ui.connect_signals(self) # populate treeview self.reloadTreeView() self.notify = NotifyIcon(self) # Integrate with Ubuntu Unity if UNITY: self.unity = UnityIntegration(self) def set_action_strings(self): # for some reason the actions strings do not get translated yet # so we define them here so they would be picked up by the pyfile scanner self.ui.get_object("newBill").set_label(_("_New")) self.ui.get_object("newBill").set_tooltip(_("Add new bill")) self.ui.get_object("editBill").set_label(_("_Edit")) self.ui.get_object("editBill").set_tooltip(_("Edit a bill")) self.ui.get_object("removeBill").set_label(_("_Delete")) self.ui.get_object("removeBill").set_tooltip(_("Delete selected bill")) self.ui.get_object("markPaid").set_label(_("P_aid")) self.ui.get_object("markPaid").set_tooltip(_("Mark as paid")) self.ui.get_object("markNotPaid").set_label(_("No_t Paid")) self.ui.get_object("markNotPaid").set_tooltip(_("Mark as not paid")) self.ui.get_object("showToolbar").set_label(_("_Show Toolbar")) self.ui.get_object("showToolbar").set_tooltip(_("Show the toolbar")) # Methods: UI def _send_tray_hints(self): self.iface.set_tray_hints(force_string(self.notify.get_hints())) gobject.timeout_add(60000, self._send_tray_hints) def get_window_visibility(self): return self.window.get_property("visible") def show_hide_window(self): if self.window.get_property("visible"): self.window.hide() else: self.window.show() def get_selected_record(self): """ Returns a bill object from the current selected record """ if len(self.list.listStore) > 0: model_ = self.list.get_model() if self.list.get_cursor()[0]: index = self.list.get_cursor()[0][0] else: index = 0 b_id = model_[index][0] records = self.actions.get_bills(id=b_id) self.currentrecord = records[0] else: self.currentrecord = None print "Current record is: %s" % self.currentrecord def populate_view(self, records): """ Populates the treeview control with the records passed """ # Reset list self.list.listStore.clear() if not records: return 0 # Loops through bills collection path = 0 for rec in records: # Format the record prior to adding it to treeview row = self.format_row(rec) self.list.add(row) # Set the cursor to the first (top) record self.list.set_cursor(path) # Returns how many records there are in the treeview return len(records) def reloadTreeView(self, *arg): # Update list with updated record status = self.gconf_client.get("show_paid_bills") path = self.list.get_cursor()[0] self.list.listStore.clear() self.currentrecord = None first = self.timeline.start_date last = self.timeline.end_date # Get list of records records = self.actions.get_interval_bills(first, last, status) # Populate treeview self.populate_view(records) # Update status bar self.update_statusbar() # populate chart self._populate_chart(status, first, last) return len(records) def format_row(self, row): """ Formats a bill to be displayed as a row. """ categoryName = row.category.name if row.category else _("None") categoryColor = row.category.color if row.category else "#d3d7cf" formatted = [ row.id, create_pixbuf(color=categoryColor), categoryName, row.payee, row.dueDate.strftime(_("%m/%d").encode("ASCII")), row.amount, row.notes, int(row.paid), None, ] return formatted def _populate_chart(self, status, start, end): records = [] categories = [] totals = [] records = self.actions.get_monthly_totals(start, end, status) # Chart widget takes data in format (('CategoryName', amount),) categories = [cat or "None" for cat, total in records] totals = [float(total) for cat, total in records] # records = [(c if c else 'None',float(t)) for c,t in records] # set bar colors all_categories = self.actions.get_categories() self.chart.chart.key_colors = dict([(cat.name or "None", cat.color) for cat in all_categories]) self.chart.plot(categories, totals) def _populate_menubar(self): try: saved_view = self.gconf_client.get("show_paid_bills") except: saved_view = 1 self.gconf_client.set("show_paid_bills", saved_view) if saved_view == 0: self.ui.get_object("showNotPaid").set_active(True) elif saved_view == 1: self.ui.get_object("showPaid").set_active(True) else: self.ui.get_object("showAll").set_active(True) # Check whether we display the toolbar or not self.ui.get_object("showToolbar").set_active(self.gconf_client.get("show_toolbar")) def add_bill(self): selectedDate = self.timeline.value records = dialogs.add_dialog(parent=self.window, selectedDate=selectedDate) # Checks if the user did not cancel the action if records: # Add new bill to database for rec in records: bill_id = self.actions.add(rec) bill = self.actions.get_bills(id=bill_id)[0] if bill: self.list.add(self.format_row(bill)) self.update_statusbar() # Reload records tree (something changed) self.reloadTreeView() self.reloadTimeline() def edit_bill(self): records = dialogs.edit_dialog(parent=self.window, record=self.currentrecord) # Checks if the user did not cancel the action if records: for rec in records: # Edit bill to database rec = self.actions.edit(rec) self.emit("bill-updated", rec) # Reload records tree (something changed) self.reloadTreeView() self.reloadTimeline() def remove_bill(self): self.actions.delete(self.currentrecord) self.list.remove() self.emit("bill-removed", None) self.update_statusbar() self.reloadTreeView() self.reloadTimeline() def toggle_bill_paid(self): # Fetch record from database record = self.actions.get_bills(id=self.currentrecord.id)[0] # Toggle paid field record.paid = False if record.paid else True # Edit bill in the database transaction = self.actions.add(record) self.emit("bill-updated", record) # Update our current copy self.currentrecord = self.actions.get_bills(id=self.currentrecord.id)[0] # Update timeline widget to reflect change self._bullet_cache[self.currentrecord.dueDate] = [self.currentrecord] # Update list with updated record idx = self.list.get_cursor()[0][0] self.update_statusbar(idx) self.reloadTreeView() self.reloadTimeline() def about(self): dialogs.about_dialog(parent=self.window) def preferences(self): dialogs.preferences_dialog(parent=self.window) # Methods def _quit_application(self): self.save_position() self.save_size() self.save_timeline_zoom() gtk.main_quit() return False def save_position(self): x, y = self.window.get_position() self.gconf_client.set("window_position_x", x) self.gconf_client.set("window_position_y", y) def save_size(self): width, height = self.window.get_size() self.gconf_client.set("window_width", width) self.gconf_client.set("window_height", height) def save_timeline_zoom(self): count = self.timeline.count self.gconf_client.set("timeline_count", count) def toggle_buttons(self, paid=None): """ Toggles all buttons conform number of records present and their state """ for widget in ["editBill", "removeBill", "markPaid", "markNotPaid"]: self.ui.get_object(widget).set_sensitive(len(self.list.listStore) > 0) if len(self.list.listStore) > 0: self.ui.get_object("markPaid").set_sensitive(paid == False) self.ui.get_object("markNotPaid").set_sensitive(paid == True) def update_statusbar(self, index=0): """ This function is used to update status bar informations about the list """ records = len(self.list.listStore) # Record count self.statusbar.Records(records) if self.currentrecord: # Display the status self.statusbar.Notes(self.currentrecord.notes) # Toggles toolbar buttons on/off self.toggle_buttons(self.currentrecord.paid) else: # Clear the status for the selected row self.statusbar.Notes("") # Toggles toolbar buttons on/off self.toggle_buttons() show_paid_bills = self.gconf_client.get("show_paid_bills") if show_paid_bills is 0: self.statusbar.Info(_("Not Paid Only")) elif show_paid_bills is 1: self.statusbar.Info(_("Paid Only")) else: self.statusbar.Info("") # Event handlers def _on_list_button_press_event(self, widget, event): """ This function will handle the signal to show a popup menu sent by a right click on tvBill widget. """ if event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS: self.get_selected_record() c = self.ui.get_object("context_menu") c.popup(None, None, None, event.button, event.get_time()) def _on_list_row_activated(self, widget, path, column): self._on_list_cursor_changed(widget) self.on_editBill_activate(None) def _on_list_cursor_changed(self, widget): # Get currently selected bill self.get_selected_record() # Update statusbar self.update_statusbar() def on_newBill_activate(self, toolbutton): self.add_bill() def on_editBill_activate(self, toolbutton): if self.currentrecord: self.edit_bill() def on_removeBill_activate(self, toolbutton): if self.currentrecord: resp = self.message.ShowQuestionYesNo( _('Do you really want to delete "%s"?') % self.currentrecord.payee, self.window, _("Confirmation") ) if resp: self.remove_bill() def on_markNotPaid_activate(self, toolbutton): self.on_markPaid_activate(toolbutton) # forward def on_markPaid_activate(self, toolbutton): if self.currentrecord: self.toggle_bill_paid() def on_btnAbout_activate(self, toolbutton): self.about() def on_btnPrefs_activate(self, toolbutton): self.preferences() def on_btnQuit_activate(self, toolbutton): self._quit_application() def on_delete_event(self, widget, event, data=None): self._quit_application() def _on_timeline_changed(self, widget, args): self.reloadTreeView() def _on_timeline_cleared(self, widget, args): self._bullet_cache = {} def _on_timeline_click(self, widget, event): if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: self.add_bill() def switch_view(self, view_number): self.gconf_client.set("show_paid_bills", view_number) self.reloadTreeView() self.reloadTimeline() def on_showNotPaid_toggled(self, action): if action.get_active(): self.switch_view(0) def on_showPaid_toggled(self, action): if action.get_active(): self.switch_view(1) def on_showAll_toggled(self, action): if action.get_active(): self.switch_view(2) def on_showToolbar_toggled(self, action): # Toggle toolbar's visibility if action.get_active(): self.toolbar.show_all() else: self.toolbar.hide_all() self.gconf_client.set("show_toolbar", action.get_active()) def reloadTimeline(self, *args): self._bullet_cache = {} self.timeline.refresh() def on_timeline_cb(self, date, display_type, force=False): # TODO: Improve tooltip # TODO: Improve cache if date in self._bullet_cache.keys() and not force: return None self._bullet_cache[date] = self.actions.get_bills(dueDate=date) if self._bullet_cache[date]: status = self.gconf_client.get("show_paid_bills") amount = 0 amount_not_paid = 0 tooltip = "" bullet = Event() bullet.date = date for bill in self._bullet_cache[date]: amount += bill.amount if tooltip: tooltip += "\n" tooltip += bill.payee + " (" + str(float_to_currency(bill.amount)) + ")" if bill.paid: tooltip += " [%s]" % _("Paid") if status == 0: return False bullet.status = bullet.status | bullet.PAID else: amount_not_paid += bill.amount if date <= datetime.date.today(): if status == 1: return False bullet.status = bullet.status | bullet.OVERDUE else: if status == 1: return False bullet.status = bullet.status | bullet.TO_BE_PAID if bill.notes: tooltip += " - " + bill.notes bullet.amountdue = amount_not_paid if amount_not_paid else amount bullet.payee = bill.payee bills = len(self._bullet_cache[date]) if bills > 1: bullet.multi = bills bullet.payee = "%d bills" % bills bullet.tooltip = tooltip return bullet return None
class PrefDialog(gtk.Dialog): """ Class used to generate dialog to allow user to edit preferences. """ def __init__(self, parent=None): title = _("Preferences") gtk.Dialog.__init__(self, title=title, parent=parent, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |gtk.DIALOG_NO_SEPARATOR, buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) self.set_position(gtk.WIN_POS_CENTER) self.set_border_width(6) self.set_resizable(False) self.set_icon_from_file(common.APP_ICON) self.props.skip_taskbar_hint = True self.gconf_client = Settings() self._initialize_dialog_widgets() self._populate_fields() self._connect_fields() self.connect("response", lambda self, *args: self.destroy()) def _initialize_dialog_widgets(self): self.topcontainer = gtk.VBox(homogeneous=False, spacing=18) self.topcontainer.set_border_width(6) # Alert Group alertFrame = gtk.VBox(homogeneous=False, spacing=6) alertAlignment = gtk.Alignment() alertAlignment.set_padding(0, 0, 12, 0) title = gtk.Label() title.set_markup(_("<b>Alarms</b>")) title.set_alignment(0.00, 0.50) alertFrame.pack_start(title) alertFrame.pack_start(alertAlignment) alertContainer = gtk.VBox(homogeneous=False, spacing=6) self.alertCheckbox = gtk.CheckButton(_('_Alert before due date:'), use_underline=True) self.alertSpinButton = gtk.SpinButton() self.alertSpinButton.set_range(0, 360) self.alertSpinButton.spin(gtk.SPIN_STEP_FORWARD) self.alertSpinButton.set_increments(1, 7) alertDays = gtk.Label("%s" % _('day(s).')) self.notificationTime = TimeWidget() alertPreferredTime = gtk.Label() alertPreferredTime.set_markup_with_mnemonic(_('_Preferred time:')) alertPreferredTime.set_mnemonic_widget(self.notificationTime.hourSpinner) alertPreferredTime.set_alignment(0.00, 0.50) alertDefinition = gtk.Label(_('Get alerted when individual bills are due.')) alertDefinition.set_alignment(0.00, 0.90) # Add label defining what an alarm means. alertContainer.pack_start(alertDefinition, expand=False, fill=True, padding=0) # Container for alert checkbox and spin button for day selection. hbox = gtk.HBox(homogeneous=False, spacing=4) hbox.pack_start(self.alertCheckbox, expand=False, fill=True, padding=0) hbox.pack_start(self.alertSpinButton, expand=False, fill=False, padding=0) hbox.pack_start(alertDays, expand=False, fill=False, padding=0) alertContainer.pack_start(hbox, expand=False, fill=True, padding=0) # Container for preferred time for alerts. hbox = gtk.HBox(homogeneous=False, spacing=12) hbox.pack_start(alertPreferredTime, expand=False, fill=True, padding=0) hbox.pack_start(self.notificationTime, expand=True, fill=True, padding=0) alertContainer.pack_start(hbox, expand=False, fill=True, padding=0) alertAlignment.add(alertContainer) # Notification Group notifyFrame = gtk.VBox(homogeneous=False, spacing=6) title = gtk.Label() title.set_markup(_("<b>Notifications</b>")) title.set_alignment(0.00, 0.50) notifyAlignment = gtk.Alignment() notifyAlignment.set_padding(0, 0, 12, 0) notifyFrame.pack_start(title) notifyFrame.pack_start(notifyAlignment) notificationDefinition = gtk.Label(_('Define when to be notified of upcoming bills.')) notificationDefinition.set_alignment(0.00, 0.90) notificationsContainer = gtk.VBox(homogeneous=False, spacing=6) # Add label defining what a definition means. notificationsContainer.pack_start(notificationDefinition, expand=False, fill=False, padding=0) self.notifyCheckbox = gtk.CheckButton(_('_Notify before due date:'), use_underline=True) self.notifySpinButton = gtk.SpinButton() self.notifySpinButton.set_range(0, 360) self.notifySpinButton.spin(gtk.SPIN_STEP_FORWARD) self.notifySpinButton.set_increments(1, 7) notifyDays = gtk.Label("%s" % _('day(s).')) # Container for notification checkbox and spin button for day selection. hbox = gtk.HBox(homogeneous=False, spacing=4) hbox.pack_start(self.notifyCheckbox, expand=False, fill=True, padding=0) hbox.pack_start(self.notifySpinButton, expand=False, fill=False, padding=0) hbox.pack_start(notifyDays, expand=False, fill=False, padding=0) notificationsContainer.pack_start(hbox, expand=False, fill=True, padding=0) notifyAlignment.add(notificationsContainer) # Alert Type Group alertTypeFrame = gtk.VBox(homogeneous=False, spacing=6) title = gtk.Label() title.set_markup(_("<b>Alert Type</b>")) title.set_alignment(0.00, 0.50) alertTypeAlignment = gtk.Alignment() alertTypeAlignment.set_padding(0, 0, 12, 0) alertTypeFrame.pack_start(title) alertTypeFrame.pack_start(alertTypeAlignment) vbox = gtk.VBox(homogeneous=False, spacing=6) hbox = gtk.HBox(homogeneous=False, spacing=12) self.alertBubble = gtk.RadioButton(label=_("Notification _Bubble")) self.alertDialog = gtk.RadioButton(group=self.alertBubble, label=_("Alert _Dialog")) hbox.pack_start(self.alertBubble, expand=False, fill=False, padding=0) hbox.pack_start(self.alertDialog, expand=False, fill=False, padding=0) vbox.pack_start(hbox, expand=False, fill=False, padding=0) alertTypeAlignment.add(vbox) # Daemon Warning daemonContainer = gtk.VBox(homogeneous=False, spacing=6) self.daemonLabel = gtk.Label( _("<b>Warning:</b> BillReminder Notifier is \n" \ "not running! You need to start it in order \n" \ " to receive notifications.")) self.daemonLabel.set_justify(gtk.JUSTIFY_CENTER) self.daemonLabel.set_use_markup(True) daemonImage = gtk.Image() daemonImage.set_from_stock('gtk-execute', 2) self.daemonButton = gtk.Button(label=_("_Start BillReminder Notifier")) self.daemonButton.set_relief(gtk.RELIEF_NONE) self.daemonButton.set_image(daemonImage) daemonContainer.pack_start(self.daemonLabel, expand=False, fill=True, padding=0) daemonContainer.pack_start(self.daemonButton, expand=False, fill=True, padding=0) # Everything self.topcontainer.pack_start(alertFrame, expand=False, fill=True, padding=0) self.topcontainer.pack_start(notifyFrame, expand=False, fill=True, padding=0) self.topcontainer.pack_start(alertTypeFrame, expand=False, fill=True, padding=0) if not utils.verify_dbus_service(common.DBUS_INTERFACE): self.topcontainer.pack_start(daemonContainer, expand=False, fill=True, padding=0) self.vbox.pack_start(self.topcontainer, expand=False, fill=True) self.show_all() def _populate_fields(self): self.notifyCheckbox.set_active(self.gconf_client.get('show_before_alarm')) self.alertCheckbox.set_active(self.gconf_client.get('show_alarm')) if not self.gconf_client.get('use_alert_dialog'): self.alertBubble.set_active(True) else: self.alertDialog.set_active(True) # Number of days before showing alarm adays = self.gconf_client.get('notification_days_limit') self.notifySpinButton.set_value(adays and adays or 3) self.alertSpinButton.set_value(self.gconf_client.get('show_alarm_before_days')) atime = self.gconf_client.get('show_alarm_at_time') atime = atime.split(":") self.notificationTime.setHourMinute(atime[0], atime[1]) def _connect_fields(self): self.notificationTime.hourSpinner.connect("value_changed", self._on_time_changed) self.notificationTime.minuteSpinner.connect("value_changed", self._on_time_changed) self.notifyCheckbox.connect("toggled", self._on_checkbox_toggled, 'show_before_alarm') self.alertCheckbox.connect("toggled", self._on_checkbox_toggled, 'show_alarm') self.notifySpinButton.connect("changed", self._on_spin_changed, 'notification_days_limit') self.alertSpinButton.connect("changed", self._on_spin_changed, 'show_alarm_before_days') self.alertDialog.connect("toggled", self._on_checkbox_toggled, 'use_alert_dialog') self.daemonButton.connect("clicked", self._launch_daemon) def _on_time_changed(self, spin): alarm = self.notificationTime.getTime() alarm = ":".join(["%.02d" % x for x in alarm]) self.gconf_client.set('show_alarm_at_time', alarm) def _on_checkbox_toggled(self, togglebutton, item): self.gconf_client.set(item, togglebutton.get_active()) def _on_spin_changed(self, obj, item): self.gconf_client.set(item, int(obj.get_value())) def _launch_daemon(self, button): Popen('billreminderd', shell=True) button.hide() self.daemonLabel.set_markup( _("<b>Note:</b> BillReminder Notifier is now running."))
class Alarm(object): def __init__(self, parent): self.parent = parent self.tray_hints = {} self.parent = parent self.tray_hints = {} self.gconf_client = Configuration() self.start() def start(self): start_delay = self.gconf_client.get('startup_delay') print start_delay showStartup = self.gconf_client.get('show_startup_notification') print showStartup if showStartup: timeout_add(start_delay, self.show_pay_notification) timeout_add(start_delay + 12000, self.verify_due) interval = self.gconf_client.get('interval') print interval if interval: timeout_add(interval, self.timer) def notification(self, title, body): notify = NotifyMessage(self.parent) notify.title = title notify.body = body notify.set_timeout(20) notify.set_default_action(self.__cb_launch_gui) notify.hints = self.tray_hints return notify def show_pay_notification(self): print "Got here 3" today = datetime.datetime.today() limit = self.gconf_client.get('notification_days_limit') print limit limit = datetime.timedelta(days=limit) end = today + limit if limit: records = self.parent.actions.get_interval_bills(end=end, paid=False) #'dueDate <= %s AND paid = 0' % (today + limit)) else: print "No limit" records = self.parent.actions.get_bills(paid=False) msg = ngettext('You have %s outstanding bill to pay!', 'You have %s outstanding bills to pay!', len(records)) % len(records) if msg and records: bubble = self.notification(common.APPNAME, msg) bubble.add_action("view", _("Show BillReminder"), self.__cb_launch_gui, None) #bubble.add_action("close", _("Cancel"), None) bubble.show() return False def verify_due(self, sum=0): print "Got here 2" showDueAlarm = self.gconf_client.get('show_due_alarm') print showDueAlarm if not showDueAlarm: print "Do not show due alarm" return today = datetime.datetime.today() if sum > 0: records = self.parent.actions.get_interval_bills(today, today, False) else: records = self.parent.actions.get_interval_bills(end=today, paid=False) i = 1 use_dialog = self.gconf_client.get('use_alert_dialog') print use_dialog # TODO: use only one dialog for all bills, if use_dialog == True for bill in records: if sum > 0: # date format string dtformat = locale.nl_langinfo(locale.D_FMT) # date from record in timestamp format dtstamp = bill.dueDate # record dictionary recDict = { 'bill': "<b>\"%s\"</b>" % bill.payee, 'day': "<b>\"%s\"</b>" % dtstamp.strftime(dtformat).encode('ASCII') } # TODO: calculate days timeout_add(i * 12000, self.show_bill_notification, bill, _("The bill %(bill)s will be due at %(day)s.") % recDict, use_dialog) else: timeout_add(i * 12000, self.show_bill_notification, bill, None, use_dialog) i += 1 def show_bill_notification(self, bill=None, msg=None, alert=False, timeout=None): if msg is None: msg = _('The bill %s is due.') % "<b>\"%s\"</b>" % bill.payee if not self.parent.actions.get_bills(id=bill.id)[0].paid: if alert: alert = Message().ShowBillInfo(text=msg, title=_("BillReminder Notifier")) if alert == RESPONSE_YES: self.__cb_mark_as_paid(None, (bill,)) elif alert == RESPONSE_NO: self.__cb_edit_bill(None, (bill,)) else: bubble = self.notification(common.APPNAME, msg) bubble.add_action("paid", _("Mark as paid"), self.__cb_mark_as_paid, bill) bubble.add_action("edit", _("Edit"), self.__cb_edit_bill, bill) #bubble.add_action("close", _("Cancel"), None) if timeout: bubble.set_timeout(timeout) bubble.show() return False def __cb_launch_gui(self, *arg): # If client is not running, launch it # Send DBus 'show_main_window' signal self.parent.dbus_server.show_main_window() if not self.parent.client_pid or \ not verify_pid(self.parent.client_pid): gui = Popen('billreminder', shell=True) self.parent.client_pid = gui.pid def __cb_mark_as_paid(self, *arg): record = arg[1][0] if record: record.paid = True try: # Edit bill to database self.parent.dbus_server.edit_bill(record) except Exception, e: print "Error #1" print str(e)
class PrefDialog(gtk.Dialog): """ Class used to generate dialog to allow user to edit preferences. """ def __init__(self, parent=None): title = _("Preferences") gtk.Dialog.__init__(self, title=title, parent=parent, flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, buttons=(gtk.STOCK_CLOSE, gtk.RESPONSE_ACCEPT)) self.set_position(gtk.WIN_POS_CENTER) self.set_border_width(6) self.set_resizable(False) self.set_icon_from_file(common.APP_ICON) self.props.skip_taskbar_hint = True self.gconf_client = Settings() self._initialize_dialog_widgets() self._populate_fields() self._connect_fields() self.connect("response", lambda self, *args: self.destroy()) def _initialize_dialog_widgets(self): self.topcontainer = gtk.VBox(homogeneous=False, spacing=18) self.topcontainer.set_border_width(6) # Alert Group alertFrame = gtk.VBox(homogeneous=False, spacing=6) alertAlignment = gtk.Alignment() alertAlignment.set_padding(0, 0, 12, 0) title = gtk.Label() title.set_markup(_("<b>Alarms</b>")) title.set_alignment(0.00, 0.50) alertFrame.pack_start(title) alertFrame.pack_start(alertAlignment) alertContainer = gtk.VBox(homogeneous=False, spacing=6) self.alertCheckbox = gtk.CheckButton(_('_Alert before due date:'), use_underline=True) self.alertSpinButton = gtk.SpinButton() self.alertSpinButton.set_range(0, 360) self.alertSpinButton.spin(gtk.SPIN_STEP_FORWARD) self.alertSpinButton.set_increments(1, 7) alertDays = gtk.Label("%s" % _('day(s).')) self.notificationTime = TimeWidget() alertPreferredTime = gtk.Label() alertPreferredTime.set_markup_with_mnemonic(_('_Preferred time:')) alertPreferredTime.set_mnemonic_widget( self.notificationTime.hourSpinner) alertPreferredTime.set_alignment(0.00, 0.50) alertDefinition = gtk.Label( _('Get alerted when individual bills are due.')) alertDefinition.set_alignment(0.00, 0.90) # Add label defining what an alarm means. alertContainer.pack_start(alertDefinition, expand=False, fill=True, padding=0) # Container for alert checkbox and spin button for day selection. hbox = gtk.HBox(homogeneous=False, spacing=4) hbox.pack_start(self.alertCheckbox, expand=False, fill=True, padding=0) hbox.pack_start(self.alertSpinButton, expand=False, fill=False, padding=0) hbox.pack_start(alertDays, expand=False, fill=False, padding=0) alertContainer.pack_start(hbox, expand=False, fill=True, padding=0) # Container for preferred time for alerts. hbox = gtk.HBox(homogeneous=False, spacing=12) hbox.pack_start(alertPreferredTime, expand=False, fill=True, padding=0) hbox.pack_start(self.notificationTime, expand=True, fill=True, padding=0) alertContainer.pack_start(hbox, expand=False, fill=True, padding=0) alertAlignment.add(alertContainer) # Notification Group notifyFrame = gtk.VBox(homogeneous=False, spacing=6) title = gtk.Label() title.set_markup(_("<b>Notifications</b>")) title.set_alignment(0.00, 0.50) notifyAlignment = gtk.Alignment() notifyAlignment.set_padding(0, 0, 12, 0) notifyFrame.pack_start(title) notifyFrame.pack_start(notifyAlignment) notificationDefinition = gtk.Label( _('Define when to be notified of upcoming bills.')) notificationDefinition.set_alignment(0.00, 0.90) notificationsContainer = gtk.VBox(homogeneous=False, spacing=6) # Add label defining what a definition means. notificationsContainer.pack_start(notificationDefinition, expand=False, fill=False, padding=0) self.notifyCheckbox = gtk.CheckButton(_('_Notify before due date:'), use_underline=True) self.notifySpinButton = gtk.SpinButton() self.notifySpinButton.set_range(0, 360) self.notifySpinButton.spin(gtk.SPIN_STEP_FORWARD) self.notifySpinButton.set_increments(1, 7) notifyDays = gtk.Label("%s" % _('day(s).')) # Container for notification checkbox and spin button for day selection. hbox = gtk.HBox(homogeneous=False, spacing=4) hbox.pack_start(self.notifyCheckbox, expand=False, fill=True, padding=0) hbox.pack_start(self.notifySpinButton, expand=False, fill=False, padding=0) hbox.pack_start(notifyDays, expand=False, fill=False, padding=0) notificationsContainer.pack_start(hbox, expand=False, fill=True, padding=0) notifyAlignment.add(notificationsContainer) # Alert Type Group alertTypeFrame = gtk.VBox(homogeneous=False, spacing=6) title = gtk.Label() title.set_markup(_("<b>Alert Type</b>")) title.set_alignment(0.00, 0.50) alertTypeAlignment = gtk.Alignment() alertTypeAlignment.set_padding(0, 0, 12, 0) alertTypeFrame.pack_start(title) alertTypeFrame.pack_start(alertTypeAlignment) vbox = gtk.VBox(homogeneous=False, spacing=6) hbox = gtk.HBox(homogeneous=False, spacing=12) self.alertBubble = gtk.RadioButton(label=_("Notification _Bubble")) self.alertDialog = gtk.RadioButton(group=self.alertBubble, label=_("Alert _Dialog")) hbox.pack_start(self.alertBubble, expand=False, fill=False, padding=0) hbox.pack_start(self.alertDialog, expand=False, fill=False, padding=0) vbox.pack_start(hbox, expand=False, fill=False, padding=0) alertTypeAlignment.add(vbox) # Daemon Warning daemonContainer = gtk.VBox(homogeneous=False, spacing=6) self.daemonLabel = gtk.Label( _("<b>Warning:</b> BillReminder Notifier is \n" \ "not running! You need to start it in order \n" \ " to receive notifications.")) self.daemonLabel.set_justify(gtk.JUSTIFY_CENTER) self.daemonLabel.set_use_markup(True) daemonImage = gtk.Image() daemonImage.set_from_stock('gtk-execute', 2) self.daemonButton = gtk.Button(label=_("_Start BillReminder Notifier")) self.daemonButton.set_relief(gtk.RELIEF_NONE) self.daemonButton.set_image(daemonImage) daemonContainer.pack_start(self.daemonLabel, expand=False, fill=True, padding=0) daemonContainer.pack_start(self.daemonButton, expand=False, fill=True, padding=0) # Everything self.topcontainer.pack_start(alertFrame, expand=False, fill=True, padding=0) self.topcontainer.pack_start(notifyFrame, expand=False, fill=True, padding=0) self.topcontainer.pack_start(alertTypeFrame, expand=False, fill=True, padding=0) if not utils.verify_dbus_service(common.DBUS_INTERFACE): self.topcontainer.pack_start(daemonContainer, expand=False, fill=True, padding=0) self.vbox.pack_start(self.topcontainer, expand=False, fill=True) self.show_all() def _populate_fields(self): self.notifyCheckbox.set_active( self.gconf_client.get('show_before_alarm')) self.alertCheckbox.set_active(self.gconf_client.get('show_alarm')) if not self.gconf_client.get('use_alert_dialog'): self.alertBubble.set_active(True) else: self.alertDialog.set_active(True) # Number of days before showing alarm adays = self.gconf_client.get('notification_days_limit') self.notifySpinButton.set_value(adays and adays or 3) self.alertSpinButton.set_value( self.gconf_client.get('show_alarm_before_days')) atime = self.gconf_client.get('show_alarm_at_time') atime = atime.split(":") self.notificationTime.setHourMinute(atime[0], atime[1]) def _connect_fields(self): self.notificationTime.hourSpinner.connect("value_changed", self._on_time_changed) self.notificationTime.minuteSpinner.connect("value_changed", self._on_time_changed) self.notifyCheckbox.connect("toggled", self._on_checkbox_toggled, 'show_before_alarm') self.alertCheckbox.connect("toggled", self._on_checkbox_toggled, 'show_alarm') self.notifySpinButton.connect("changed", self._on_spin_changed, 'notification_days_limit') self.alertSpinButton.connect("changed", self._on_spin_changed, 'show_alarm_before_days') self.alertDialog.connect("toggled", self._on_checkbox_toggled, 'use_alert_dialog') self.daemonButton.connect("clicked", self._launch_daemon) def _on_time_changed(self, spin): alarm = self.notificationTime.getTime() alarm = ":".join(["%.02d" % x for x in alarm]) self.gconf_client.set('show_alarm_at_time', alarm) def _on_checkbox_toggled(self, togglebutton, item): self.gconf_client.set(item, togglebutton.get_active()) def _on_spin_changed(self, obj, item): self.gconf_client.set(item, int(obj.get_value())) def _launch_daemon(self, button): Popen('billreminderd', shell=True) button.hide() self.daemonLabel.set_markup( _("<b>Note:</b> BillReminder Notifier is now running."))
class MainDialog(gobject.GObject): search_text = "" _bullet_cache = {} __gsignals__ = { 'bill-added': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )), 'bill-updated': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )), 'bill-removed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_PYOBJECT, )) } def __init__(self): gobject.GObject.__init__(self) if exists(join(USER_CFG_PATH, CFG_NAME)): from lib.migrate_to_gconf import migrate migrate(join(USER_CFG_PATH, CFG_NAME)) self.gconf_client = Configuration() self.message = Message() # Connects to the database self.actions = Actions() self.ui = gtk.Builder() self.ui.add_from_file(os.path.join(DEFAULT_CFG_PATH, "main.ui")) self.window = self.ui.get_object("main_window") self.window.set_title("%s" % common.APPNAME) self.window.set_icon_from_file(common.APP_ICON) # ViewBill self.list = ViewBill() self.list.connect('cursor_changed', self._on_list_cursor_changed) self.list.connect('row_activated', self._on_list_row_activated) self.list.connect('button_press_event', self._on_list_button_press_event) self.ui.get_object("bill_box").add(self.list) # Toolbar self.toolbar = self.ui.get_object("toolbar") # Menubar self._populate_menubar() # Statusbar self.statusbar = Statusbar() self.ui.get_object("statusbar_box").add(self.statusbar) # Restore timeline zoom timeline_count = self.gconf_client.get('timeline_count') # Timeline self.timeline = Timeline(count=timeline_count, callback=self.on_timeline_cb) self.timeline.connect("value-changed", self._on_timeline_changed) self.timeline.connect("cleared", self._on_timeline_cleared) self.ui.get_object("timeline_box").add(self.timeline) # Chart self.chart = ChartWidget() self.chart.set_border_width(10) self.ui.get_object("chart_box").add(self.chart) # Restore position and size of window width = self.gconf_client.get('window_width') height = self.gconf_client.get('window_height') x = self.gconf_client.get('window_position_x') y = self.gconf_client.get('window_position_y') if width and height: self.window.resize(width, height) if x and y: self.window.move(x, y) self.window.show_all() # Whether to display toolbar or not self.on_showToolbar_toggled(self.ui.get_object("showToolbar")) self.list.grab_focus() if self.gconf_client.get('start_in_tray'): self.window.hide() self.toggle_buttons() # Connects to the Daemon self.iface = None iface = get_dbus_interface(common.DBUS_INTERFACE, common.DBUS_PATH) if iface: iface.connect_to_signal("bill_edited", self.reloadTreeView) iface.connect_to_signal("bill_edited", self.reloadTimeline) iface.connect_to_signal("show_main_window", self.window.show) self.iface = iface timeout_add(2000, self._send_tray_hints) self.set_action_strings() self.ui.connect_signals(self) # populate treeview self.reloadTreeView() self.notify = NotifyIcon(self) # Integrate with Ubuntu Unity if UNITY: self.unity = UnityIntegration(self) def set_action_strings(self): # for some reason the actions strings do not get translated yet # so we define them here so they would be picked up by the pyfile scanner self.ui.get_object("newBill").set_label(_("_New")) self.ui.get_object("newBill").set_tooltip(_("Add new bill")) self.ui.get_object("editBill").set_label(_("_Edit")) self.ui.get_object("editBill").set_tooltip(_("Edit a bill")) self.ui.get_object("removeBill").set_label(_("_Delete")) self.ui.get_object("removeBill").set_tooltip(_("Delete selected bill")) self.ui.get_object("markPaid").set_label(_("P_aid")) self.ui.get_object("markPaid").set_tooltip(_("Mark as paid")) self.ui.get_object("markNotPaid").set_label(_("No_t Paid")) self.ui.get_object("markNotPaid").set_tooltip(_("Mark as not paid")) self.ui.get_object("showToolbar").set_label(_("_Show Toolbar")) self.ui.get_object("showToolbar").set_tooltip(_("Show the toolbar")) # Methods: UI def _send_tray_hints(self): self.iface.set_tray_hints(force_string(self.notify.get_hints())) timeout_add(60000, self._send_tray_hints) def get_window_visibility(self): return self.window.get_property("visible") def show_hide_window(self): if self.window.get_property("visible"): self.window.hide() else: self.window.show() def get_selected_record(self): """ Returns a bill object from the current selected record """ if len(self.list.listStore) > 0: model_ = self.list.get_model() if self.list.get_cursor()[0]: index = self.list.get_cursor()[0][0] else: index = 0 b_id = model_[index][0] records = self.actions.get_bills(id=b_id) self.currentrecord = records[0] else: self.currentrecord = None print "Current record is: %s" % self.currentrecord def populate_view(self, records): """ Populates the treeview control with the records passed """ # Reset list self.list.listStore.clear() if not records: return 0 # Loops through bills collection path = 0 for rec in records: # Format the record prior to adding it to treeview row = self.format_row(rec) self.list.add(row) # Set the cursor to the first (top) record self.list.set_cursor(path) # Returns how many records there are in the treeview return len(records) def reloadTreeView(self, *arg): # Update list with updated record status = self.gconf_client.get('show_paid_bills') path = self.list.get_cursor()[0] self.list.listStore.clear() self.currentrecord = None first = self.timeline.start_date last = self.timeline.end_date # Get list of records records = self.actions.get_interval_bills(first, last, status) # Populate treeview self.populate_view(records) # Update status bar self.update_statusbar() # populate chart self._populate_chart(status, first, last) return len(records) def format_row(self, row): """ Formats a bill to be displayed as a row. """ categoryName = row.category.name if row.category else _('None') categoryColor = row.category.color if row.category else '#d3d7cf' formatted = [ row.id, create_pixbuf(color=categoryColor), categoryName, row.payee, row.dueDate.strftime(_('%m/%d').encode('ASCII')), row.amount, row.notes, int(row.paid), None ] return formatted def _populate_chart(self, status, start, end): records = [] categories = [] totals = [] records = self.actions.get_monthly_totals(start, end, status) # Chart widget takes data in format (('CategoryName', amount),) categories = [cat or 'None' for cat, total in records] totals = [float(total) for cat, total in records] #records = [(c if c else 'None',float(t)) for c,t in records] # set bar colors all_categories = self.actions.get_categories() self.chart.chart.key_colors = dict([(cat.name or 'None', cat.color) for cat in all_categories]) self.chart.plot(categories, totals) def _populate_menubar(self): try: saved_view = self.gconf_client.get('show_paid_bills') except: saved_view = 1 self.gconf_client.set("show_paid_bills", saved_view) if saved_view == 0: self.ui.get_object("showNotPaid").set_active(True) elif saved_view == 1: self.ui.get_object("showPaid").set_active(True) else: self.ui.get_object("showAll").set_active(True) # Check whether we display the toolbar or not self.ui.get_object("showToolbar").set_active( self.gconf_client.get('show_toolbar')) def add_bill(self): selectedDate = self.timeline.value records = dialogs.add_dialog(parent=self.window, selectedDate=selectedDate) # Checks if the user did not cancel the action if records: # Add new bill to database for rec in records: bill_id = self.actions.add(rec) bill = self.actions.get_bills(id=bill_id)[0] if bill: self.list.add(self.format_row(bill)) self.update_statusbar() # Reload records tree (something changed) self.reloadTreeView() self.reloadTimeline() def edit_bill(self): records = dialogs.edit_dialog(parent=self.window, record=self.currentrecord) # Checks if the user did not cancel the action if records: for rec in records: # Edit bill to database rec = self.actions.edit(rec) self.emit('bill-updated', rec) # Reload records tree (something changed) self.reloadTreeView() self.reloadTimeline() def remove_bill(self): self.actions.delete(self.currentrecord) self.list.remove() self.emit('bill-removed', None) self.update_statusbar() self.reloadTreeView() self.reloadTimeline() def toggle_bill_paid(self): # Fetch record from database record = self.actions.get_bills(id=self.currentrecord.id)[0] # Toggle paid field record.paid = False if record.paid else True # Edit bill in the database transaction = self.actions.add(record) self.emit('bill-updated', record) # Update our current copy self.currentrecord = self.actions.get_bills( id=self.currentrecord.id)[0] # Update timeline widget to reflect change self._bullet_cache[self.currentrecord.dueDate] = [self.currentrecord] # Update list with updated record idx = self.list.get_cursor()[0][0] self.update_statusbar(idx) self.reloadTreeView() self.reloadTimeline() def about(self): dialogs.about_dialog(parent=self.window) def preferences(self): dialogs.preferences_dialog(parent=self.window) # Methods def _quit_application(self): self.save_position() self.save_size() self.save_timeline_zoom() gtk.main_quit() return False def save_position(self): x, y = self.window.get_position() self.gconf_client.set('window_position_x', x) self.gconf_client.set('window_position_y', y) def save_size(self): width, height = self.window.get_size() self.gconf_client.set('window_width', width) self.gconf_client.set('window_height', height) def save_timeline_zoom(self): count = self.timeline.count self.gconf_client.set('timeline_count', count) def toggle_buttons(self, paid=None): """ Toggles all buttons conform number of records present and their state """ for widget in ["editBill", "removeBill", "markPaid", "markNotPaid"]: self.ui.get_object(widget).set_sensitive( len(self.list.listStore) > 0) if len(self.list.listStore) > 0: self.ui.get_object("markPaid").set_sensitive(paid == False) self.ui.get_object("markNotPaid").set_sensitive(paid == True) def update_statusbar(self, index=0): """ This function is used to update status bar informations about the list """ records = len(self.list.listStore) # Record count self.statusbar.Records(records) if self.currentrecord: # Display the status self.statusbar.Notes(self.currentrecord.notes) # Toggles toolbar buttons on/off self.toggle_buttons(self.currentrecord.paid) else: # Clear the status for the selected row self.statusbar.Notes("") # Toggles toolbar buttons on/off self.toggle_buttons() show_paid_bills = self.gconf_client.get('show_paid_bills') if show_paid_bills is 0: self.statusbar.Info(_("Not Paid Only")) elif show_paid_bills is 1: self.statusbar.Info(_("Paid Only")) else: self.statusbar.Info('') # Event handlers def _on_list_button_press_event(self, widget, event): """ This function will handle the signal to show a popup menu sent by a right click on tvBill widget. """ if event.button == 3 and event.type == gtk.gdk.BUTTON_PRESS: self.get_selected_record() c = self.ui.get_object("context_menu") c.popup(None, None, None, event.button, event.get_time()) def _on_list_row_activated(self, widget, path, column): self._on_list_cursor_changed(widget) self.on_editBill_activate(None) def _on_list_cursor_changed(self, widget): # Get currently selected bill self.get_selected_record() # Update statusbar self.update_statusbar() def on_newBill_activate(self, toolbutton): self.add_bill() def on_editBill_activate(self, toolbutton): if self.currentrecord: self.edit_bill() def on_removeBill_activate(self, toolbutton): if self.currentrecord: resp = self.message.ShowQuestionYesNo( _("Do you really want to delete \"%s\"?") % \ self.currentrecord.payee, self.window, _("Confirmation")) if resp: self.remove_bill() def on_markNotPaid_activate(self, toolbutton): self.on_markPaid_activate(toolbutton) # forward def on_markPaid_activate(self, toolbutton): if self.currentrecord: self.toggle_bill_paid() def on_btnAbout_activate(self, toolbutton): self.about() def on_btnPrefs_activate(self, toolbutton): self.preferences() def on_btnQuit_activate(self, toolbutton): self._quit_application() def on_delete_event(self, widget, event, data=None): self._quit_application() def _on_timeline_changed(self, widget, args): self.reloadTreeView() def _on_timeline_cleared(self, widget, args): self._bullet_cache = {} def switch_view(self, view_number): self.gconf_client.set('show_paid_bills', view_number) self.reloadTreeView() self.reloadTimeline() def on_showNotPaid_toggled(self, action): if action.get_active(): self.switch_view(0) def on_showPaid_toggled(self, action): if action.get_active(): self.switch_view(1) def on_showAll_toggled(self, action): if action.get_active(): self.switch_view(2) def on_showToolbar_toggled(self, action): # Toggle toolbar's visibility if action.get_active(): self.toolbar.show_all() else: self.toolbar.hide_all() self.gconf_client.set("show_toolbar", action.get_active()) def reloadTimeline(self, *args): self._bullet_cache = {} self.timeline.refresh() def on_timeline_cb(self, date, display_type, force=False): # TODO: Improve tooltip # TODO: Improve cache if date in self._bullet_cache.keys() and not force: return None self._bullet_cache[date] = self.actions.get_bills(dueDate=date) if self._bullet_cache[date]: status = self.gconf_client.get('show_paid_bills') amount = 0 amount_not_paid = 0 tooltip = '' bullet = Event() bullet.date = date for bill in self._bullet_cache[date]: amount += bill.amount if tooltip: tooltip += '\n' tooltip += bill.payee + ' (' + str( float_to_currency(bill.amount)) + ')' if bill.paid: tooltip += ' [%s]' % _('Paid') if status == 0: return False bullet.status = bullet.status | bullet.PAID else: amount_not_paid += bill.amount if date <= datetime.date.today(): if status == 1: return False bullet.status = bullet.status | bullet.OVERDUE else: if status == 1: return False bullet.status = bullet.status | bullet.TO_BE_PAID if bill.notes: tooltip += ' - ' + bill.notes bullet.amountdue = amount_not_paid if amount_not_paid else amount bullet.payee = bill.payee bills = len(self._bullet_cache[date]) if bills > 1: bullet.multi = bills bullet.payee = '%d bills' % bills bullet.tooltip = tooltip return bullet return None
class AddDialog(object): """ Class used to generate dialog to allow user to enter/edit records. """ def __init__(self, title=None, parent=None, record=None, selectedDate=None): self.ui = gtk.Builder() self.ui.add_from_file(os.path.join(common.DEFAULT_CFG_PATH, "add_bill.ui")) self.window = self.ui.get_object("add_bill_dialog") self.window.set_icon_from_file(common.APP_ICON) if parent: self.window.set_transient_for(parent) # If we have a selected date, then set calendar to use it if not selectedDate: selectedDate = datetime.date.today() self.selectedDate = selectedDate self.gconf_client = Configuration() # Private copy of any record passed self.currentrecord = record self.alarm = [None, None] # TODO: This needs to be run BEFORE connecting the widgets self._set_currency() # Set up the UI self._initialize_dialog_widgets() self._populate_widgets() self.category_index_before = 0 self.ui.connect_signals(self) def _set_currency(self): self.decimal_sep = locale.localeconv()['mon_decimal_point'] self.thousands_sep = locale.localeconv()['mon_thousands_sep'] self.allowed_digts = [self.decimal_sep , self.thousands_sep] self.allowed_digts += [str(i) for i in range(10)] def _initialize_dialog_widgets(self): self.frequency = self.ui.get_object("frequency") self.dueDate = DatePicker() self.ui.get_object("due_date_box").add(self.dueDate) self.dueDate.connect('date_changed', self._on_datepicker_date_changed) self.endDate = DatePicker() self.ui.get_object("end_date_box").add(self.endDate) self.endDate.connect('date_changed', self._on_datepicker_date_changed) self.payee = self.ui.get_object("payee") self.payeecompletion = gtk.EntryCompletion() self.payee.child.set_completion(self.payeecompletion) self.amount = self.ui.get_object("amount") self.category = self.ui.get_object("category") px = gtk.CellRendererPixbuf() txt = gtk.CellRendererText() self.category.pack_start(px, False) self.category.pack_start(txt, False) self.category.add_attribute(px, "pixbuf", 0) self.category.add_attribute(txt, "text", 1) self.category.set_row_separator_func(self._determine_separator) self.categorybutton = self.ui.get_object("edit_categories") self.notes = self.ui.get_object("notes") self.txtbuffer = self.notes.get_buffer() self.alarmbutton = DateButton(self.window) self.alarmbutton.set_tooltip_text(_("Select Date and Time")) self.ui.get_object("alarm_button_box").add(self.alarmbutton) self.ui.get_object("alarm_label").set_mnemonic_widget(self.alarmbutton) self.window.show_all() def _populate_widgets(self): """ Populate dialog widgets so they can be used. """ self._populate_frequency() self._populate_payee() # Populate combobox with payee from db self._populate_category() # Populate combobox with category from db # If a record was passed, we're in edit mode if self.currentrecord: self._populate_widgets_with_record() #in edit mode we must disable repetition self.frequency.set_sensitive(False) self.endDate.set_sensitive(False) else: self.dueDate.set_date(self.selectedDate) self.endDate.set_date(self.selectedDate) # Use alarm values from preferences showalarm = self.gconf_client.get('show_alarm') atime = self.gconf_client.get('show_alarm_at_time') adays = self.gconf_client.get('show_alarm_before_days') if showalarm: alarmDate = scheduler.get_alarm_timestamp(adays, atime, self.selectedDate) self.alarmbutton.set_date(alarmDate) def _determine_separator(self, model, iter, data=None): return model.get_value(iter, 1) == "---" def _populate_widgets_with_record(self): # Format the amount field if self.currentrecord.amount: self.amount.set_text(utils.float_to_currency(self.currentrecord.amount)) else: self.amount.set_text("") # Format the dueDate field dt = self.currentrecord.dueDate self.dueDate.set_date(dt) utils.select_combo_text(self.payee, self.currentrecord.payee) if self.currentrecord.category: actions = Actions() cat_name = self.currentrecord.category.name records = actions.get_categories(name=cat_name) if records: categoryname = records[0].name utils.select_combo_text(self.category, categoryname, 1) else: self.category.set_active(0) if self.currentrecord.notes: self.txtbuffer.set_text(self.currentrecord.notes) #self.chkPaid.set_active(self.currentrecord.Paid) if self.currentrecord.alarmDate: self.alarmbutton.set_date(self.currentrecord.alarmDate) def _populate_payee(self): """ Populates combobox with existing payees """ # Connects to the database actions = Actions() # List of payees from database payees = [] records = actions.get_bills() for rec in records: if rec.payee not in payees: payees.append(rec.payee) store = gtk.ListStore(gobject.TYPE_STRING) for payee in payees: store.append([payee]) self.payee.set_model(store) self.payeecompletion.set_model(store) self.payee.set_text_column(0) self.payeecompletion.set_text_column(0) self.payeeEntry = self.payee.child self.selectedText = '' def _get_payee(self): """ Extracts information typed into comboboxentry """ if self.payee.get_active_iter() is not None: model = self.payee.get_model() iteration = self.payee.get_active_iter() if iteration: return model.get_value(iteration, 0) else: return self.payeeEntry.get_text() def _populate_frequency(self): """ Populates combobox with allowable frequency. """ store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_INT) self.frequency.set_model(store) cell = gtk.CellRendererText() self.frequency.pack_start(cell, True) self.frequency.add_attribute(cell, 'text', 0) for i, frequency in enumerate([scheduler.SC_ONCE, scheduler.SC_MONTHLY, scheduler.SC_WEEKLY]): store.append([frequency, i]) # Set SC_ONCE as default self.frequency.set_active(0) self.on_frequency_changed(self.frequency) def _populate_category(self, categoryname=None): """ Populates combobox with existing categories """ # Connects to the database actions = Actions() # List of categories from database categories = [] records = actions.get_categories() ret = 0 empty_color = create_pixbuf() for rec in records: #if [rec['categoryname'], rec['id']] not in categories: #TODO: Better put color creation in a function color = rec.color and rec.color or '#000' categories.append([create_pixbuf(color=color), rec.name, int(rec.id)]) if categoryname and categoryname == rec.name: ret = len(categories) + 1 store = gtk.ListStore(gtk.gdk.Pixbuf, str, int) self.category.set_model(store) store.append([empty_color, _("None"), 0]) store.append([None, "---", -1]) for category in categories: store.append(category) store.append([None, "---", -1]) store.append([empty_color, _("New Category"), -2]) self.category.set_active(ret) return ret def _get_category(self): """ Extracts information typed into comboboxentry """ actions = Actions() if self.category.get_active_iter() is not None: model = self.category.get_model() iteration = self.category.get_active_iter() if iteration: name = model.get_value(iteration, 1) else: name = None if not name or name == _("None"): return None records = actions.get_categories(name=name) if records: return records[0] else: return None def get_record(self): frequency = self.frequency.get_active_text() # Extracts the date off the calendar widget # Create datetime object selectedDate = self.dueDate.get_date() # End date if frequency != scheduler.SC_ONCE: endDate = self.endDate.get_date() # Notify user that the endDate is set in the past if endDate < selectedDate: endDate = selectedDate message = utils.Message() text = _("The end date is set to a date prior to the start date. Setting it to match the start date.") title = _("Date set in the past") message.ShowInfo(text=text, parentWindow=self.window, title=title) else: endDate = None #buffer = self.txtNotes.get_buffer() startiter, enditer = self.txtbuffer.get_bounds() sbuffer = self.txtbuffer.get_text(startiter, enditer) # Gets the payee payee = self._get_payee() # Gets the category category = self._get_category() # Gets the alarm date alarm = self.alarmbutton.get_date() or None # Validate form if not payee.strip(): return None if self.amount.get_text().strip(): amount = utils.currency_to_float(self.amount.get_text()) else: amount = None if self.currentrecord is None: # Verify how many bills will be inserted # this will only work for new bills records = [] # Figures out how many times we're repeating this bill days = scheduler.get_schedule_date( frequency, selectedDate, endDate) for day in days: if alarm: alarm = self.__get_alarm_date(day) rec = Bill(payee=payee, amount=amount, dueDate=day, alarmDate=alarm, notes=sbuffer, repeats=False) # Bill repeats... if len(days) > 1: rec.repeats = True # ... and has a category. if category: rec.category = category records.append(rec) return records else: # Edit existing bill self.currentrecord.payee = payee self.currentrecord.dueDate = selectedDate self.currentrecord.amount = amount self.currentrecord.notes = sbuffer self.currentrecord.alarmDate = alarm if category: self.currentrecord.category = category #return the bill return [self.currentrecord] def on_frequency_changed(self, widget): startDate = self.dueDate.get_date() endDate = self.endDate.get_date() frequency = widget.get_active_text() if frequency == scheduler.SC_ONCE: self.endDate.set_sensitive(False) else: self.endDate.set_sensitive(True) if startDate > endDate: self.endDate.set_date(self.dueDate.get_date()) def on_edit_categories_clicked(self, button, new = False): category = None # if new == True, a simpler categories dialog pops up self.window.category = self.category categories = CategoriesDialog(parent = self.window, new = new) ret = categories.run() if ret == gtk.RESPONSE_OK: #TODO: We should handle the saving in the dialog itself. # the category hasn't been saved yet... so save it. if new: categories._on_savebutton_clicked(None) category = categories.currentrecord categories.destroy() # Always re-populate the categories dropdown widget, regardless if # newer category was added. If something was returned, select it. if category: self._populate_category(category.name) else: self._populate_category() return ret def on_category_changed(self, combobox): index = self.category.get_active() model = self.category.get_model() if index == len(model) - 1: self.category.set_active(self.category_index_before) self.on_edit_categories_clicked(combobox, True) self.category_index_before = index def on_amount_insert_text(self, entry, string, len, position): for char in string: if char not in self.allowed_digts: print "Invalid Character: %s" % char entry.emit_stop_by_name("insert-text") gtk.gdk.beep() return def on_save_clicked(self, widget): message = utils.Message() startDate = self.dueDate.get_date() endDate = self.endDate.get_date() if not self._get_payee().strip() and \ not self.amount.get_text().strip(): message.ShowError(_("\"%s\" and \"%s\" are required fields.") \ % (_("Payee"), _("Amount")), self.window) self.payee.grab_focus() elif not self._get_payee().strip(): message.ShowError(_("\"%s\" is required field.") % _("Payee"), self.window) self.payee.grab_focus() elif not self.amount.get_text().strip(): message.ShowError(_("\"%s\" is required field.") % _("Amount"), self.window) self.amount.grab_focus() elif self.endDate.get_sensitive() and startDate > endDate: message.ShowError(_("The end date is set to a date prior to the start date."), self.window) else: self.window.response(gtk.RESPONSE_ACCEPT) def _on_datepicker_date_changed(self, widget, args): startDate = self.dueDate.get_date() endDate = self.endDate.get_date() if widget == self.dueDate: if self.endDate.get_sensitive() and startDate > endDate: # Update endDate to be equal to dueDate self.endDate.set_date(self.dueDate.get_date()) else: if self.endDate.get_sensitive(): if startDate > endDate: # Update endDate to be equal to dueDate self.endDate.set_date(self.dueDate.get_date()) if self.alarmbutton.get_date(): # Extracts the date off the datepicker widget alarmDate = self.__get_alarm_date(self.dueDate.get_date()) self.alarmbutton.set_date(alarmDate) def __get_alarm_date(self, date): # Use alarm values from preferences atime = self.gconf_client.get('show_alarm_at_time') adays = self.gconf_client.get('show_alarm_before_days') return scheduler.get_alarm_timestamp(adays, atime, date)