Esempio n. 1
0
class ComboSearchFilter(SearchFilter):
    """
    - a label
    - a combo with a set of predefined item to select from
    """
    __gtype_name__ = 'ComboSearchFilter'

    def __init__(self, label='', values=None):
        """
        Create a new ComboSearchFilter object.
        @param name: name of the search filter
        @param values: items to put in the combo, see
            L{kiwi.ui.widgets.combo.ProxyComboBox.prefill}
        """
        SearchFilter.__init__(self, label=label)
        label = gtk.Label(label)
        self.pack_start(label, False, False)
        label.show()
        self.title_label = label

        self.combo = ProxyComboBox()
        if values:
            self.combo.prefill(values)
        self.combo.connect('content-changed', self._on_combo__content_changed)
        self.pack_start(self.combo, False, False, 6)
        self.combo.show()

    #
    # SearchFilter
    #

    def get_state(self):
        return NumberQueryState(filter=self,
                                value=self.combo.get_selected_data())

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.combo

    #
    # Public API
    #

    def select(self, data):
        """
        selects an item in the combo
        @param data: what to select
        """
        self.combo.select(data)

    #
    # Callbacks
    #

    def _on_combo__content_changed(self, mode):
        self.emit('changed')
Esempio n. 2
0
    def _add_options(self, attr, pos):
        combo = ProxyComboBox()
        label = gtk.Label(attr.attribute.description)

        # This dictionary is populated with the purpose of tests
        self._widgets[attr.attribute.description] = combo
        self.attr_table.attach(label, 0, 1, pos, pos + 1, 0, 0, 0, 0)
        self.attr_table.attach(combo, 1, 2, pos, pos + 1, 0, gtk.EXPAND | gtk.FILL, 0, 0)
        self.attr_table.show_all()
        self._fill_options(combo, attr)
        combo.connect('changed', self._on_combo_selection__changed)
Esempio n. 3
0
    def _add_options(self, attr, pos):
        combo = ProxyComboBox()
        label = gtk.Label(attr.attribute.description)

        # This dictionary is populated with the purpose of tests
        self._widgets[attr.attribute.description] = combo
        self.attr_table.attach(label, 0, 1, pos, pos + 1, 0, 0, 0, 0)
        self.attr_table.attach(combo, 1, 2, pos, pos + 1, 0, gtk.EXPAND | gtk.FILL, 0, 0)
        self.attr_table.show_all()
        self._fill_options(combo, attr)
        combo.connect('changed', self._on_combo_selection__changed)
Esempio n. 4
0
class Editor(Gtk.HBox):
    """Base class for field editors

    Subclasses must define a list of operations and a datatype
    """

    operations = []
    data_type = None

    def __init__(self, store, field, other_fields):
        """
        :param store: a storm store if its needed
        :param field: the field that is being edited
        :param other_fields: other fields available for math operations
        """
        assert len(self.operations)

        self._store = store
        self._other_fields = other_fields
        self._oper = None
        self._field = field
        super(Editor, self).__init__(spacing=6)
        self.operations_combo = ProxyComboBox()
        self.pack_start(self.operations_combo, True, True, 0)
        self.operations_combo.connect('changed', self._on_operation_changed)
        for oper in self.operations:
            self.operations_combo.append_item(oper.label, oper)
        self.operations_combo.select(self.operations[0])
        self.show_all()

    def set_field(self, field):
        assert field.data_type == self.data_type
        self._field = field
        self._oper.set_field(field)

    def _on_operation_changed(self, combo):
        if self._oper is not None:
            # Remove previous operation
            self.remove(self._oper)

        self._oper = combo.get_selected()(self._store, self._field,
                                          self._other_fields)
        self.pack_start(self._oper, True, True, 0)

    def apply_operation(self, item):
        return self._oper.apply_operation(item)
Esempio n. 5
0
class Editor(Gtk.HBox):
    """Base class for field editors

    Subclasses must define a list of operations and a datatype
    """

    operations = []
    data_type = None

    def __init__(self, store, field, other_fields):
        """
        :param store: a storm store if its needed
        :param field: the field that is being edited
        :param other_fields: other fields available for math operations
        """
        assert len(self.operations)

        self._store = store
        self._other_fields = other_fields
        self._oper = None
        self._field = field
        super(Editor, self).__init__(spacing=6)
        self.operations_combo = ProxyComboBox()
        self.pack_start(self.operations_combo, True, True, 0)
        self.operations_combo.connect('changed', self._on_operation_changed)
        for oper in self.operations:
            self.operations_combo.append_item(oper.label, oper)
        self.operations_combo.select(self.operations[0])
        self.show_all()

    def set_field(self, field):
        assert field.data_type == self.data_type
        self._field = field
        self._oper.set_field(field)

    def _on_operation_changed(self, combo):
        if self._oper is not None:
            # Remove previous operation
            self.remove(self._oper)

        self._oper = combo.get_selected()(self._store, self._field,
                                          self._other_fields)
        self.pack_start(self._oper, True, True, 0)

    def apply_operation(self, item):
        return self._oper.apply_operation(item)
Esempio n. 6
0
    def _add_options(self, attr, pos):
        combo = ProxyComboBox()
        label = Gtk.Label(label=attr.attribute.description + u':')
        label.set_alignment(xalign=1, yalign=0.5)

        # This dictionary is populated with the purpose of tests
        self._widgets[attr.attribute.description] = combo
        # Use 3 labels per row
        row_pos = pos / 3
        col_pos = 2 * (pos % 3)
        self.attr_table.attach(label, col_pos, col_pos + 1, row_pos, row_pos + 1,
                               Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0)
        self.attr_table.attach(combo, col_pos + 1, col_pos + 2, row_pos, row_pos + 1,
                               Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0, 0, 0)
        self.attr_table.show_all()
        self._fill_options(combo, attr)
        combo.connect('changed', self._on_combo_selection__changed)
Esempio n. 7
0
class ComboSearchFilter(SearchFilter):
    """
    - a label
    - a combo with a set of predefined item to select from
    """
    __gtype_name__ = 'ComboSearchFilter'

    def __init__(self, label='', values=None):
        """
        Create a new ComboSearchFilter object.
        :param label: label of the search filter
        :param values: items to put in the combo, see
            :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill`
        """
        self._block_updates = False
        SearchFilter.__init__(self, label=label)
        label = gtk.Label(label)
        self.pack_start(label, False, False)
        label.show()
        self.title_label = label

        self.combo = ProxyComboBox()
        if values:
            self.update_values(values)
        self.combo.connect('content-changed', self._on_combo__content_changed)
        self.pack_start(self.combo, False, False, 6)
        self.combo.show()

    #
    # SearchFilter
    #

    def get_state(self):
        value = self.combo.get_selected_data()
        state = NumberQueryState(filter=self, value=value)
        if hasattr(value, 'id'):
            state.value_id = value.id
        return state

    def set_state(self, value, value_id=None):
        if value_id is not None:
            for item in self.combo.get_model_items().values():
                if item is None:
                    continue
                if item.id == value_id:
                    value = item
                    break
        self.select(value)

    def update_values(self, values):
        self._block_updates = True
        self.combo.prefill(values)
        self._block_updates = False

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.combo

    def get_description(self):
        desc = ''
        data = self.combo.get_selected_data()
        if data is not None:
            desc += self.combo.get_selected_label()
            return '%s %s' % (
                self.title_label.get_text(),
                desc,
            )

    #
    # Public API
    #

    def select(self, data):
        """
        selects an item in the combo
        :param data: what to select
        """
        self.combo.select(data)

    #
    # Callbacks
    #

    def _on_combo__content_changed(self, mode):
        if not self._block_updates:
            self.emit('changed')
Esempio n. 8
0
class MassEditorWidget(Gtk.HBox):
    _editors = {
        currency: DecimalEditor,
        Decimal: DecimalEditor,
        str: UnicodeEditor,
        datetime.date: DateEditor,
        object: ObjectEditor,
    }

    def __init__(self, store, fields, results):
        self._store = store
        self._editor = None
        self._fields = fields
        self._results = results
        super(MassEditorWidget, self).__init__(spacing=6)
        self._setup_widgets()

    def _filter_fields(self, data_type):
        return [f for f in self._fields if f.data_type == data_type]

    def _setup_editor(self, field):
        # Reuse editor if its possible (not when data_type is an object, since
        # that requires changing the reference values)
        if (self._editor and field.data_type is not object and
                self._editor.data_type == field.data_type):
            self._editor.set_field(field)
            return

        if self._editor:
            self.editor_placeholder.remove(self._editor)

        other_fields = self._filter_fields(field.data_type)
        klass = self._editors[field.data_type]
        self._editor = klass(self._store, field, other_fields)
        self.editor_placeholder.add(self._editor)

    def _setup_widgets(self):
        label = Gtk.Label(label=_('Update'))
        self.pack_start(label, False, False, 0)
        self.field_combo = ProxyComboBox()
        self.field_combo.connect('changed', self._on_field_combo__changed)
        self.pack_start(self.field_combo, False, False, 0)
        self.editor_placeholder = Gtk.EventBox()
        self.pack_start(self.editor_placeholder, False, False, 0)
        self.apply_button = Gtk.Button(stock=Gtk.STOCK_APPLY)
        self.apply_button.connect('clicked', self._on_apply_button__clicked)
        self.pack_start(self.apply_button, False, False, 0)

        for field in self._fields:
            # Don't let the user edit unique fields for now
            if field.unique or field.read_only:
                continue
            self.field_combo.append_item(field.label, field)
        self.field_combo.select_item_by_position(0)

    def _apply(self):
        marker('Updating values')
        for i in self._results:
            self._editor.apply_operation(i)
            self._results.refresh(i)
        marker('Done updating values')

    #
    # Public API
    #

    def get_changed_objects(self):
        """Returns a set of all the changed objects"""
        objs = set()
        for field in self._fields:
            objs.update(field.new_values.keys())
        return objs

    #
    # BaseEditorSlave
    #

    def confirm(self, dialog):
        marker('Saving data')

        objs = self.get_changed_objects()
        total = len(objs)
        for i, obj in enumerate(objs):
            for field in self._fields:
                field.save_value(obj)
                yield i, total
            # Flush soon, so that any errors triggered by database constraints
            # pop up.
            self._store.flush()

        marker('Done saving data')

    #
    #   Callbacks
    #

    def _on_field_combo__changed(self, combo):
        self._setup_editor(combo.get_selected())

    def _on_apply_button__clicked(self, button):
        self._apply()
Esempio n. 9
0
class MassEditorWidget(Gtk.HBox):
    _editors = {
        currency: DecimalEditor,
        Decimal: DecimalEditor,
        str: UnicodeEditor,
        datetime.date: DateEditor,
        object: ObjectEditor,
    }

    def __init__(self, store, fields, results):
        self._store = store
        self._editor = None
        self._fields = fields
        self._results = results
        super(MassEditorWidget, self).__init__(spacing=6)
        self._setup_widgets()

    def _filter_fields(self, data_type):
        return [f for f in self._fields if f.data_type == data_type]

    def _setup_editor(self, field):
        # Reuse editor if its possible (not when data_type is an object, since
        # that requires changing the reference values)
        if (self._editor and field.data_type is not object
                and self._editor.data_type == field.data_type):
            self._editor.set_field(field)
            return

        if self._editor:
            self.editor_placeholder.remove(self._editor)

        other_fields = self._filter_fields(field.data_type)
        klass = self._editors[field.data_type]
        self._editor = klass(self._store, field, other_fields)
        self.editor_placeholder.add(self._editor)

    def _setup_widgets(self):
        label = Gtk.Label(label=_('Update'))
        self.pack_start(label, False, False, 0)
        self.field_combo = ProxyComboBox()
        self.field_combo.connect('changed', self._on_field_combo__changed)
        self.pack_start(self.field_combo, False, False, 0)
        self.editor_placeholder = Gtk.EventBox()
        self.pack_start(self.editor_placeholder, False, False, 0)
        self.apply_button = Gtk.Button(stock=Gtk.STOCK_APPLY)
        self.apply_button.connect('clicked', self._on_apply_button__clicked)
        self.pack_start(self.apply_button, False, False, 0)

        for field in self._fields:
            # Don't let the user edit unique fields for now
            if field.unique or field.read_only:
                continue
            self.field_combo.append_item(field.label, field)
        self.field_combo.select_item_by_position(0)

    def _apply(self):
        marker('Updating values')
        for i in self._results:
            self._editor.apply_operation(i)
            self._results.refresh(i)
        marker('Done updating values')

    #
    # Public API
    #

    def get_changed_objects(self):
        """Returns a set of all the changed objects"""
        objs = set()
        for field in self._fields:
            objs.update(field.new_values.keys())
        return objs

    #
    # BaseEditorSlave
    #

    def confirm(self, dialog):
        marker('Saving data')

        objs = self.get_changed_objects()
        total = len(objs)
        for i, obj in enumerate(objs):
            for field in self._fields:
                field.save_value(obj)
                yield i, total
            # Flush soon, so that any errors triggered by database constraints
            # pop up.
            self._store.flush()

        marker('Done saving data')

    #
    #   Callbacks
    #

    def _on_field_combo__changed(self, combo):
        self._setup_editor(combo.get_selected())

    def _on_apply_button__clicked(self, button):
        self._apply()
Esempio n. 10
0
class ComboSearchFilter(SearchFilter):
    """
    - a label
    - a combo with a set of predefined item to select from
    """
    __gtype_name__ = 'ComboSearchFilter'

    def __init__(self, label='', values=None):
        """
        Create a new ComboSearchFilter object.
        :param label: label of the search filter
        :param values: items to put in the combo, see
            :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill`
        """
        self._block_updates = False
        SearchFilter.__init__(self, label=label)
        label = gtk.Label(label)
        self.pack_start(label, False, False)
        label.show()
        self.title_label = label

        self.combo = ProxyComboBox()
        if values:
            self.update_values(values)
        self.combo.connect('content-changed', self._on_combo__content_changed)
        self.pack_start(self.combo, False, False, 6)
        self.combo.show()

    #
    # SearchFilter
    #

    def get_state(self):
        value = self.combo.get_selected_data()
        state = NumberQueryState(filter=self,
                                 value=value)
        if hasattr(value, 'id'):
            state.value_id = value.id
        return state

    def set_state(self, value, value_id=None):
        if value_id is not None:
            for item in self.combo.get_model_items().values():
                if item is None:
                    continue
                if item.id == value_id:
                    value = item
                    break
        self.select(value)

    def update_values(self, values):
        self._block_updates = True
        self.combo.prefill(values)
        self._block_updates = False

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.combo

    def get_description(self):
        desc = ''
        data = self.combo.get_selected_data()
        if data is not None:
            desc += self.combo.get_selected_label()
            return '%s %s' % (self.title_label.get_text(), desc,)

    #
    # Public API
    #

    def select(self, data):
        """
        selects an item in the combo
        :param data: what to select
        """
        self.combo.select(data)

    #
    # Callbacks
    #

    def _on_combo__content_changed(self, mode):
        if not self._block_updates:
            self.emit('changed')
Esempio n. 11
0
class ChartDialog(gtk.Window):
    def __init__(self):
        self._js_data = None
        self._js_options = None
        self._current = None

        gtk.Window.__init__(self)
        self.set_size_request(800, 480)

        self.vbox = gtk.VBox()
        self.add(self.vbox)
        self.vbox.show()

        hbox = gtk.HBox()
        self.vbox.pack_start(hbox, False, False, 6)
        hbox.show()

        label = gtk.Label('Period:')
        hbox.pack_start(label, False, False, 6)
        label.show()

        self.chart_type = ProxyComboBox()
        self.chart_type.connect(
            'content-changed',
            self._on_chart_type__content_changed)
        hbox.pack_start(self.chart_type, False, False, 6)
        self.chart_type.show()

        self.period_values = ProxyComboBox()
        self.period_values.connect(
            'content-changed',
            self._on_period_values__content_changed)
        hbox.pack_start(self.period_values, False, False, 6)
        self.period_values.show()

        self._view = WebView()
        self._view.get_view().connect(
            'load-finished',
            self._on_view__document_load_finished)
        self.vbox.pack_start(self._view, True, True)

        self.results = ObjectList()
        self.results.connect(
            'row-activated',
            self._on_results__row_activated)
        self.vbox.pack_start(self.results, True, True)

        self._setup_daemon()

    @api.async
    def _setup_daemon(self):
        daemon = yield start_daemon()
        self._daemon_uri = daemon.base_uri

        proxy = daemon.get_client()
        yield proxy.callRemote('start_webservice')

        self.chart_type.prefill([
            ('Year', 'YearlyPayments'),
            ('Month', 'MonthlyPayments'),
            ('Day', 'DailyPayments'),
        ])

    @api.async
    def _invoke_chart(self, chart_type_name, **report_kwargs):
        def _get_chart_url(**kwargs):
            params = []
            for key, value in kwargs.items():
                params.append(key + '=' + str(value))
            return '%s/web/chart.json?%s' % (
                self._daemon_uri, '&'.join(params))

        url = _get_chart_url(type=chart_type_name, **report_kwargs)
        page = yield getPage(url)
        data = json.loads(page)
        api.asyncReturn(data)

    def _render_chart(self, chart_class, response):
        self._render_javascript(chart_class, response)
        self._render_objectlist(chart_class, response)

    def _render_javascript(self, chart_class, response):
        ticks = [item['short_title'] for item in response['items']]

        self._js_data = response['data']

        options = {}
        options['description'] = response['description']
        options['series'] = [dict(label=c['title']) for c in chart_class.columns][1:]
        options['xaxis_ticks'] = ticks
        self._js_options = options

        self._view.load_uri('%s/web/static/chart.html' % (
                            self._daemon_uri,))

    def _render_objectlist(self, chart_class, response):
        columns = []
        for kwargs in chart_class.columns:
            kwargs = kwargs.copy()
            name = kwargs.pop('name')
            columns.append(Column(name, **kwargs))
        self.results.set_columns(columns)

        items = []
        for item in response['items']:
            settable = Settable(**item)
            settable.chart_class = chart_class
            items.append(settable)
        self.results.add_list(items, clear=True)
        self.results.show()

    def _load_finished(self):
        self._view.js_function_call(
            "plot", self._js_data, self._js_options)

    @api.async
    def _show_one(self, chart_type_name, start, end):
        chart_class = get_chart_class(chart_type_name)
        report_kwargs = dict(start=start.strftime('%Y-%m-%d'),
                             end=end.strftime('%Y-%m-%d'))

        # Get chart datab
        response = yield self._invoke_chart(chart_type_name, **report_kwargs)
        self._render_chart(chart_class, response)

    def _update_period_values(self):
        chart_type_name = self.chart_type.get_selected()
        chart_class = get_chart_class(chart_type_name)
        values = chart_class.get_combo_labels()
        self.period_values.prefill(values)

    #
    # Callbacks
    #

    def _on_view__document_load_finished(self, view, frame):
        self._load_finished()

    def _on_chart_type__content_changed(self, combo):
        self._update_period_values()

    def _on_period_values__content_changed(self, combo):
        kind = self.chart_type.get_selected()
        value = self.period_values.get_selected()
        if not value:
            return
        start, end = value
        if self._current == (kind, start, end):
            return
        self._show_one(kind, start, end)
        self._current = kind, start, end

    def _on_results__row_activated(self, results, item):
        chart_type_name = item.chart_class.__name__
        if chart_type_name == 'YearlyPayments':
            start = localdate(item.year, 1, 1).date()
            end = localdate(item.year, 12, 31).date()
            chart_type_name = 'MonthlyPayments'
        elif chart_type_name == 'MonthlyPayments':
            start = localdate(item.year, item.month, 1).date()
            end = start + relativedelta(days=31)
            chart_type_name = 'DailyPayments'
        else:
            return
        self._show_one(chart_type_name, start, end)
Esempio n. 12
0
class DateSearchFilter(SearchFilter):
    """
    A filter which helps you to search by a date interval.
    Can be customized through add_option.
    """
    __gtype_name__ = 'DateSearchFilter'

    class Type(enum):
        (USER_DAY, USER_INTERVAL) = range(100, 102)

    def __init__(self, label=''):
        """
        Create a new DateSearchFilter object.
        :param label: name of the search filter
        """
        self._options = {}
        SearchFilter.__init__(self, label=label)
        self.title_label = Gtk.Label(label=label)
        self.pack_start(self.title_label, False, False, 0)
        self.title_label.show()

        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)
        self.mode.show()

        self.from_label = Gtk.Label(label=_("From:"))
        self.pack_start(self.from_label, False, False, 0)
        self.from_label.show()

        self.start_date = ProxyDateEntry()
        self._start_changed_id = self.start_date.connect(
            'content-changed', self._on_start_date__changed)
        self.pack_start(self.start_date, False, False, 6)
        self.start_date.show()

        self.to_label = Gtk.Label(label=_("To:"))
        self.pack_start(self.to_label, False, False, 0)
        self.to_label.show()

        self.end_date = ProxyDateEntry()
        self._end_changed_id = self.end_date.connect(
            'content-changed', self._on_end_date__changed)
        self.pack_start(self.end_date, False, False, 6)
        self.end_date.show()

        self.add_custom_options()

        for option in (Any, Today, Yesterday, LastWeek, LastMonth):
            self.add_option(option)

        self.mode.select_item_by_position(0)

    #
    # SearchFilter
    #

    def get_state(self):
        start = self.start_date.get_date()
        end = self.end_date.get_date()
        if start == end:
            return DateQueryState(filter=self, date=start)
        return DateIntervalQueryState(filter=self, start=start, end=end)

    def set_state(self, start, end=None):
        self.start_date.set_date(start)
        if end is not None:
            self.end_date.set_date(end)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = ''
        start_date = self.start_date.get_date()
        end_date = self.end_date.get_date()
        if start_date:
            if end_date and start_date != end_date:
                desc += ' %s %s %s %s' % (
                    _(u'from'),
                    start_date.strftime('%x'),
                    _(u'to'),
                    end_date.strftime('%x'),
                )

            else:
                desc += start_date.strftime('%x')
        if desc:
            return '%s %s' % (
                self.get_title_label().get_text(),
                desc,
            )

    #
    # Public API
    #

    def clear_options(self):
        """
        Removes all previously added options
        """
        self._options = {}
        self.mode.clear()

    def add_option(self, option_type, position=-2):
        """
        Adds a date option
        :param option_type: option to add
        :type option_type: a :class:`DateSearchOption` subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option

    def add_option_fixed(self, name, date, position=-2):
        """
        Adds a fixed option, eg one for which date is not
        possible to modify.
        :param name: name of the option
        :param date: fixed data
        :param position: position to add the option at
        """
        option_type = type('', (FixedDateSearchOption, ),
                           dict(name=name, date=date))
        self.add_option(option_type, position=position)

    def add_option_fixed_interval(self, name, start, end, position=-2):
        """
        Adds a fixed option interval, eg one for which the dates are not
        possible to modify.
        :param name: name of the option
        :param start: start of the fixed interval
        :param end: end of the fixed interval
        :param position: position to add the option at
        """
        option_type = type('', (FixedIntervalSearchOption, ),
                           dict(name=name, start=start, end=end))
        self.add_option(option_type, position=position)

    def add_custom_options(self):
        """Adds the custom options 'Custom day' and 'Custom interval' which
        let the user define its own interval dates.
        """
        pos = len(self.mode) + 1
        for name, option_type in [
            (_('Custom day'), DateSearchFilter.Type.USER_DAY),
            (_('Custom interval'), DateSearchFilter.Type.USER_INTERVAL)
        ]:

            self.mode.insert_item(pos, name, option_type)
            pos += 1

    def get_start_date(self):
        """
        Get the start date.
        :returns: start date
        :rtype: datetime.date or None
        """
        return self.start_date.get_date()

    def get_end_date(self):
        """
        Get the end date.
        :returns: end date
        :rtype: datetime.date or None
        """
        return self.end_date.get_date()

    def set_use_date_entries(self, use_date_entries):
        """
        Toggles the visibility of the user selectable date entries
        :param use_date_entries:
        """
        self.from_label.props.visible = use_date_entries
        self.to_label.props.visible = use_date_entries
        self.start_date.props.visible = use_date_entries
        self.end_date.props.visible = use_date_entries

    def select(self, data=None, position=None):
        """
        selects an item in the combo
        Data or position can be sent in. If nothing
        is sent in the first item will be selected, if any

        :param data: data to select
        :param position: position of data to select
        """
        if data is not None and position is not None:
            raise TypeError("You can't send in both data and position")

        if data is None and position is None:
            position = 0

        if position is not None:
            if len(self.mode):
                self.mode.select_item_by_position(position)
        elif data:
            self.mode.select(data)

    #
    # Private
    #

    def _update_dates(self):
        # This is called when we change mode
        date_type = self.mode.get_selected_data()
        if date_type is None:
            return

        # If we switch to a user selectable day, make sure that
        # both dates are set to today
        if date_type == DateSearchFilter.Type.USER_DAY:
            today = datetime.date.today()
            self.start_date.set_date(today)
            self.end_date.set_date(today)
        # And for user interval, set start to today and to tomorrow
        elif date_type == DateSearchFilter.Type.USER_INTERVAL:
            today = datetime.date.today()
            self.start_date.set_date(today)
            self.end_date.set_date(today + datetime.timedelta(days=1))
        # Finally for pre-defined ones let the DateSearchOption decide what the
        # values are going to be, these dates are not user editable so
        # we don't need to do any checking.
        else:
            option = self._options.get(date_type)
            assert option, (date_type, self._options)
            start_date, end_date = option.get_interval()
            self.start_date.set_date(start_date)
            self.end_date.set_date(end_date)

    def _update_sensitivity(self):
        date_type = self.mode.get_selected_data()
        enabled = date_type == DateSearchFilter.Type.USER_INTERVAL
        self.to_label.set_sensitive(enabled)
        self.end_date.set_sensitive(enabled)

        enabled = (date_type == DateSearchFilter.Type.USER_INTERVAL
                   or date_type == DateSearchFilter.Type.USER_DAY)
        self.from_label.set_sensitive(enabled)
        self.start_date.set_sensitive(enabled)

    def _internal_set_start_date(self, date):
        self.start_date.handler_block(self._start_changed_id)
        self.start_date.set_date(date)
        self.start_date.handler_unblock(self._start_changed_id)

    def _internal_set_end_date(self, date):
        self.end_date.handler_block(self._end_changed_id)
        self.end_date.set_date(date)
        self.end_date.handler_unblock(self._end_changed_id)

    def _restore_date_validation(self):
        self.start_date.set_valid()
        self.end_date.set_valid()

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, mode):
        self._update_dates()
        self._update_sensitivity()
        self._restore_date_validation()
        self.emit('changed')

    def _on_start_date__changed(self, start_date):
        date_type = self.mode.get_selected_data()
        start = start_date.get_date()
        # For user days, just make sure that the date entries
        # always are in sync
        if date_type == DateSearchFilter.Type.USER_DAY:
            if start is None:
                self.start_date.set_invalid(_(u'Invalid date'))
            else:
                self.start_date.set_valid()
                self._internal_set_end_date(start)
        # Make sure that we cannot select a start date after
        # the end date, be nice and increase the end date if
        # the start date happen to be the same
        elif date_type == DateSearchFilter.Type.USER_INTERVAL:
            end = self.end_date.get_date()
            if start is None:
                self.start_date.set_invalid(_(u'Invalid date'))
                return
            if end and start >= end:
                self._internal_set_end_date(start + datetime.timedelta(days=1))

            self.start_date.set_valid()

    def _on_end_date__changed(self, end_date):
        date_type = self.mode.get_selected_data()
        # We don't need to do anything for user day, since
        # this the end date widget is disabled
        if date_type == DateSearchFilter.Type.USER_DAY:
            pass
        # Make sure that we cannot select an end date before
        # the start date, be nice and decrease the start date if
        # the end date happen to be the same
        elif date_type == DateSearchFilter.Type.USER_INTERVAL:
            start = self.start_date.get_date()
            end = end_date.get_date()
            if end is None:
                self.end_date.set_invalid(_(u'Invalid date'))
            else:
                self.end_date.set_valid()

            if start and end and end <= start:
                self._internal_set_start_date(end - datetime.timedelta(days=1))
Esempio n. 13
0
class DateSearchFilter(SearchFilter):
    """
    A filter which helps you to search by a date interval.
    Can be customized through add_option.
    """
    __gtype_name__ = 'DateSearchFilter'
    class Type(enum):
        (USER_DAY,
         USER_INTERVAL) = range(100, 102)

    def __init__(self, label=''):
        """
        Create a new DateSearchFilter object.
        @param label: name of the search filter
        """
        self._options = {}
        SearchFilter.__init__(self, label=label)
        self.title_label = gtk.Label(label)
        self.pack_start(self.title_label, False, False)
        self.title_label.show()

        self.mode = ProxyComboBox()
        self.mode.connect(
            'content-changed',
            self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)
        self.mode.show()

        self.from_label = gtk.Label(_("From:"))
        self.pack_start(self.from_label, False, False)
        self.from_label.show()

        self.start_date = ProxyDateEntry()
        self._start_changed_id = self.start_date.connect(
            'content-changed', self._on_start_date__changed)
        self.pack_start(self.start_date, False, False, 6)
        self.start_date.show()

        self.to_label = gtk.Label(_("To:"))
        self.pack_start(self.to_label, False, False)
        self.to_label.show()

        self.end_date = ProxyDateEntry()
        self._end_changed_id = self.end_date.connect(
            'content-changed', self._on_end_date__changed)
        self.pack_start(self.end_date, False, False, 6)
        self.end_date.show()

        self.add_custom_options()

        for option in (Any, Today, Yesterday, LastWeek, LastMonth):
            self.add_option(option)

        self.mode.select_item_by_position(0)

    #
    # SearchFilter
    #

    def get_state(self):
        start = self.start_date.get_date()
        end = self.end_date.get_date()
        if start == end:
            return DateQueryState(filter=self, date=start)
        return DateIntervalQueryState(filter=self, start=start, end=end)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = ''
        start_date = self.start_date.get_date()
        end_date = self.end_date.get_date()
        if start_date:
            if end_date and start_date != end_date:
                desc += ' %s %s %s %s' % (_(u'from'), start_date.strftime('%x'),
                                          _(u'to'), end_date.strftime('%x'),)

            else:
                 desc += start_date.strftime('%x')
        if desc:
            return '%s %s' % (self.get_title_label().get_text(), desc,)

    #
    # Public API
    #

    def clear_options(self):
        """
        Removes all previously added options
        """
        self._options = {}
        self.mode.clear()

    def add_option(self, option_type, position=-2):
        """
        Adds a date option
        @param option_type: option to add
        @type option_type: a L{DateSearchOption} subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option

    def add_option_fixed(self, name, date, position=-2):
        """
        Adds a fixed option, eg one for which date is not
        possible to modify.
        @param name: name of the option
        @param date: fixed data
        @param position: position to add the option at
        """
        option_type = type('', (FixedDateSearchOption,),
                           dict(name=name, date=date))
        self.add_option(option_type, position=position)


    def add_option_fixed_interval(self, name, start, end, position=-2):
        """
        Adds a fixed option interval, eg one for which the dates are not
        possible to modify.
        @param name: name of the option
        @param start: start of the fixed interval
        @param end: end of the fixed interval
        @param position: position to add the option at
        """
        option_type = type('', (FixedIntervalSearchOption,),
                           dict(name=name, start=start, end=end))
        self.add_option(option_type, position=position)

    def add_custom_options(self):
        """Adds the custom options 'Custom day' and 'Custom interval' which
        let the user define its own interval dates.
        """
        pos = len(self.mode) + 1
        for name, option_type in [
            (_('Custom day'), DateSearchFilter.Type.USER_DAY),
            (_('Custom interval'), DateSearchFilter.Type.USER_INTERVAL)]:

            self.mode.insert_item(pos, name, option_type)
            pos += 1

    def get_start_date(self):
        """
        Get the start date.
        @returns: start date
        @rtype: datetime.date or None
        """
        return self.start_date.get_date()

    def get_end_date(self):
        """
        Get the end date.
        @returns: end date
        @rtype: datetime.date or None
        """
        return self.end_date.get_date()

    def set_use_date_entries(self, use_date_entries):
        """
        Toggles the visibility of the user selectable date entries
        @param use_date_entries:
        """
        self.from_label.props.visible = use_date_entries
        self.to_label.props.visible = use_date_entries
        self.start_date.props.visible = use_date_entries
        self.end_date.props.visible = use_date_entries

    def select(self, data=None, position=None):
        """
        selects an item in the combo
        Data or position can be sent in. If nothing
        is sent in the first item will be selected, if any

        @param data: data to select
        @param position: position of data to select
        """
        if data is not None and position is not None:
            raise TypeError("You can't send in both data and position")

        if data is None and position is None:
            position = 0

        if position is not None:
            if len(self.mode):
                self.mode.select_item_by_position(position)
        elif data:
            self.mode.select(data)

    #
    # Private
    #

    def _update_dates(self):
        # This is called when we change mode
        date_type = self.mode.get_selected_data()
        if date_type is None:
            return

        # If we switch to a user selectable day, make sure that
        # both dates are set to today
        if date_type == DateSearchFilter.Type.USER_DAY:
            today = datetime.date.today()
            self.start_date.set_date(today)
            self.end_date.set_date(today)
        # And for user interval, set start to today and to tomorrow
        elif date_type == DateSearchFilter.Type.USER_INTERVAL:
            today = datetime.date.today()
            self.start_date.set_date(today)
            self.end_date.set_date(today + datetime.timedelta(days=1))
        # Finally for pre-defined ones let the DateSearchOption decide what the
        # values are going to be, these dates are not user editable so
        # we don't need to do any checking.
        else:
            option = self._options.get(date_type)
            assert option, (date_type, self._options)
            start_date, end_date = option.get_interval()
            self.start_date.set_date(start_date)
            self.end_date.set_date(end_date)

    def _update_sensitivity(self):
        date_type = self.mode.get_selected_data()
        enabled = date_type == DateSearchFilter.Type.USER_INTERVAL
        self.to_label.set_sensitive(enabled)
        self.end_date.set_sensitive(enabled)

        enabled = (date_type == DateSearchFilter.Type.USER_INTERVAL or
                   date_type == DateSearchFilter.Type.USER_DAY)
        self.from_label.set_sensitive(enabled)
        self.start_date.set_sensitive(enabled)

    def _internal_set_start_date(self, date):
        self.start_date.handler_block(self._start_changed_id)
        self.start_date.set_date(date)
        self.start_date.handler_unblock(self._start_changed_id)

    def _internal_set_end_date(self, date):
        self.end_date.handler_block(self._end_changed_id)
        self.end_date.set_date(date)
        self.end_date.handler_unblock(self._end_changed_id)

    def _restore_date_validation(self):
        self.start_date.set_valid()
        self.end_date.set_valid()

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, mode):
        self._update_dates()
        self._update_sensitivity()
        self._restore_date_validation()
        self.emit('changed')

    def _on_start_date__changed(self, start_date):
        date_type = self.mode.get_selected_data()
        start = start_date.get_date()
        # For user days, just make sure that the date entries
        # always are in sync
        if date_type == DateSearchFilter.Type.USER_DAY:
            if start is None:
                self.start_date.set_invalid(_(u'Invalid date'))
            else:
                self.start_date.set_valid()
                self._internal_set_end_date(start)
        # Make sure that we cannot select a start date after
        # the end date, be nice and increase the end date if
        # the start date happen to be the same
        elif date_type == DateSearchFilter.Type.USER_INTERVAL:
            end = self.end_date.get_date()
            if start is None:
                self.start_date.set_invalid(_(u'Invalid date'))
                return
            if end and start >= end:
                self._internal_set_end_date(start + datetime.timedelta(days=1))

            self.start_date.set_valid()

    def _on_end_date__changed(self, end_date):
        date_type = self.mode.get_selected_data()
        # We don't need to do anything for user day, since
        # this the end date widget is disabled
        if date_type == DateSearchFilter.Type.USER_DAY:
            pass
        # Make sure that we cannot select an end date before
        # the start date, be nice and decrease the start date if
        # the end date happen to be the same
        elif date_type == DateSearchFilter.Type.USER_INTERVAL:
            start = self.start_date.get_date()
            end = end_date.get_date()
            if end is None:
                self.end_date.set_invalid(_(u'Invalid date'))
            else:
                self.end_date.set_valid()

            if start and end and end <= start:
                self._internal_set_start_date(end - datetime.timedelta(days=1))
Esempio n. 14
0
class AccountEditor(BaseEditor):
    """ Account Editor """
    gladefile = "AccountEditor"
    proxy_widgets = ['description', 'code']
    model_type = Account
    model_name = _('Account')

    def __init__(self, store, model=None, parent_account=None):
        self._last_account_type = None
        self._bank_number = -1
        self._bank_widgets = []
        self._bank_option_widgets = []
        self._option_fields = {}
        self._test_button = None
        self.existing = model is not None
        self.parent_account = parent_account
        self.bank_model = _TemporaryBankAccount()
        BaseEditor.__init__(self, store, model)

        action_area = self.main_dialog.action_area
        action_area.set_layout(gtk.BUTTONBOX_END)
        action_area.pack_start(self._test_button, expand=False, fill=False)
        action_area.set_child_secondary(self._test_button, True)
        self._test_button.show()

    #
    # BaseEditor hooks
    #

    def create_model(self, store):
        return Account(description=u"",
                       account_type=Account.TYPE_CASH,
                       store=store)

    def _setup_widgets(self):
        self._test_button = gtk.Button(_("Print a test bill"))
        self._test_button.connect('clicked',
                                  self._on_test_button__clicked)
        self.parent_accounts = AccountTree(with_code=False, create_mode=True)
        self.parent_accounts.connect('selection-changed',
                                     self._on_parent_accounts__selection_changed)
        self.tree_box.pack_start(self.parent_accounts)
        self.tree_box.reorder_child(self.parent_accounts, 0)

        if self.model == sysparam(self.store).IMBALANCE_ACCOUNT:
            self.account_type.set_sensitive(False)

        self.account_type.prefill(Account.account_type_descriptions)
        account_type = self.model.account_type

        self.parent_accounts.insert_initial(self.store,
                                            edited_account=self.model)
        if self.parent_account:
            account = self.parent_accounts.get_account_by_id(
                self.parent_account.id)
            self.parent_accounts.select(account)
            if not self.existing:
                account_type = account.account_type
        self.account_type.select(account_type)
        self.parent_accounts.show()

    def setup_proxies(self):
        self._setup_widgets()
        self.add_proxy(self.model, AccountEditor.proxy_widgets)

    def validate_confirm(self):
        if not self.model.description:
            return False
        account = self.parent_accounts.get_selected()
        if not account:
            return True
        return account.selectable

    def on_confirm(self):
        new_parent = self.parent_accounts.get_selected()
        if new_parent:
            new_parent = new_parent.account
        if new_parent != self.model:
            self.model.parent = new_parent
        self.model.account_type = self.account_type.get_selected()
        self._save_bank()

    def refresh_ok(self, value):
        BaseEditor.refresh_ok(self, value)

        account_type = self.account_type.get_selected()
        if account_type != Account.TYPE_BANK:
            value = False
        self._test_button.set_sensitive(value)

    # Private

    def _save_bank(self):
        bank_account = self.model.bank
        if not bank_account:
            bank_account = BankAccount(account=self.model,
                                       store=self.store)
        # FIXME: Who sets this to a str?
        bank_account.bank_account = unicode(self.bank_model.bank_account)
        bank_account.bank_branch = unicode(self.bank_model.bank_branch)
        if self._bank_number is not None:
            bank_account.bank_number = self.bank_model.bank_number

        self._save_bank_bill_options(bank_account)

    def _save_bank_bill_options(self, bank_account):
        for option, entry in self._option_fields.items():
            value = unicode(entry.get_text())
            bill_option = self.store.find(BillOption,
                                          bank_account=bank_account,
                                          option=option).one()
            if bill_option is None:
                bill_option = BillOption(store=self.store,
                                         bank_account=bank_account,
                                         option=option,
                                         value=value)
            bill_option.value = value

    def _add_widget(self, label, widget, options=False):
        n_rows = self.table.props.n_rows
        l = gtk.Label()
        l.set_markup(label)
        l.props.xalign = 1.0
        self.table.resize(n_rows + 1, 2)
        self.table.attach(
            l, 0, 1, n_rows, n_rows + 1,
            gtk.FILL, 0, 0, 0)
        self.table.attach(
            widget, 1, 2, n_rows, n_rows + 1,
            gtk.EXPAND | gtk.FILL, 0, 0, 0)
        if options:
            self._bank_option_widgets.extend([l, widget])
        else:
            self._bank_widgets.extend([l, widget])
        l.show()
        return l

    def _update_bank_type(self):
        self._remove_bank_option_widgets()

        bank_number = self.bank_type.get_selected()
        bank_info = None
        if bank_number:
            bank_info = get_bank_info_by_number(bank_number)

        self.bank_number = ProxyEntry()
        self.bank_number.props.data_type = int
        self.bank_number.set_sensitive(False)
        bank_number_lbl = self._add_widget(api.escape(_("Number:")),
                                           self.bank_number, options=True)

        self.bank_branch = ProxyEntry()
        self.bank_branch.connect('validate', self._on_bank_branch__validate,
                                 bank_info)
        self.bank_branch.props.data_type = 'str'
        self.bank_branch.props.mandatory = True
        self.bank_branch.model_attribute = "bank_branch"
        bank_branch_lbl = self._add_widget(api.escape(_("Agency:")),
                                           self.bank_branch, options=True)
        if bank_number is not None:
            bank_branch_lbl.show()
            self.bank_branch.show()
        else:
            bank_branch_lbl.hide()

        self.bank_account = ProxyEntry()
        self.bank_account.connect('validate', self._on_bank_account__validate,
                                  bank_info)
        self._add_widget(api.escape(_("Account:")),
                         self.bank_account, options=True)
        self.bank_account.model_attribute = "bank_account"
        self.bank_account.props.data_type = 'str'
        if bank_number is not None:
            self.bank_account.props.mandatory = True
        self.bank_account.show()

        attributes = ['bank_account', 'bank_branch',
                      'bank_number']
        if bank_number is not None:
            bank_number_lbl.show()
            self.bank_number.show()

            self.bank_model.bank_number = bank_number

            for i, option in enumerate(bank_info.get_extra_options()):
                name = 'option' + str(i)
                entry = ProxyEntry()
                entry.model_attribute = name
                setattr(self, name, entry)
                # Set the model attr too so it can be validated
                setattr(self.bank_model, name, u'')
                entry.props.data_type = 'str'
                entry.connect('validate', self._on_bank_option__validate,
                              bank_info, option)
                self._add_widget("<i>%s</i>:" % (api.escape(option.capitalize()), ),
                                 entry, options=True)
                entry.show()
                self._option_fields[option] = entry
                attributes.append(entry.model_attribute)
        else:
            bank_number_lbl.hide()

        self.bank_proxy = self.add_proxy(
            self.bank_model, attributes)
        self._fill_bank_account()

    def _fill_bank_account(self):
        if not self.model.bank:
            return

        self.bank_model.bank_branch = self.model.bank.bank_branch.encode('utf-8')
        self.bank_model.bank_account = self.model.bank.bank_account.encode('utf-8')
        self.bank_proxy.update('bank_branch')
        self.bank_proxy.update('bank_account')

        bill_options = list(self.store.find(BillOption,
                                            bank_account=self.model.bank))
        for bill_option in bill_options:
            if bill_option.option is None:
                continue
            field_entry = self._option_fields.get(bill_option.option)
            if field_entry:
                field_entry.set_text(bill_option.value)

    def _update_account_type(self, account_type):
        if not self.account_type.get_sensitive():
            return
        if account_type != Account.TYPE_BANK:
            self._remove_bank_widgets()
            self._remove_bank_option_widgets()
            self.code.set_sensitive(True)
            return

        self.code.set_sensitive(False)
        self.bank_type = ProxyComboBox()
        self._add_widget(api.escape(_("Bank:")), self.bank_type)
        self.bank_type.connect('content-changed',
                               self._on_bank_type__content_changed)
        self.bank_type.show()

        banks = [(_('Generic'), None)]
        banks.extend([(b.description,
                       b.bank_number) for b in get_all_banks()])
        self.bank_type.prefill(banks)

        if self.model.bank:
            try:
                self.bank_type.select(self.model.bank.bank_number)
            except KeyError:
                self.bank_type.select(None)

        self._update_bank_type()

    def _remove_bank_widgets(self):
        for widget in self._bank_widgets:
            widget.get_parent().remove(widget)
            widget.destroy()
        self.table.resize(5, 2)
        self._bank_widgets = []

    def _remove_bank_option_widgets(self):
        for widget in self._bank_option_widgets:
            widget.get_parent().remove(widget)
            widget.destroy()
        self.table.resize(5 + len(self._bank_widgets) / 2, 2)
        self._bank_option_widgets = []
        self._option_fields = {}

    def _print_test_bill(self):
        try:
            bank_info = get_bank_info_by_number(self.bank_model.bank_number)
        except NotImplementedError:
            info(_("This bank does not support printing of bills"))
            return
        kwargs = dict(
            valor_documento=12345.67,
            data_vencimento=datetime.date.today(),
            data_documento=datetime.date.today(),
            data_processamento=datetime.date.today(),
            nosso_numero=u'624533',
            numero_documento=u'1138',
            sacado=[_(u"Drawee"), _(u"Address"), _(u"Details")],
            cedente=_(u"Supplier"),
            demonstrativo=[_(u"Demonstration")],
            instrucoes=[_(u"Instructions")],
            agencia=self.bank_model.bank_branch,
            conta=self.bank_model.bank_account,
        )
        for opt in self.bank_model.options:
            kwargs[opt.option] = opt.value
        data = bank_info(**kwargs)
        print_report(BillTestReport, data)

    # Callbacks

    def _on_parent_accounts__selection_changed(self, objectlist, account):
        self.force_validation()

    def on_description__activate(self, entry):
        if self.validate_confirm():
            self.confirm()

    def on_description__validate(self, entry, text):
        if not text:
            return ValidationError(_("Account description cannot be empty"))

    def on_account_type__content_changed(self, account_type):
        account_type = account_type.get_selected()
        if self._last_account_type == account_type:
            return
        self._update_account_type(account_type)
        self._last_account_type = account_type

    def _on_bank_type__content_changed(self, bank_type):
        bank_number = bank_type.get_selected()
        if self._bank_number == bank_number:
            return
        self._update_bank_type()

        self._bank_number = bank_number

    def _on_bank_branch__validate(self, entry, value, bank_info):
        if bank_info:
            try:
                bank_info.validate_field(value)
            except BoletoException, e:
                return ValidationError(str(e))
Esempio n. 15
0
class ChartDialog(gtk.Window):
    def __init__(self):
        self._js_data = None
        self._js_options = None
        self._current = None

        gtk.Window.__init__(self)
        self.set_size_request(800, 480)

        self.vbox = gtk.VBox()
        self.add(self.vbox)
        self.vbox.show()

        hbox = gtk.HBox()
        self.vbox.pack_start(hbox, False, False, 6)
        hbox.show()

        label = gtk.Label('Period:')
        hbox.pack_start(label, False, False, 6)
        label.show()

        self.chart_type = ProxyComboBox()
        self.chart_type.connect('content-changed',
                                self._on_chart_type__content_changed)
        hbox.pack_start(self.chart_type, False, False, 6)
        self.chart_type.show()

        self.period_values = ProxyComboBox()
        self.period_values.connect('content-changed',
                                   self._on_period_values__content_changed)
        hbox.pack_start(self.period_values, False, False, 6)
        self.period_values.show()

        self._view = WebView()
        self._view.get_view().connect('load-finished',
                                      self._on_view__document_load_finished)
        self.vbox.pack_start(self._view, True, True)

        self.results = ObjectList()
        self.results.connect('row-activated', self._on_results__row_activated)
        self.vbox.pack_start(self.results, True, True)

        self._setup_daemon()

    @api. async
    def _setup_daemon(self):
        daemon = yield start_daemon()
        self._daemon_uri = daemon.base_uri

        proxy = daemon.get_client()
        yield proxy.callRemote('start_webservice')

        self.chart_type.prefill([
            ('Year', 'YearlyPayments'),
            ('Month', 'MonthlyPayments'),
            ('Day', 'DailyPayments'),
        ])

    @api. async
    def _invoke_chart(self, chart_type_name, **report_kwargs):
        def _get_chart_url(**kwargs):
            params = []
            for key, value in kwargs.items():
                params.append(key + '=' + str(value))
            return '%s/web/chart.json?%s' % (self._daemon_uri,
                                             '&'.join(params))

        url = _get_chart_url(type=chart_type_name, **report_kwargs)
        page = yield getPage(url)
        data = json.loads(page)
        api.asyncReturn(data)

    def _render_chart(self, chart_class, response):
        self._render_javascript(chart_class, response)
        self._render_objectlist(chart_class, response)

    def _render_javascript(self, chart_class, response):
        ticks = [item['short_title'] for item in response['items']]

        self._js_data = response['data']

        options = {}
        options['description'] = response['description']
        options['series'] = [
            dict(label=c['title']) for c in chart_class.columns
        ][1:]
        options['xaxis_ticks'] = ticks
        self._js_options = options

        self._view.load_uri('%s/web/static/chart.html' % (self._daemon_uri, ))

    def _render_objectlist(self, chart_class, response):
        columns = []
        for kwargs in chart_class.columns:
            kwargs = kwargs.copy()
            name = kwargs.pop('name')
            columns.append(Column(name, **kwargs))
        self.results.set_columns(columns)

        items = []
        for item in response['items']:
            settable = Settable(**item)
            settable.chart_class = chart_class
            items.append(settable)
        self.results.add_list(items, clear=True)
        self.results.show()

    def _load_finished(self):
        self._view.js_function_call("plot", self._js_data, self._js_options)

    @api. async
    def _show_one(self, chart_type_name, start, end):
        chart_class = get_chart_class(chart_type_name)
        report_kwargs = dict(start=start.strftime('%Y-%m-%d'),
                             end=end.strftime('%Y-%m-%d'))

        # Get chart datab
        response = yield self._invoke_chart(chart_type_name, **report_kwargs)
        self._render_chart(chart_class, response)

    def _update_period_values(self):
        chart_type_name = self.chart_type.get_selected()
        chart_class = get_chart_class(chart_type_name)
        values = chart_class.get_combo_labels()
        self.period_values.prefill(values)

    #
    # Callbacks
    #

    def _on_view__document_load_finished(self, view, frame):
        self._load_finished()

    def _on_chart_type__content_changed(self, combo):
        self._update_period_values()

    def _on_period_values__content_changed(self, combo):
        kind = self.chart_type.get_selected()
        value = self.period_values.get_selected()
        if not value:
            return
        start, end = value
        if self._current == (kind, start, end):
            return
        self._show_one(kind, start, end)
        self._current = kind, start, end

    def _on_results__row_activated(self, results, item):
        chart_type_name = item.chart_class.__name__
        if chart_type_name == 'YearlyPayments':
            start = localdate(item.year, 1, 1).date()
            end = localdate(item.year, 12, 31).date()
            chart_type_name = 'MonthlyPayments'
        elif chart_type_name == 'MonthlyPayments':
            start = localdate(item.year, item.month, 1).date()
            end = start + relativedelta(days=31)
            chart_type_name = 'DailyPayments'
        else:
            return
        self._show_one(chart_type_name, start, end)
Esempio n. 16
0
class StringSearchFilter(SearchFilter):
    """
    Contains:

      - a label
      - an entry

    :ivar entry: the entry
    :ivar label: the label
    """
    def __init__(self, label, chars=0, container=None):
        """
        Create a new StringSearchFilter object.
        :param label: label of the search filter
        :param chars: maximum number of chars used by the search entry
        """
        self._container = container
        SearchFilter.__init__(self, label=label)
        self.title_label = gtk.Label(label)
        self.pack_start(self.title_label, False, False)
        self.title_label.show()

        self._options = {}
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)

        self.entry = HintedEntry()
        self.entry.set_hint(_("Search"))
        self.entry.show_hint()
        self.entry.props.secondary_icon_sensitive = False
        data = environ.get_resource_string('stoq', 'pixmaps',
                                           'stoq-funnel-16x16.png')
        image = pixbuf_from_string(data)
        self.entry.set_icon_from_pixbuf(gtk.ENTRY_ICON_PRIMARY,
                                        image)
        self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_PRIMARY,
                                         _("Add a filter"))
        self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY,
                                       gtk.STOCK_CLEAR)
        self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_SECONDARY,
                                         _("Clear the search"))
        self.entry.connect("icon-release", self._on_entry__icon_release)
        self.entry.connect('activate', self._on_entry__activate)
        self.entry.connect('changed', self._on_entry__changed)
        if chars:
            self.entry.set_width_chars(chars)
        self.pack_start(self.entry, False, False, 6)
        self.entry.show()

        for option in (ContainsAll, ContainsExactly, DoesNotContain):
            self._add_option(option)
        self.mode.select_item_by_position(0)

    def _add_option(self, option_type, position=-2):
        option = option_type()
        num = abs(position)
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        self.emit('changed')

    def _on_entry__activate(self, entry):
        self.emit('changed')

    def _on_entry__changed(self, entry):
        entry.props.secondary_icon_sensitive = bool(entry.get_text())

    def _position_filter_menu(self, data):
        window = self.entry.get_icon_window(gtk.ENTRY_ICON_PRIMARY)
        x, y = window.get_origin()
        y += window.get_size()[1]
        border = self.entry.style_get_property('progress-border')
        if border is not None:
            y += border.bottom
        return (x, y, True)

    def _on_entry__icon_release(self, entry, icon_pos, event):
        if icon_pos == gtk.ENTRY_ICON_SECONDARY:
            entry.set_text("")
            entry.grab_focus()
            self.emit('changed')
        elif icon_pos == gtk.ENTRY_ICON_PRIMARY:
            # We don't need create popup filters if haven't search columns.
            if (not self._container or not hasattr(self._container, 'menu') or
                not self._container.menu):
                return
            self._container.menu.popup(None, None,
                                       self._position_filter_menu, 0, event.time)

    #
    # SearchFilter
    #

    def get_state(self):
        option = self.mode.get_selected_data()
        return StringQueryState(filter=self,
                                text=unicode(self.entry.get_text()),
                                mode=option and option.mode)

    def set_state(self, text, mode=None):
        self.entry.set_text(text)
        if mode is not None:
            self.mode.select_item_by_position(mode)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = self.entry.get_text()
        if desc:
            mode = self.mode.get_selected_label()
            return '%s %s "%s"' % (self.title_label.get_text(), mode, desc,)

    #
    # Public API
    #

    def enable_advanced(self):
        # Do not show the funnel icon if its an advanced filter
        self.entry.set_icon_from_pixbuf(gtk.ENTRY_ICON_PRIMARY, None)
        self.mode.show()

    def set_label(self, label):
        self.title_label.set_text(label)
Esempio n. 17
0
class ComboSearchFilter(SearchFilter):
    """
    - a label
    - a combo with a set of predefined item to select from
    """
    __gtype_name__ = 'ComboSearchFilter'

    def __init__(self, label='', values=None):
        """
        Create a new ComboSearchFilter object.
        :param label: label of the search filter
        :param values: items to put in the combo, see
            :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill`
        """
        self._block_updates = False
        SearchFilter.__init__(self, label=label)
        label = gtk.Label(label)
        self.pack_start(label, False, False)
        label.show()
        self.title_label = label

        # We create the mode, but it will only be added to this box when
        # enable_advanced is called
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        for option in (ComboEquals, ComboDifferent):
            self.add_option(option)
        self.mode.select_item_by_position(0)

        self.combo = ProxyComboBox()
        if values:
            self.update_values(values)
        self.combo.connect('content-changed', self._on_combo__content_changed)
        self.pack_start(self.combo, False, False, 6)
        self.combo.show()

    #
    # SearchFilter
    #

    def get_state(self):
        value = self.combo.get_selected_data()
        mode = self.mode.get_selected_data()
        state = NumberQueryState(filter=self, value=value, mode=mode.mode)
        if hasattr(value, 'id'):
            state.value_id = value.id
        return state

    def set_state(self, value, value_id=None, mode=None):
        if mode is None:
            mode = NumberQueryState.EQUALS
        if value_id is not None:
            for item in self.combo.get_model_items().values():
                if item is None:
                    continue
                if item.id == value_id:
                    value = item
                    break
        self.select(value)

    def update_values(self, values):
        self._block_updates = True
        self.combo.prefill(values)
        self._block_updates = False

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.combo

    def get_description(self):
        desc = ''
        data = self.combo.get_selected_data()
        if data is not None:
            desc += self.combo.get_selected_label()
            return '%s %s' % (self.title_label.get_text(), desc,)

    #
    # Public API
    #

    def add_option(self, option_type, position=0):
        """
        Adds an option
        :param option_type: option to add
        :type option_type: a :class:`ComboSearchOption` subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)

    def select(self, data):
        """
        selects an item in the combo
        :param data: what to select
        """
        self.combo.select(data)

    def enable_advanced(self):
        self.pack_start(self.mode, False, False, 6)
        self.reorder_child(self.mode, 1)
        self.mode.show()

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        if not self._block_updates:
            self.emit('changed')

    def _on_combo__content_changed(self, mode):
        if not self._block_updates:
            self.emit('changed')
Esempio n. 18
0
class StringSearchFilter(SearchFilter):
    """
    Contains:

      - a label
      - an entry

    :ivar entry: the entry
    :ivar label: the label
    """
    def __init__(self, label, chars=0, container=None):
        """
        Create a new StringSearchFilter object.
        :param label: label of the search filter
        :param chars: maximum number of chars used by the search entry
        """
        self._container = container
        SearchFilter.__init__(self, label=label)
        self.title_label = gtk.Label(label)
        self.pack_start(self.title_label, False, False)
        self.title_label.show()

        self._options = {}
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)

        self.entry = HintedEntry()
        self.entry.set_hint(_("Search"))
        self.entry.show_hint()
        self.entry.props.secondary_icon_sensitive = False
        self.entry.set_icon_from_stock(gtk.ENTRY_ICON_PRIMARY, gtk.STOCK_FIND)
        self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY,
                                       gtk.STOCK_CLEAR)
        self.entry.set_icon_tooltip_text(gtk.ENTRY_ICON_SECONDARY,
                                         _("Clear the search"))
        self.entry.connect("icon-release", self._on_entry__icon_release)
        self.entry.connect('activate', self._on_entry__activate)
        self.entry.connect('changed', self._on_entry__changed)
        if chars:
            self.entry.set_width_chars(chars)
        self.pack_start(self.entry, False, False, 6)
        self.entry.show()

        for option in (ContainsAll, ContainsExactly, DoesNotContain):
            self._add_option(option)
        self.mode.select_item_by_position(0)

    def _add_option(self, option_type, position=-2):
        option = option_type()
        num = abs(position)
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        self.emit('changed')

    def _on_entry__activate(self, entry):
        self.emit('changed')

    def _on_entry__changed(self, entry):
        entry.props.secondary_icon_sensitive = bool(entry.get_text())

    def _position_filter_menu(self, data):
        window = self.entry.get_icon_window(gtk.ENTRY_ICON_PRIMARY)
        x, y = window.get_origin()
        y += window.get_size()[1]
        border = self.entry.style_get_property('progress-border')
        if border is not None:
            y += border.bottom
        return (x, y, True)

    def _on_entry__icon_release(self, entry, icon_pos, event):
        if icon_pos == gtk.ENTRY_ICON_SECONDARY:
            entry.set_text("")
            entry.grab_focus()
            self.emit('changed')
        elif icon_pos == gtk.ENTRY_ICON_PRIMARY:
            # We don't need create popup filters if haven't search columns.
            if (not self._container or not hasattr(self._container, 'menu')
                    or not self._container.menu):
                return
            self._container.menu.popup(None, None, self._position_filter_menu,
                                       0, event.time)

    #
    # SearchFilter
    #

    def get_state(self):
        option = self.mode.get_selected_data()
        return StringQueryState(filter=self,
                                text=self.entry.get_text(),
                                mode=option and option.mode)

    def set_state(self, text, mode=None):
        self.entry.set_text(text)
        if mode is not None:
            self.mode.select_item_by_position(mode)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = self.entry.get_text()
        if desc:
            mode = self.mode.get_selected_label()
            return '%s %s "%s"' % (
                self.title_label.get_text(),
                mode,
                desc,
            )

    #
    # Public API
    #

    def enable_advanced(self):
        self.mode.show()

    def set_label(self, label):
        self.title_label.set_text(label)
Esempio n. 19
0
class StringSearchFilter(SearchFilter):
    """
    Contains:

      - a label
      - an entry

    :ivar entry: the entry
    :ivar label: the label
    """

    __gtype_name__ = 'StringSearchFilter'

    def __init__(self, label, chars=0, container=None):
        """
        Create a new StringSearchFilter object.
        :param label: label of the search filter
        :param chars: maximum number of chars used by the search entry
        """
        self._container = container
        SearchFilter.__init__(self, label=label)
        self.title_label = Gtk.Label(label=label)
        self.pack_start(self.title_label, False, False, 0)
        self.title_label.show()

        self._options = {}
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)

        self.entry = Gtk.Entry()
        self.entry.set_placeholder_text(_("Search"))
        self.entry.props.secondary_icon_sensitive = False
        data = environ.get_resource_string('stoq', 'pixmaps',
                                           'stoq-funnel-16x16.png')
        image = pixbuf_from_string(data)
        self.entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, image)
        self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
                                         _("Add a filter"))
        self.entry.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY,
                                       Gtk.STOCK_CLEAR)
        self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY,
                                         _("Clear the search"))
        self.entry.connect("icon-release", self._on_entry__icon_release)
        self.entry.connect('activate', self._on_entry__activate)
        self.entry.connect('changed', self._on_entry__changed)
        if chars:
            self.entry.set_width_chars(chars)
        self.pack_start(self.entry, False, False, 6)
        self.entry.show()

        for option in [
                ContainsAll, ContainsExactly, IdenticalTo, DoesNotContain
        ]:
            self._add_option(option)
        self.mode.select_item_by_position(0)

    def _add_option(self, option_type):
        option = option_type()
        self.mode.append_item(option.name, option_type)
        self._options[option_type] = option

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        self.emit('changed')

    def _on_entry__activate(self, entry):
        self.emit('changed')

    def _on_entry__changed(self, entry):
        entry.props.secondary_icon_sensitive = bool(entry.get_text())

    def _position_filter_menu(self, data):
        window = self.entry.get_icon_window(Gtk.EntryIconPosition.PRIMARY)
        x, y = window.get_origin()
        y += window.get_size()[1]
        border = self.entry.style_get_property('progress-border')
        if border is not None:
            y += border.bottom
        return (x, y, True)

    def _on_entry__icon_release(self, entry, icon_pos, event):
        if icon_pos == Gtk.EntryIconPosition.SECONDARY:
            entry.set_text("")
            entry.grab_focus()
            self.emit('changed')
        elif icon_pos == Gtk.EntryIconPosition.PRIMARY:
            # We don't need create popup filters if haven't search columns.
            if (not self._container or not hasattr(self._container, 'menu')
                    or not self._container.menu):
                return
            self._container.menu.popup(None, None, None,
                                       self._position_filter_menu, 0,
                                       event.time)

    #
    # SearchFilter
    #

    def get_state(self):
        option = self.mode.get_selected_data()
        return StringQueryState(filter=self,
                                text=str(self.entry.get_text()),
                                mode=option and option.mode)

    def set_state(self, text, mode=None):
        self.entry.set_text(text)
        if mode is not None:
            self.mode.select_item_by_position(mode)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = self.entry.get_text()
        if desc:
            mode = self.mode.get_selected_label()
            return '%s %s "%s"' % (
                self.title_label.get_text(),
                mode,
                desc,
            )

    #
    # Public API
    #

    def enable_advanced(self):
        # Do not show the funnel icon if its an advanced filter
        self.entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, None)
        self.mode.show()

    def set_label(self, label):
        self.title_label.set_text(label)
Esempio n. 20
0
class AccountEditor(BaseEditor):
    """ Account Editor """
    gladefile = "AccountEditor"
    proxy_widgets = ['description', 'code']
    model_type = Account
    model_name = _('Account')

    def __init__(self, store, model=None, parent_account=None):
        self._last_account_type = None
        self._bank_number = -1
        self._bank_widgets = []
        self._bank_option_widgets = []
        self._option_fields = {}
        self._test_button = None
        self.existing = model is not None
        self.parent_account = parent_account
        self.bank_model = _TemporaryBankAccount()
        BaseEditor.__init__(self, store, model)

        action_area = self.main_dialog.action_area
        action_area.set_layout(Gtk.ButtonBoxStyle.END)
        action_area.pack_start(self._test_button, False, False, 0)
        action_area.set_child_secondary(self._test_button, True)
        self._test_button.show()

    #
    # BaseEditor hooks
    #

    def create_model(self, store):
        return Account(description=u"",
                       account_type=Account.TYPE_CASH,
                       store=store)

    def _setup_widgets(self):
        self._test_button = Gtk.Button(_("Print a test bill"))
        self._test_button.connect('clicked', self._on_test_button__clicked)
        self.parent_accounts = AccountTree(with_code=False, create_mode=True)
        self.parent_accounts.connect(
            'selection-changed', self._on_parent_accounts__selection_changed)
        self.tree_box.pack_start(self.parent_accounts, True, True, 0)
        self.tree_box.reorder_child(self.parent_accounts, 0)

        if sysparam.compare_object('IMBALANCE_ACCOUNT', self.model):
            self.account_type.set_sensitive(False)

        self.account_type.prefill(Account.account_type_descriptions)
        account_type = self.model.account_type

        self.parent_accounts.insert_initial(self.store,
                                            edited_account=self.model)
        if self.parent_account:
            account = self.parent_accounts.get_account_by_id(
                self.parent_account.id)
            self.parent_accounts.select(account)
            if not self.existing:
                account_type = account.account_type
        self.account_type.select(account_type)
        self.parent_accounts.show()

    def setup_proxies(self):
        self._setup_widgets()
        self.add_proxy(self.model, AccountEditor.proxy_widgets)

    def validate_confirm(self):
        if not self.model.description:
            return False
        account = self.parent_accounts.get_selected()
        if not account:
            return True
        return account.selectable

    def on_confirm(self):
        new_parent = self.parent_accounts.get_selected()
        if new_parent:
            new_parent = new_parent.account
        if new_parent != self.model:
            self.model.parent = new_parent
        self.model.account_type = self.account_type.get_selected()
        self._save_bank()

    def refresh_ok(self, value):
        BaseEditor.refresh_ok(self, value)

        account_type = self.account_type.get_selected()
        if account_type != Account.TYPE_BANK:
            value = False
        self._test_button.set_sensitive(value)

    # Private

    def _save_bank(self):
        bank_account = self.model.bank
        if not bank_account:
            bank_account = BankAccount(account=self.model, store=self.store)
        # FIXME: Who sets this to a str?
        bank_account.bank_account = unicode(self.bank_model.bank_account)
        bank_account.bank_branch = unicode(self.bank_model.bank_branch)
        if self._bank_number is not None:
            bank_account.bank_number = self.bank_model.bank_number

        self._save_bank_bill_options(bank_account)

    def _save_bank_bill_options(self, bank_account):
        for option, entry in self._option_fields.items():
            value = unicode(entry.get_text())
            bill_option = self.store.find(BillOption,
                                          bank_account=bank_account,
                                          option=option).one()
            if bill_option is None:
                bill_option = BillOption(store=self.store,
                                         bank_account=bank_account,
                                         option=option,
                                         value=value)
            bill_option.value = value

    def _add_widget(self, label, widget, options=False):
        n_rows = self.table.props.n_rows
        l = Gtk.Label()
        l.set_markup(label)
        l.props.xalign = 1.0
        self.table.resize(n_rows + 1, 2)
        self.table.attach(l, 0, 1, n_rows, n_rows + 1, Gtk.AttachOptions.FILL,
                          0, 0, 0)
        self.table.attach(widget, 1, 2, n_rows, n_rows + 1,
                          Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, 0,
                          0, 0)
        if options:
            self._bank_option_widgets.extend([l, widget])
        else:
            self._bank_widgets.extend([l, widget])
        l.show()
        return l

    def _update_bank_type(self):
        self._remove_bank_option_widgets()

        bank_number = self.bank_type.get_selected()
        bank_info = None
        if bank_number:
            bank_info = get_bank_info_by_number(bank_number)

        self.bank_number = ProxyEntry()
        self.bank_number.props.data_type = int
        self.bank_number.set_sensitive(False)
        bank_number_lbl = self._add_widget(api.escape(_("Number:")),
                                           self.bank_number,
                                           options=True)

        self.bank_branch = ProxyEntry()
        self.bank_branch.connect('validate', self._on_bank_branch__validate,
                                 bank_info)
        self.bank_branch.props.data_type = 'str'
        self.bank_branch.props.mandatory = True
        self.bank_branch.model_attribute = "bank_branch"
        bank_branch_lbl = self._add_widget(api.escape(_("Agency:")),
                                           self.bank_branch,
                                           options=True)
        if bank_number is not None:
            bank_branch_lbl.show()
            self.bank_branch.show()
        else:
            bank_branch_lbl.hide()

        self.bank_account = ProxyEntry()
        self.bank_account.connect('validate', self._on_bank_account__validate,
                                  bank_info)
        self._add_widget(api.escape(_("Account:")),
                         self.bank_account,
                         options=True)
        self.bank_account.model_attribute = "bank_account"
        self.bank_account.props.data_type = 'str'
        if bank_number is not None:
            self.bank_account.props.mandatory = True
        self.bank_account.show()

        attributes = ['bank_account', 'bank_branch', 'bank_number']
        if bank_number is not None:
            bank_number_lbl.show()
            self.bank_number.show()

            self.bank_model.bank_number = bank_number

            for i, option in enumerate(bank_info.get_extra_options()):
                name = 'option' + str(i)
                entry = ProxyEntry()
                entry.model_attribute = name
                setattr(self, name, entry)
                # Set the model attr too so it can be validated
                setattr(self.bank_model, name, u'')
                entry.props.data_type = 'str'
                entry.connect('validate', self._on_bank_option__validate,
                              bank_info, option)
                name = option.replace('_', ' ').capitalize()
                self._add_widget("<i>%s</i>:" % api.escape(name),
                                 entry,
                                 options=True)
                entry.show()
                self._option_fields[option] = entry
                attributes.append(entry.model_attribute)
        else:
            bank_number_lbl.hide()

        self.bank_proxy = self.add_proxy(self.bank_model, attributes)
        self._fill_bank_account()

    def _fill_bank_account(self):
        if not self.model.bank:
            return

        self.bank_model.bank_branch = self.model.bank.bank_branch.encode(
            'utf-8')
        self.bank_model.bank_account = self.model.bank.bank_account.encode(
            'utf-8')
        self.bank_proxy.update('bank_branch')
        self.bank_proxy.update('bank_account')

        bill_options = list(
            self.store.find(BillOption, bank_account=self.model.bank))
        for bill_option in bill_options:
            if bill_option.option is None:
                continue
            field_entry = self._option_fields.get(bill_option.option)
            if field_entry:
                field_entry.set_text(bill_option.value)

    def _update_account_type(self, account_type):
        if not self.account_type.get_sensitive():
            return
        if account_type != Account.TYPE_BANK:
            self._remove_bank_widgets()
            self._remove_bank_option_widgets()
            self.code.set_sensitive(True)
            return

        self.code.set_sensitive(False)
        self.bank_type = ProxyComboBox()
        self._add_widget(api.escape(_("Bank:")), self.bank_type)
        self.bank_type.connect('content-changed',
                               self._on_bank_type__content_changed)
        self.bank_type.show()

        banks = [(_('Generic'), None)]
        banks.extend([(b.description, b.bank_number) for b in get_all_banks()])
        self.bank_type.prefill(banks)

        if self.model.bank:
            try:
                self.bank_type.select(self.model.bank.bank_number)
            except KeyError:
                self.bank_type.select(None)

        self._update_bank_type()

    def _remove_bank_widgets(self):
        for widget in self._bank_widgets:
            widget.get_parent().remove(widget)
            widget.destroy()
        self.table.resize(5, 2)
        self._bank_widgets = []

    def _remove_bank_option_widgets(self):
        for widget in self._bank_option_widgets:
            widget.get_parent().remove(widget)
            widget.destroy()
        self.table.resize(5 + len(self._bank_widgets) / 2, 2)
        self._bank_option_widgets = []
        self._option_fields = {}

    def _print_test_bill(self):
        try:
            bank_info = get_bank_info_by_number(self.bank_model.bank_number)
        except NotImplementedError:
            info(_("This bank does not support printing of bills"))
            return
        kwargs = dict(
            valor_documento=12345.67,
            data_vencimento=datetime.date.today(),
            data_documento=datetime.date.today(),
            data_processamento=datetime.date.today(),
            nosso_numero=u'24533',
            numero_documento=u'1138',
            sacado=[_(u"Drawee"), _(u"Address"),
                    _(u"Details")],
            cedente=[_(u"Supplier"),
                     _(u"Address"),
                     _(u"Details"), ("CNPJ")],
            demonstrativo=[_(u"Demonstration")],
            instrucoes=[_(u"Instructions")],
            agencia=self.bank_model.bank_branch,
            conta=self.bank_model.bank_account,
        )
        for opt in self.bank_model.options:
            kwargs[opt.option] = opt.value
        data = bank_info(**kwargs)
        print_report(BillTestReport, data)

    # Callbacks

    def _on_parent_accounts__selection_changed(self, objectlist, account):
        self.force_validation()

    def on_description__activate(self, entry):
        if self.validate_confirm():
            self.confirm()

    def on_description__validate(self, entry, text):
        if not text:
            return ValidationError(_("Account description cannot be empty"))

    def on_account_type__content_changed(self, account_type):
        account_type = account_type.get_selected()
        if self._last_account_type == account_type:
            return
        self._update_account_type(account_type)
        self._last_account_type = account_type

    def _on_bank_type__content_changed(self, bank_type):
        bank_number = bank_type.get_selected()
        if self._bank_number == bank_number:
            return
        self._update_bank_type()

        self._bank_number = bank_number

    def _on_bank_branch__validate(self, entry, value, bank_info):
        if bank_info:
            try:
                bank_info.validate_field(value)
            except BoletoException as e:
                return ValidationError(str(e))

    def _on_bank_account__validate(self, entry, value, bank_info):
        if bank_info:
            try:
                bank_info.validate_field(value)
            except BoletoException as e:
                return ValidationError(str(e))

    def _on_bank_option__validate(self, entry, value, bank_info, option):
        try:
            bank_info.validate_option(option, value)
        except BoletoException as e:
            return ValidationError(str(e))
        self.bank_model.set_option(option, value)

    def _on_test_button__clicked(self, button):
        self._print_test_bill()
Esempio n. 21
0
class NumberSearchFilter(SearchFilter):
    """
    A filter which helps you to search by a number interval.
    """
    __gtype_name__ = 'NumberSearchFilter'

    def __init__(self, label=''):
        """
        @param label: name of the search filter
        """

        self._options = {}

        SearchFilter.__init__(self, label=label)
        self.title_label = gtk.Label(label)
        self.title_label.set_alignment(1.0, 0.5)
        self.pack_start(self.title_label, False, False)
        self.title_label.show()

        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)
        self.mode.show()

        self.start = gtk.SpinButton(climb_rate=1.0)
        self.start.get_adjustment().step_increment = 1.0
        self.start.set_range(-sys.maxint-1, sys.maxint)
        self.pack_start(self.start, False, False, 6)
        self.start.show()

        self.and_label = gtk.Label(_("And"))
        self.pack_start(self.and_label, False, False)
        self.and_label.show()

        self.end = gtk.SpinButton(climb_rate=1.0)
        self.end.get_adjustment().step_increment = 1.0
        self.end.set_range(-sys.maxint-1, sys.maxint)
        self.pack_start(self.end, False, False, 6)
        self.end.show()

        for option in (LowerThan, EqualsTo, GreaterThan, Between):
            self.add_option(option)

        self.mode.select_item_by_position(0)

    #
    #   Private
    #

    def _update_visibility(self):
        option = self.mode.get_selected_data()
        numbers = option.numbers
        if numbers == 0:
            self.start.hide()
            self.and_label.hide()
            self.end.hide()
        elif numbers == 1:
            self.start.show()
            self.and_label.hide()
            self.end.hide()
        elif numbers == 2:
            self.start.show()
            self.and_label.show()
            self.end.show()


    #
    #   Callbacks
    #

    def _on_mode__content_changed(self, combo):
        self._update_visibility()

    #
    #   SearchFilter
    #

    def get_state(self):
        start_value = self.start.get_value_as_int()
        end_value = self.end.get_value_as_int()
        option = self.mode.get_selected_data()

        start, end = option().get_interval(start_value, end_value)
        return NumberIntervalQueryState(filter=self, start=start, end=end)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode


    #
    #   Public API
    #

    def add_option(self, option_type, position=-2):
        """
        Adds a date option
        @param option_type: option to add
        @type option_type: a L{NumberSearchOption} subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option
Esempio n. 22
0
class ComboSearchFilter(SearchFilter):
    """
    - a label
    - a combo with a set of predefined item to select from
    """
    __gtype_name__ = 'ComboSearchFilter'

    def __init__(self, label='', values=None):
        """
        Create a new ComboSearchFilter object.
        @param name: name of the search filter
        @param values: items to put in the combo, see
            L{kiwi.ui.widgets.combo.ProxyComboBox.prefill}
        """
        SearchFilter.__init__(self, label=label)
        label = gtk.Label(label)
        self.pack_start(label, False, False)
        label.show()
        self.title_label = label

        self.combo = ProxyComboBox()
        if values:
            self.combo.prefill(values)
        self.combo.connect('content-changed', self._on_combo__content_changed)
        self.pack_start(self.combo, False, False, 6)
        self.combo.show()

    #
    # SearchFilter
    #

    def get_state(self):
        return NumberQueryState(filter=self,
                                value=self.combo.get_selected_data())

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.combo

    def get_description(self):
        desc = ''
        data = self.combo.get_selected_data()
        if data is not None:
            desc += self.combo.get_selected_label()
            return '%s %s' % (self.title_label.get_text(), desc,)

    #
    # Public API
    #

    def select(self, data):
        """
        selects an item in the combo
        @param data: what to select
        """
        self.combo.select(data)

    #
    # Callbacks
    #

    def _on_combo__content_changed(self, mode):
        self.emit('changed')
Esempio n. 23
0
class StringSearchFilter(SearchFilter):
    """
    Contains:

      - a label
      - an entry

    :ivar entry: the entry
    :ivar label: the label
    """

    __gtype_name__ = 'StringSearchFilter'

    def __init__(self, label, chars=0, container=None):
        """
        Create a new StringSearchFilter object.
        :param label: label of the search filter
        :param chars: maximum number of chars used by the search entry
        """
        self._container = container
        SearchFilter.__init__(self, label=label)
        self.title_label = Gtk.Label(label=label)
        self.pack_start(self.title_label, False, False, 0)
        self.title_label.show()

        self._options = {}
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)

        self.entry = Gtk.Entry()
        self.entry.set_placeholder_text(_("Search"))
        self.entry.props.secondary_icon_sensitive = False
        self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,
                                           'fa-filter-symbolic')
        self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.PRIMARY,
                                         _("Add a filter"))
        self.entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
                                           'edit-clear-symbolic')
        self.entry.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY,
                                         _("Clear the search"))
        self.entry.connect("icon-release", self._on_entry__icon_release)
        self.entry.connect('activate', self._on_entry__activate)
        self.entry.connect('changed', self._on_entry__changed)
        if chars:
            self.entry.set_width_chars(chars)
        self.pack_start(self.entry, False, False, 6)
        self.entry.show()

        # Default filter will be only contains all. When advanced filter is enabled it will add
        # other options
        self._add_option(ContainsAll)
        self.mode.select_item_by_position(0)

    def _add_option(self, option_type):
        option = option_type()
        self.mode.append_item(option.name, option_type)
        self._options[option_type] = option

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        self.emit('changed')

    def _on_entry__activate(self, entry):
        self.emit('changed')

    def _on_entry__changed(self, entry):
        entry.props.secondary_icon_sensitive = bool(entry.get_text())

    def _position_filter_menu(self, data):
        window = self.entry.get_icon_window(Gtk.EntryIconPosition.PRIMARY)
        x, y = window.get_origin()
        y += window.get_size()[1]
        border = self.entry.style_get_property('progress-border')
        if border is not None:
            y += border.bottom
        return (x, y, True)

    def _on_entry__icon_release(self, entry, icon_pos, event):
        if icon_pos == Gtk.EntryIconPosition.SECONDARY:
            entry.set_text("")
            entry.grab_focus()
            self.emit('changed')
        elif icon_pos == Gtk.EntryIconPosition.PRIMARY:
            # We don't need create popup filters if haven't search columns.
            if (not self._container or not hasattr(self._container, 'menu') or
                    not self._container.menu):
                return
            self._container.menu.popup(None, None, None,
                                       self._position_filter_menu, 0, event.time)

    #
    # SearchFilter
    #

    def get_state(self):
        option = self.mode.get_selected_data()
        return StringQueryState(filter=self,
                                text=str(self.entry.get_text()),
                                mode=option and option.mode)

    def set_state(self, text, mode=None):
        self.entry.set_text(text)
        for i in self.mode.get_model_items().values():
            if i.mode == mode:
                self.mode.select_item_by_data(i)
                break

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = self.entry.get_text()
        if desc:
            mode = self.mode.get_selected_label()
            return '%s %s "%s"' % (self.title_label.get_text(), mode, desc,)

    #
    # Public API
    #

    def enable_advanced(self):
        # Do not show the funnel icon if its an advanced filter
        self.entry.set_icon_from_pixbuf(Gtk.EntryIconPosition.PRIMARY, None)
        for option in [ContainsExactly, IdenticalTo, DoesNotContain]:
            self._add_option(option)
        self.mode.show()

    def set_label(self, label):
        self.title_label.set_text(label)
Esempio n. 24
0
class ComboSearchFilter(SearchFilter):
    """
    - a label
    - a combo with a set of predefined item to select from
    """
    __gtype_name__ = 'ComboSearchFilter'

    def __init__(self, label='', values=None):
        """
        Create a new ComboSearchFilter object.
        :param label: label of the search filter
        :param values: items to put in the combo, see
            :class:`kiwi.ui.widgets.combo.ProxyComboBox.prefill`
        """
        self._block_updates = False
        SearchFilter.__init__(self, label=label)
        label = Gtk.Label(label=label)
        self.pack_start(label, False, False, 0)
        label.show()
        self.title_label = label

        # We create the mode, but it will only be added to this box when
        # enable_advanced is called
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        for option in (ComboEquals, ComboDifferent):
            self.add_option(option)
        self.mode.select_item_by_position(0)

        self.combo = ProxyComboBox()
        if values:
            self.update_values(values)
        self.combo.connect('content-changed', self._on_combo__content_changed)
        self.pack_start(self.combo, False, False, 6)
        self.combo.show()

    #
    # SearchFilter
    #

    def get_state(self):
        value = self.combo.get_selected_data()
        mode = self.mode.get_selected_data()
        state = NumberQueryState(filter=self, value=value, mode=mode.mode)
        if hasattr(value, 'id'):
            state.value_id = value.id
        return state

    def set_state(self, value, value_id=None, mode=None):
        if mode is None:
            mode = NumberQueryState.EQUALS
        if value_id is not None:
            for item in self.combo.get_model_items().values():
                if item is None:
                    continue
                # Filter can come as a string or as a FilterItem object
                item_id = item if isinstance(item, str) else item.id
                if item_id == value_id:
                    value = item
                    break
        self.select(value)

    def update_values(self, values):
        self._block_updates = True
        self.combo.prefill(values)
        self._block_updates = False

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.combo

    def get_description(self):
        desc = ''
        data = self.combo.get_selected_data()
        if data is not None:
            desc += self.combo.get_selected_label()
            return '%s %s' % (
                self.title_label.get_text(),
                desc,
            )

    #
    # Public API
    #

    def add_option(self, option_type, position=0):
        """
        Adds an option
        :param option_type: option to add
        :type option_type: a :class:`ComboSearchOption` subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)

    def select(self, data):
        """
        selects an item in the combo
        :param data: what to select
        """
        self.combo.select(data)

    def enable_advanced(self):
        self.pack_start(self.mode, False, False, 6)
        self.reorder_child(self.mode, 1)
        self.mode.show()

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        if not self._block_updates:
            self.emit('changed')

    def _on_combo__content_changed(self, mode):
        if not self._block_updates:
            self.emit('changed')
Esempio n. 25
0
class StringSearchFilter(SearchFilter):
    """
    - a label
    - an entry
    @ivar entry: the entry
    @ivar label: the label
    """
    def __init__(self, label, chars=0):
        """
        Create a new StringSearchFilter object.
        @param label: label of the search filter
        @param chars: maximum number of chars used by the search entry
        """
        SearchFilter.__init__(self, label=label)
        self.title_label = gtk.Label(label)
        self.pack_start(self.title_label, False, False)
        self.title_label.show()

        self._options = {}
        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)

        self.entry = gtk.Entry()
        self.entry.connect('activate', self._on_entry__activate)
        if chars:
            self.entry.set_width_chars(chars)
        self.pack_start(self.entry, False, False, 6)
        self.entry.show()

        for option in (Contains, DoesNotContain):
            self._add_option(option)
        self.mode.select_item_by_position(0)

    def _add_option(self, option_type, position=-2):
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option

    #
    # Callbacks
    #

    def _on_mode__content_changed(self, combo):
        self.emit('changed')

    def _on_entry__activate(self, entry):
        self.emit('changed')

    #
    # SearchFilter
    #

    def get_state(self):
        option = self.mode.get_selected_data()
        return StringQueryState(filter=self,
                                text=self.entry.get_text(),
                                mode=option.mode)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = self.entry.get_text()
        if desc:
            mode = self.mode.get_selected_label()
            return '%s %s "%s"' % (self.title_label.get_text(), mode, desc,)

    #
    # Public API
    #

    def enable_advaced(self):
        self.mode.show()

    def set_label(self, label):
        self.title_label.set_text(label)
Esempio n. 26
0
class NumberSearchFilter(SearchFilter):
    """
    A filter which helps you to search by a number interval.
    """
    __gtype_name__ = 'NumberSearchFilter'

    def __init__(self, label=''):
        """
        Create a new NumberSearchFilter object.
        :param label: name of the search filter
        """

        self._options = {}

        SearchFilter.__init__(self, label=label)
        self.title_label = Gtk.Label(label=label)
        self.title_label.set_alignment(1.0, 0.5)
        self.pack_start(self.title_label, False, False, 0)
        self.title_label.show()

        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)
        self.mode.show()

        self.start = Gtk.SpinButton(climb_rate=1.0)
        self.start.get_adjustment().step_increment = 1.0
        self.start.set_range(-MAX_INT - 1, MAX_INT)
        self.pack_start(self.start, False, False, 6)
        self.start.show()
        self.start.connect_after('activate', self._on_entry__activate)

        self.and_label = Gtk.Label(label=_("And"))
        self.pack_start(self.and_label, False, False, 0)
        self.and_label.show()

        self.end = Gtk.SpinButton(climb_rate=1.0)
        self.end.get_adjustment().step_increment = 1.0
        self.end.set_range(-MAX_INT - 1, MAX_INT)
        self.pack_start(self.end, False, False, 6)
        self.end.show()
        self.end.connect_after('activate', self._on_entry__activate)

        for option in (LowerThan, EqualsTo, GreaterThan, Between):
            self.add_option(option)

        self.mode.select_item_by_position(0)

    def set_digits(self, digits):
        """
        Number of decimal place to be displayed
        :param digits: number of decimal places
        """
        self.start.set_digits(digits)
        self.end.set_digits(digits)

    #
    #   Private
    #

    def _update_visibility(self):
        option = self.mode.get_selected_data()
        numbers = option.numbers
        if numbers == 0:
            self.start.hide()
            self.and_label.hide()
            self.end.hide()
        elif numbers == 1:
            self.start.show()
            self.and_label.hide()
            self.end.hide()
        elif numbers == 2:
            self.start.show()
            self.and_label.show()
            self.end.show()

    #
    #   Callbacks
    #

    def _on_entry__activate(self, entry):
        self.emit('changed')

    def _on_mode__content_changed(self, combo):
        self._update_visibility()
        self.emit('changed')

    #
    #   SearchFilter
    #

    def get_state(self):
        # Using Decimals for better precision.
        start_value = Decimal("%.2f" % self.start.get_value())
        end_value = Decimal("%.2f" % self.end.get_value())
        option = self.mode.get_selected_data()

        start, end = option().get_interval(start_value, end_value)
        return NumberIntervalQueryState(filter=self, start=start, end=end)

    def set_state(self, start, end):
        self.start.set_value(start)
        self.end.set_value(end)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = ''
        option = self.mode.get_selected_data()
        if option is not None:
            desc += option.name
            if option.numbers > 0:
                start = self.start.get_value_as_int()
                if option.numbers == 1:
                    desc += ' %d' % start
                elif option.numbers == 2:
                    end = self.end.get_value_as_int()
                    desc += ' %d %s %d' % (
                        start,
                        self.and_label.get_text(),
                        end,
                    )
        if desc:
            return '%s %s' % (self.get_title_label().get_text(), desc)

    #
    #   Public API
    #

    def add_option(self, option_type, position=-2):
        """
        Adds a date option
        :param option_type: option to add
        :type option_type: a :class:`NumberSearchOption` subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option
Esempio n. 27
0
class NumberSearchFilter(SearchFilter):
    """
    A filter which helps you to search by a number interval.
    """
    __gtype_name__ = 'NumberSearchFilter'

    def __init__(self, label=''):
        """
        Create a new NumberSearchFilter object.
        @param label: name of the search filter
        """

        self._options = {}

        SearchFilter.__init__(self, label=label)
        self.title_label = gtk.Label(label)
        self.title_label.set_alignment(1.0, 0.5)
        self.pack_start(self.title_label, False, False)
        self.title_label.show()

        self.mode = ProxyComboBox()
        self.mode.connect('content-changed', self._on_mode__content_changed)
        self.pack_start(self.mode, False, False, 6)
        self.mode.show()

        self.start = gtk.SpinButton(climb_rate=1.0)
        self.start.get_adjustment().step_increment = 1.0
        self.start.set_range(-sys.maxint-1, sys.maxint)
        self.pack_start(self.start, False, False, 6)
        self.start.show()
        self.start.connect_after('activate', self._on_entry__activate)

        self.and_label = gtk.Label(_("And"))
        self.pack_start(self.and_label, False, False)
        self.and_label.show()

        self.end = gtk.SpinButton(climb_rate=1.0)
        self.end.get_adjustment().step_increment = 1.0
        self.end.set_range(-sys.maxint-1, sys.maxint)
        self.pack_start(self.end, False, False, 6)
        self.end.show()
        self.end.connect_after('activate', self._on_entry__activate)

        for option in (LowerThan, EqualsTo, GreaterThan, Between):
            self.add_option(option)

        self.mode.select_item_by_position(0)

    def set_digits(self, digits):
        """
        Number of decimal place to be displayed
        @param digits: number of decimal places
        """
        self.start.set_digits(digits)
        self.end.set_digits(digits)

    #
    #   Private
    #

    def _update_visibility(self):
        option = self.mode.get_selected_data()
        numbers = option.numbers
        if numbers == 0:
            self.start.hide()
            self.and_label.hide()
            self.end.hide()
        elif numbers == 1:
            self.start.show()
            self.and_label.hide()
            self.end.hide()
        elif numbers == 2:
            self.start.show()
            self.and_label.show()
            self.end.show()


    #
    #   Callbacks
    #

    def _on_entry__activate(self, entry):
        self.emit('changed')

    def _on_mode__content_changed(self, combo):
        self._update_visibility()
        self.emit('changed')

    #
    #   SearchFilter
    #

    def get_state(self):
        # Using Decimals for better precision.
        start_value = Decimal("%.2f" % self.start.get_value())
        end_value = Decimal("%.2f" % self.end.get_value())
        option = self.mode.get_selected_data()

        start, end = option().get_interval(start_value, end_value)
        return NumberIntervalQueryState(filter=self, start=start, end=end)

    def get_title_label(self):
        return self.title_label

    def get_mode_combo(self):
        return self.mode

    def get_description(self):
        desc = ''
        option = self.mode.get_selected_data()
        if option is not None:
            desc += option.name
            if option.numbers > 0:
                start = self.start.get_value_as_int()
                if option.numbers == 1:
                    desc += ' %d' % start
                elif option.numbers == 2:
                    end = self.end.get_value_as_int()
                    desc += ' %d %s %d' % (start, self.and_label.get_text(), end,)
        if desc:
            return '%s %s' % (self.get_title_label().get_text(), desc)

    #
    #   Public API
    #

    def add_option(self, option_type, position=-2):
        """
        Adds a date option
        @param option_type: option to add
        @type option_type: a L{NumberSearchOption} subclass
        """
        option = option_type()
        num = len(self.mode) + position
        self.mode.insert_item(num, option.name, option_type)
        self._options[option_type] = option