Example #1
0
    def __init__(self):
        gtk.HBox.__init__(self)

        self._popping_down = False
        self._old_date = None

        # bootstrap problems, kiwi.ui.widgets.entry imports dateentry
        # we need to use a proxy entry because we want the mask
        from kiwi.ui.widgets.entry import ProxyEntry
        self.entry = ProxyEntry()
        self.entry.connect('changed', self._on_entry__changed)
        self.entry.connect('activate', self._on_entry__activate)
        self.entry.set_property('data-type', datetime.date)
        mask = self.entry.get_mask()
        if mask:
            self.entry.set_width_chars(len(mask))
        self.pack_start(self.entry, False, False)
        self.entry.show()

        self._button = gtk.ToggleButton()
        self._button.connect('scroll-event', self._on_entry__scroll_event)
        self._button.connect('toggled', self._on_button__toggled)
        self._button.set_focus_on_click(False)
        self.pack_start(self._button, False, False)
        self._button.show()

        arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
        self._button.add(arrow)
        arrow.show()

        self._popup = _DateEntryPopup(self)
        self._popup.connect('date-selected', self._on_popup__date_selected)
        self._popup.connect('hide', self._on_popup__hide)
        self._popup.set_size_request(-1, 24)
Example #2
0
    def test_attach(self):
        entry = ProxyEntry()
        entry.data_type = currency
        self.assertEqual(entry.get_property('secondary-icon-pixbuf'), None)

        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)
        pixbuf_pixels = calc.render_icon(STOQ_CALC,
                                         gtk.ICON_SIZE_MENU).get_pixels()
        self.assertEqual(
            entry.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels)
        entry.set_sensitive(False)
        self.assertEqual(entry.get_property('secondary-icon-pixbuf'), None)
        entry.set_sensitive(True)
        self.assertEqual(
            entry.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels)

        spinbutton = ProxySpinButton()
        spinbutton.data_type = currency
        self.assertEqual(spinbutton.get_property('secondary-icon-pixbuf'), None)

        calc = CalculatorPopup(spinbutton, CalculatorPopup.MODE_SUB)
        pixbuf_pixels = calc.render_icon(STOQ_CALC,
                                         gtk.ICON_SIZE_MENU).get_pixels()
        self.assertEqual(
            spinbutton.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels)
        spinbutton.set_sensitive(False)
        self.assertEqual(spinbutton.get_property('secondary-icon-pixbuf'), None)
        spinbutton.set_sensitive(True)
        self.assertEqual(
            spinbutton.get_property('secondary-icon-pixbuf').get_pixels(), pixbuf_pixels)
Example #3
0
 def __init__(self):
     entry = ProxyEntry()
     ComboEntry.__init__(self, entry=entry)
     ValidatableProxyWidgetMixin.__init__(self)
     entry.connect('content-changed', self._on_entry__content_changed)
     entry.connect('validation-changed',
                   self._on_entry__validation_changed)
Example #4
0
 def testDigitMask(self):
     e = ProxyEntry()
     e.set_mask('000.000')
     self.assertEqual(e.get_text(), '   .   ')
     e.set_text('123.456')
     self.assertEqual(e.get_text(), '123.456')
     e.delete_text(0, 2)
     self.assertEqual(e.get_text(), '345.6  ')
Example #5
0
 def testRead(self):
     entry = ProxyEntry()
     entry.set_text('1')
     entry.set_property("data-type", "int")
     self.assertEqual(entry.read(), 1)
     entry.set_text('')
     self.assertEqual(entry.read(), ValueUnset)
Example #6
0
    def _create_interface(self, run_editor=None):
        self.sale = self.create_sale()

        self.window = Gtk.Window()
        self.entry = ProxyEntry()
        self.window.add(self.entry)
        self.client_gadget = SearchEntryGadget(
            self.entry, self.store, model=self.sale, model_property='client',
            search_columns=['name'], search_class=ClientSearch,
            parent=self.window, run_editor=run_editor)
        self.client_gadget.get_model_obj = lambda obj: obj and obj.client
Example #7
0
 def testAsciiMask(self):
     e = ProxyEntry()
     e.set_mask('LLLL-L')
     self.assertEqual(e.get_text(), '    - ')
     self.assertEqual(e.get_fields(), ['', ''])
     e.set_text('abcd-e')
     self.assertEqual(e.get_text(), 'abcd-e')
     self.assertEqual(e.get_fields(), ['abcd', 'e'])
Example #8
0
 def testMaskSmallFields(self):
     e = ProxyEntry()
     e.set_mask('0.0.0')
     self.assertEqual(e.get_text(), ' . . ')
     self.assertEqual(e.get_fields(), ['', '', ''])
     e.set_text('1.2.3')
     self.assertEqual(e.get_text(), '1.2.3')
     self.assertEqual(e.get_fields(), ['1', '2', '3'])
Example #9
0
 def testAlphaNumericMask(self):
     e = ProxyEntry()
     e.set_mask('&&&-aaa')
     self.assertEqual(e.get_text(), '   -   ')
     self.assertEqual(e.get_fields(), ['', ''])
     e.set_text('aáé-á1e')
     self.assertEqual(e.get_text(), 'aáé-á1e')
     self.assertEqual(e.get_fields(), ['aáé', 'á1e'])
Example #10
0
    def get_main_widget(self):
        # This is done on entry to check where to put the validation/mandatory
        # icons. We should put the calculator on the other side.
        # Note that spinbuttons are always right aligned and thus
        # xalign will always be 1.0
        if self.attached_widget.get_alignment() > 0.5:
            self._icon_pos = 'secondary-icon'
        else:
            self._icon_pos = 'primary-icon'

        self.attached_widget.set_property(self._icon_pos + '-activatable',
                                          True)
        self.attached_widget.set_property(
            self._icon_pos + '-tooltip-text',
            _("Do calculations on top of this value"))
        self.attached_widget.connect('notify::sensitive',
                                     self._on_entry_sensitive__notify)
        self.attached_widget.connect('icon-press', self._on_entry__icon_press)
        self._toggle_calculator_icon()

        vbox = Gtk.VBox(spacing=6)
        vbox.show()

        self._main_label = Gtk.Label()
        self._main_label.set_ellipsize(Pango.EllipsizeMode.END)
        vbox.pack_start(self._main_label, True, True, 0)
        self._main_label.show()

        self._entry = ProxyEntry()
        # FIXME: We need a model_attribute here or else the entry.is_valid()
        # will always return None. Check proxywidget.py's FIXME to see why
        self._entry.model_attribute = 'not_used'
        self._entry.connect('validate', self._on_entry__validate)
        self._entry.connect_after('changed', self._after_entry__changed)
        self._entry.set_alignment(1.0)
        vbox.pack_start(self._entry, True, True, 0)
        self._entry.show()

        hbox = Gtk.HBox(spacing=6)
        vbox.pack_start(hbox, True, True, 0)
        hbox.show()

        self._label = Gtk.Label()
        self._label.set_property('xalign', 1.0)
        self._label.set_use_markup(True)
        hbox.pack_start(self._label, True, True, 0)
        self._label.show()

        self._warning = Gtk.Image()
        hbox.pack_start(self._warning, False, False, 0)

        return vbox
Example #11
0
    def _create_interface(self):
        self.sale = self.create_sale()

        self.window = gtk.Window()
        self.entry = ProxyEntry()
        self.window.add(self.entry)
        self.client_gadget = SearchEntryGadget(self.entry,
                                               self.store,
                                               model=self.sale,
                                               model_property='client',
                                               search_columns=['name'],
                                               search_class=ClientSearch,
                                               parent=self.window)
Example #12
0
    def test_apply(self):
        entry = ProxyEntry()
        entry.data_type = currency
        entry.set_text('150')
        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)

        # calc.popup will not work here, so call _update_ui directly
        calc._update_ui()
        calc._entry.set_text('10%')
        event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
        event.keyval = gtk.keysyms.Return
        event.window = gtk.gdk.get_default_root_window()
        calc.emit('key-press-event', event)
        calc.emit('key-press-event', event)
        self.assertEqual(entry.read(), 135)
Example #13
0
    def __init__(self):
        super(DateEntry, self).__init__(orientation=Gtk.Orientation.HORIZONTAL)
        self.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)

        self._popping_down = False
        self._old_date = None
        self._block_changed = False

        # This will force both the entry and the button have the same height
        self._sizegroup = Gtk.SizeGroup.new(Gtk.SizeGroupMode.VERTICAL)

        # bootstrap problems, kiwi.ui.widgets.entry imports dateentry
        # we need to use a proxy entry because we want the mask
        from kiwi.ui.widgets.entry import ProxyEntry
        self.entry = ProxyEntry()
        # Set datatype before connecting to change event, to not get when the
        # mask is set
        self.entry.set_property('data-type', datetime.date)
        self.entry.connect('changed', self._on_entry__changed)
        self.entry.connect('activate', self._on_entry__activate)
        mask = self.entry.get_mask()
        if mask:
            self.entry.set_width_chars(len(mask))
        self.pack_start(self.entry, True, True, 0)
        self.entry.set_valign(Gtk.Align.CENTER)
        self._sizegroup.add_widget(self.entry)
        self.entry.show()

        self._button = Gtk.ToggleButton()
        self._button.set_valign(Gtk.Align.CENTER)
        self._button.connect('scroll-event', self._on_entry__scroll_event)
        self._button.connect('toggled', self._on_button__toggled)
        self._button.set_focus_on_click(False)
        self.pack_start(self._button, False, False, 0)
        self._sizegroup.add_widget(self._button)
        self._button.show()

        arrow = Gtk.Arrow(arrow_type=Gtk.ArrowType.DOWN,
                          shadow_type=Gtk.ShadowType.NONE)
        self._button.add(arrow)
        arrow.show()

        self._popup = _DateEntryPopup(self)
        self._popup.connect('date-selected', self._on_popup__date_selected)
        self._popup.connect('hide', self._on_popup__hide)
        self._popup.set_size_request(-1, 24)

        self.set_valign(Gtk.Align.CENTER)
Example #14
0
    def __init__(self):
        gtk.HBox.__init__(self)

        self._popping_down = False
        self._old_date = None

        # bootstrap problems, kiwi.ui.widgets.entry imports dateentry
        # we need to use a proxy entry because we want the mask
        from kiwi.ui.widgets.entry import ProxyEntry
        self.entry = ProxyEntry()
        self.entry.connect('changed', self._on_entry__changed)
        self.entry.connect('activate', self._on_entry__activate)
        self.entry.set_property('data-type', datetime.date)
        mask = self.entry.get_mask()
        if mask:
            self.entry.set_width_chars(len(mask))
        self.pack_start(self.entry, False, False)
        self.entry.show()

        self._button = gtk.ToggleButton()
        self._button.connect('scroll-event', self._on_entry__scroll_event)
        self._button.connect('toggled', self._on_button__toggled)
        self._button.set_focus_on_click(False)
        self.pack_start(self._button, False, False)
        self._button.show()

        arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_NONE)
        self._button.add(arrow)
        arrow.show()

        self._popup = _DateEntryPopup(self)
        self._popup.connect('date-selected', self._on_popup__date_selected)
        self._popup.connect('hide', self._on_popup__hide)
        self._popup.set_size_request(-1, 24)
Example #15
0
    def build_widget(self):
        if self.editable:
            widget = ProxyEntry()
            if self.max_length != 0:
                widget.set_width_chars(self.max_length)
            if self.input_mask:
                widget.set_mask(self.input_mask)
        else:
            widget = ProxyLabel()
            # This label should be left aligned.
            widget.set_alignment(0, 0.5)

        return widget
Example #16
0
    def _setup_entry_slave(self, box=None):
        widget = ProxyEntry()
        widget.props.sensitive = self.sensitive
        widget.data_type = unicode
        widget.model_attribute = "field_value"
        self.proxy.add_widget("field_value", widget)
        if box is None:
            self.container.add(widget)
        else:
            box.pack_start(widget)

        widget.show()
        widget.connect('validate', self._on_entry__validate)
        widget.connect('validation-changed',
                       self._on_entry__validation_changed)

        self._entry = widget
Example #17
0
    def _setup_widgets(self):
        # Hide total and subtotal
        self.table1.hide()
        self.hbox4.hide()

        # Hide invoice number details
        self.invoice_number_label.hide()
        self.invoice_number.hide()

        # Hide cost center combobox
        self.cost_center_lbl.hide()
        self.cost_center.hide()

        # Responsible combo
        self.salesperson_lbl.set_text(_(u'Responsible:'))
        self.salesperson.model_attribute = 'responsible'
        users = self.store.find(LoginUser, is_active=True)
        self.salesperson.prefill(api.for_person_combo(users))
        self.salesperson.set_sensitive(False)

        self._fill_clients_combo()
        self._fill_clients_category_combo()

        self.expire_date.mandatory = True

        # CFOP combo
        self.cfop_lbl.hide()
        self.cfop.hide()
        self.create_cfop.hide()

        # Transporter/RemovedBy Combo
        self.transporter_lbl.set_text(_(u'Removed By:'))
        self.create_transporter.hide()

        # removed_by widget
        self.removed_by = ProxyEntry(unicode)
        self.removed_by.model_attribute = 'removed_by'
        if 'removed_by' not in self.proxy_widgets:
            self.proxy_widgets.append('removed_by')
        self.removed_by.show()
        self._replace_widget(self.transporter, self.removed_by)

        # Operation Nature widget
        self.operation_nature.hide()
        self.nature_lbl.hide()
Example #18
0
    def test_popup(self):
        entry = ProxyEntry()
        entry.data_type = currency
        entry.set_text('150')
        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)

        event = Gdk.Event.new(Gdk.EventType.BUTTON_PRESS)
        event.window = Gdk.get_default_root_window()

        with mock.patch.object(calc, 'popup') as popup:
            entry.emit('icon-press', Gtk.EntryIconPosition.PRIMARY, event)
            self.assertEqual(popup.call_count, 0)
            entry.emit('icon-press', Gtk.EntryIconPosition.SECONDARY, event)
            self.assertEqual(popup.call_count, 1)
Example #19
0
    def add_entry(self, data_type):
        """Add a entry with the specified data_type

        The user will be able to provide any information in the entry that
        should be used by this operation (for instance, a number do multiply a
        value for or a string to replace a value for)
        """
        entry = ProxyEntry(data_type=data_type)
        self.pack_start(entry, False, False, 0)
        return entry
Example #20
0
    def _create_interface(self):
        self.sale = self.create_sale()

        self.window = gtk.Window()
        self.entry = ProxyEntry()
        self.window.add(self.entry)
        self.client_gadget = SearchEntryGadget(
            self.entry, self.store, model=self.sale, model_property='client',
            search_columns=['name'], search_class=ClientSearch,
            parent=self.window)
Example #21
0
    def test_popdown(self):
        entry = ProxyEntry()
        entry.data_type = currency
        entry.set_text('150')
        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)

        with contextlib.nested(
                mock.patch.object(calc, '_maybe_apply_new_value'),
                mock.patch.object(calc, 'popdown')) as (manv, popdown):
            # Those keys should try to apply the value
            for keyval in [gtk.keysyms.Return,
                           gtk.keysyms.KP_Enter,
                           gtk.keysyms.Tab]:
                event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
                event.keyval = keyval
                event.window = gtk.gdk.get_default_root_window()
                calc.emit('key-press-event', event)

                self.assertEqual(manv.call_count, 1)
                self.assertEqual(popdown.call_count, 0)

                manv.reset_mock()
                popdown.reset_mock()

            event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
            # Escape should popdown the popup
            event.keyval = gtk.keysyms.Escape
            event.window = gtk.gdk.get_default_root_window()
            calc.emit('key-press-event', event)

            self.assertEqual(popdown.call_count, 1)
            self.assertEqual(manv.call_count, 0)
            manv.reset_mock()
            popdown.reset_mock()

            event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
            # Any other should not do anything
            event.keyval = gtk.keysyms.A
            event.window = gtk.gdk.get_default_root_window()
            calc.emit('key-press-event', event)

            self.assertEqual(manv.call_count, 0)
            self.assertEqual(popdown.call_count, 0)
Example #22
0
    def test_popdown(self):
        entry = ProxyEntry()
        entry.data_type = currency
        entry.set_text('150')
        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)

        with contextlib.nested(
                mock.patch.object(calc, '_maybe_apply_new_value'),
                mock.patch.object(calc, 'popdown')) as (manv, popdown):
            # Those keys should try to apply the value
            for keyval in [
                    gtk.keysyms.Return, gtk.keysyms.KP_Enter, gtk.keysyms.Tab
            ]:
                event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
                event.keyval = keyval
                event.window = gtk.gdk.get_default_root_window()
                calc.emit('key-press-event', event)

                self.assertEqual(manv.call_count, 1)
                self.assertEqual(popdown.call_count, 0)

                manv.reset_mock()
                popdown.reset_mock()

            event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
            # Escape should popdown the popup
            event.keyval = gtk.keysyms.Escape
            event.window = gtk.gdk.get_default_root_window()
            calc.emit('key-press-event', event)

            self.assertEqual(popdown.call_count, 1)
            self.assertEqual(manv.call_count, 0)
            manv.reset_mock()
            popdown.reset_mock()

            event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
            # Any other should not do anything
            event.keyval = gtk.keysyms.A
            event.window = gtk.gdk.get_default_root_window()
            calc.emit('key-press-event', event)

            self.assertEqual(manv.call_count, 0)
            self.assertEqual(popdown.call_count, 0)
Example #23
0
    def _setup_entry_slave(self, box=None):
        widget = ProxyEntry()
        widget.props.sensitive = self.sensitive
        widget.data_type = unicode
        widget.model_attribute = "field_value"
        self.proxy.add_widget("field_value", widget)
        if box is None:
            self.container.add(widget)
        else:
            box.pack_start(widget)

        widget.show()
        widget.connect("validate", self._on_entry__validate)
        widget.connect("validation-changed", self._on_entry__validation_changed)

        self._entry = widget
Example #24
0
    def _create_interface(self, run_editor=None):
        self.sale = self.create_sale()

        self.window = gtk.Window()
        self.entry = ProxyEntry()
        self.window.add(self.entry)
        self.client_gadget = SearchEntryGadget(
            self.entry, self.store, model=self.sale, model_property='client',
            search_columns=['name'], search_class=ClientSearch,
            parent=self.window, run_editor=run_editor)
        self.client_gadget.get_model_obj = lambda obj: obj and obj.client
Example #25
0
    def build_widget(self):
        if self.editable:
            widget = ProxyEntry()
            if self.max_length != 0:
                widget.set_width_chars(self.max_length)
            if self.input_mask:
                widget.set_mask(self.input_mask)
        else:
            widget = ProxyLabel()
            # This label should be left aligned.
            widget.set_alignment(0, 0.5)

        return widget
Example #26
0
    def test_popup(self):
        entry = ProxyEntry()
        entry.data_type = currency
        entry.set_text('150')
        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)

        event = gtk.gdk.Event(gtk.gdk.BUTTON_PRESS)
        event.window = gtk.gdk.get_default_root_window()

        with mock.patch.object(calc, 'popup') as popup:
            entry.emit('icon-press', gtk.ENTRY_ICON_PRIMARY, event)
            self.assertEqual(popup.call_count, 0)
            entry.emit('icon-press', gtk.ENTRY_ICON_SECONDARY, event)
            popup.assert_called_once()
Example #27
0
    def test_create(self):
        window = Gtk.Window()
        box = Gtk.VBox()
        window.add(box)
        entry = ProxyEntry()
        box.pack_start(entry, True, True, 0)
        self.check_dialog(window, 'search-entry-before-replace')

        sale = self.create_sale()
        SearchEntryGadget(entry, self.store, model=sale,
                          model_property='client', search_columns=['name'],
                          search_class=ClientSearch, parent=window)
        self.check_dialog(window, 'search-entry-after-replace')
Example #28
0
    def get_main_widget(self):
        # This is done on entry to check where to put the validation/mandatory
        # icons. We should put the calculator on the other side.
        # Note that spinbuttons are always right aligned and thus
        # xalign will always be 1.0
        if self.attached_widget.get_alignment() > 0.5:
            self._icon_pos = 'secondary-icon'
        else:
            self._icon_pos = 'primary-icon'

        self.attached_widget.set_property(self._icon_pos + '-activatable', True)
        self.attached_widget.set_property(
            self._icon_pos + '-tooltip-text',
            _("Do calculations on top of this value"))
        self.attached_widget.connect(
            'notify::sensitive', self._on_entry_sensitive__notify)
        self.attached_widget.connect('icon-press', self._on_entry__icon_press)
        self._toggle_calculator_icon()

        vbox = Gtk.VBox(spacing=6)
        vbox.show()

        self._main_label = Gtk.Label()
        self._main_label.set_ellipsize(Pango.EllipsizeMode.END)
        vbox.pack_start(self._main_label, True, True, 0)
        self._main_label.show()

        self._entry = ProxyEntry()
        # FIXME: We need a model_attribute here or else the entry.is_valid()
        # will always return None. Check proxywidget.py's FIXME to see why
        self._entry.model_attribute = 'not_used'
        self._entry.connect('validate', self._on_entry__validate)
        self._entry.connect_after('changed', self._after_entry__changed)
        self._entry.set_alignment(1.0)
        vbox.pack_start(self._entry, True, True, 0)
        self._entry.show()

        hbox = Gtk.HBox(spacing=6)
        vbox.pack_start(hbox, True, True, 0)
        hbox.show()

        self._label = Gtk.Label()
        self._label.set_property('xalign', 1.0)
        self._label.set_use_markup(True)
        hbox.pack_start(self._label, True, True, 0)
        self._label.show()

        self._warning = Gtk.Image()
        hbox.pack_start(self._warning, False, False, 0)

        return vbox
Example #29
0
    def _setup_widgets(self):
        # Hide total and subtotal
        self.summary_table.hide()
        self.total_box.hide()

        # Hide cost center combobox
        self.cost_center_lbl.hide()
        self.cost_center.hide()

        # Responsible combo
        self.salesperson_lbl.set_text(_(u'Responsible:'))
        self.salesperson.model_attribute = 'responsible'
        users = self.store.find(LoginUser, is_active=True)
        self.salesperson.prefill(stoq_api.for_person_combo(users))
        self.salesperson.set_sensitive(False)

        self._setup_clients_widget()
        self._fill_clients_category_combo()

        self.expire_date.mandatory = True

        # CFOP combo
        self.cfop_lbl.hide()
        self.cfop.hide()
        self.create_cfop.hide()

        # Transporter/RemovedBy Combo
        self.transporter_lbl.set_text(_(u'Removed By:'))
        self.create_transporter.hide()

        # removed_by widget
        self.removed_by = ProxyEntry(str)
        self.removed_by.model_attribute = 'removed_by'
        if 'removed_by' not in self.proxy_widgets:
            self.proxy_widgets.append('removed_by')
        self.removed_by.show()
        self._replace_widget(self.transporter, self.removed_by)
Example #30
0
class DateTimeEditUI(GladeSlaveDelegate):

	def __init__(self, dt = None):
		if not dt:
			self.dt = datetime.datetime.now()
		else:
			self.dt = dt
			
		# Set up the user interface
		GladeSlaveDelegate.__init__(self, gladefile="mo_datetime_edit", toplevel_name="window_main")

		self.entry_date = DateEntry()
		self.entry_time = ProxyEntry(data_type=datetime.time)
		self.set_datetime(self.dt)
		self.entry_date.connect('changed', self.entry_date__changed)
		self.entry_time.connect('changed', self.entry_time__changed)
		self.hbox.pack_start(self.entry_date, expand=False, fill=False)
		self.hbox.pack_start(self.entry_time, expand=False, fill=False)

		self.show()

	def entry_date__changed(self, *args):
		dt = self.entry_date.get_date()
		if dt != None:
			self.dt = self.dt.replace(dt.year, dt.month, dt.day)

	def entry_time__changed(self, *args):
		dt = None
		try:
			dt = self.entry_time.read()
		except ValidationError, e:
			# Ignore invalid times because the user might still be filling it
			# out.
			pass

		if dt != None:
			self.dt = self.dt.replace(self.dt.year, self.dt.month, self.dt.day, dt.hour, dt.minute)
Example #31
0
 def build_widget(self):
     if self.editable:
         widget = ProxyEntry()
         if self.max_length != 0:
             widget.set_width_chars(self.max_length)
         if self.input_mask:
             widget.set_mask(self.input_mask)
     else:
         widget = ProxyLabel()
     return widget
Example #32
0
    def __init__(self):
        super(DateEntry, self).__init__(orientation=Gtk.Orientation.HORIZONTAL)
        self.get_style_context().add_class(Gtk.STYLE_CLASS_LINKED)

        self._popping_down = False
        self._old_date = None
        self._block_changed = False

        # This will force both the entry and the button have the same height
        self._sizegroup = Gtk.SizeGroup.new(Gtk.SizeGroupMode.VERTICAL)

        # bootstrap problems, kiwi.ui.widgets.entry imports dateentry
        # we need to use a proxy entry because we want the mask
        from kiwi.ui.widgets.entry import ProxyEntry
        self.entry = ProxyEntry()
        # Set datatype before connecting to change event, to not get when the
        # mask is set
        self.entry.set_property('data-type', datetime.date)
        self.entry.connect('changed', self._on_entry__changed)
        self.entry.connect('activate', self._on_entry__activate)
        mask = self.entry.get_mask()
        if mask:
            self.entry.set_width_chars(len(mask))
        self.pack_start(self.entry, True, True, 0)
        self.entry.set_valign(Gtk.Align.CENTER)
        self._sizegroup.add_widget(self.entry)
        self.entry.show()

        self._button = Gtk.ToggleButton()
        self._button.set_valign(Gtk.Align.CENTER)
        self._button.connect('scroll-event', self._on_entry__scroll_event)
        self._button.connect('toggled', self._on_button__toggled)
        self._button.set_focus_on_click(False)
        self.pack_start(self._button, False, False, 0)
        self._sizegroup.add_widget(self._button)
        self._button.show()

        arrow = Gtk.Arrow(arrow_type=Gtk.ArrowType.DOWN, shadow_type=Gtk.ShadowType.NONE)
        self._button.add(arrow)
        arrow.show()

        self._popup = _DateEntryPopup(self)
        self._popup.connect('date-selected', self._on_popup__date_selected)
        self._popup.connect('hide', self._on_popup__hide)
        self._popup.set_size_request(-1, 24)

        self.set_valign(Gtk.Align.CENTER)
Example #33
0
    def test_with_cfop(self, run_dialog, new_store):
        new_store.return_value = self.store
        window = Gtk.Window()
        entry = ProxyEntry()
        window.add(entry)

        sale = self.create_sale()
        gadget = SearchEntryGadget(
            entry, self.store, model=sale, model_property='cfop',
            search_columns=['name'], search_class=CfopSearch, parent=window)

        with mock.patch.object(self.store, 'commit'):
            with mock.patch.object(self.store, 'close'):
                run_dialog.return_value = None
                self.click(gadget.edit_button)
                run_dialog.assert_called_once_with(
                    CfopEditor, window, self.store, sale.cfop)
Example #34
0
    def _setup_widgets(self):
        # Hide total and subtotal
        self.table1.hide()
        self.hbox4.hide()

        # Hide invoice number details
        self.invoice_number_label.hide()
        self.invoice_number.hide()

        # Hide cost center combobox
        self.cost_center_lbl.hide()
        self.cost_center.hide()

        # Responsible combo
        self.salesperson_lbl.set_text(_(u'Responsible:'))
        self.salesperson.model_attribute = 'responsible'
        users = self.store.find(LoginUser, is_active=True)
        self.salesperson.prefill(api.for_person_combo(users))
        self.salesperson.set_sensitive(False)

        self._fill_clients_combo()
        self._fill_clients_category_combo()

        self.expire_date.mandatory = True

        # CFOP combo
        self.cfop_lbl.hide()
        self.cfop.hide()
        self.create_cfop.hide()

        # Transporter/RemovedBy Combo
        self.transporter_lbl.set_text(_(u'Removed By:'))
        self.create_transporter.hide()

        # removed_by widget
        self.removed_by = ProxyEntry(unicode)
        self.removed_by.model_attribute = 'removed_by'
        if 'removed_by' not in self.proxy_widgets:
            self.proxy_widgets.append('removed_by')
        self.removed_by.show()
        self._replace_widget(self.transporter, self.removed_by)

        # Operation Nature widget
        self.operation_nature.hide()
        self.nature_lbl.hide()
Example #35
0
	def __init__(self, dt = None):
		if not dt:
			self.dt = datetime.datetime.now()
		else:
			self.dt = dt
			
		# Set up the user interface
		GladeSlaveDelegate.__init__(self, gladefile="mo_datetime_edit", toplevel_name="window_main")

		self.entry_date = DateEntry()
		self.entry_time = ProxyEntry(data_type=datetime.time)
		self.set_datetime(self.dt)
		self.entry_date.connect('changed', self.entry_date__changed)
		self.entry_time.connect('changed', self.entry_time__changed)
		self.hbox.pack_start(self.entry_date, expand=False, fill=False)
		self.hbox.pack_start(self.entry_time, expand=False, fill=False)

		self.show()
Example #36
0
    def test_apply(self):
        entry = ProxyEntry()
        entry.data_type = currency
        entry.set_text('150')
        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)

        # calc.popup will not work here, so call validate_popup directly
        calc.validate_popup()
        calc._entry.set_text('10%')
        event = Gdk.Event.new(Gdk.EventType.KEY_PRESS)
        event.keyval = Gdk.KEY_Return
        event.window = Gdk.get_default_root_window()
        calc.emit('key-press-event', event)
        calc.emit('key-press-event', event)
        self.assertEqual(entry.read(), 135)
Example #37
0
def get_widget_for_type(rtype):
    if rtype is types.boolean:
        return ProxyCheckButton()
    elif rtype is types.file:
        w = ProxyFileChooserButton('Select File')
        #w.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
        return w
    elif rtype is types.readonlyfile:
        w = ProxyFileChooserButton('Select File')
        w.set_sensitive(False)
        #w.set_action(gtk.FILE_CHOOSER_ACTION_SAVE)
        return w
    elif rtype in [types.directory]:
        w = ProxyFileChooserButton(title='Select Directory')
        w.set_action(gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
        return w
    elif rtype is types.font:
        return ProxyFontButton()
    elif rtype is types.color:
        return CleverProxyColorButton()
    elif rtype is types.integer:
        w = ProxySpinButton()
        return w
    elif rtype.__name__ is 'intrange':
        adjvals = rtype.lower, rtype.upper, rtype.step
        adj = gtk.Adjustment(0, *adjvals)
        w = ProxySpinButton()
        w.set_adjustment(adj)
        return w
    elif rtype is types.readonly:
        return FormattedLabel(VC_NAME_MU)
    elif rtype.__name__ is 'stringlist':
        w = ProxyComboBox()
        w.set_property('data-type', str)
        w.prefill(rtype.choices)
        return w
    else:
        w = ProxyEntry(data_type=str)
        w.set_width_chars(18)
        return w
Example #38
0
    def test_validate(self):
        def validate_entry(entry, value):
            if value == 100:
                return ValidationError()

        # FIXME: For some reason, entry is not emitting 'changed' event
        # on set_text, not even if we call entry.emit('changed') by hand.
        # That only happens here on the test. Figure out why
        def update_entry(entry, value):
            entry.set_text(value)
            entry.validate(force=True)

        entry = ProxyEntry()
        entry.data_type = currency
        entry.connect('validate', validate_entry)
        entry.set_text('150')

        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)
        # calc.popup will not work here, so call validate_popup directly
        calc.validate_popup()
        self.assertValid(calc, ['_entry'])
        self.assertNotVisible(calc, ['_warning'])

        for value in ['abc', '+10%', '-10%', '+10', '-10']:
            update_entry(calc._entry, value)
            self.assertInvalid(calc, ['_entry'])
            self.assertNotVisible(calc, ['_warning'])

        update_entry(calc._entry, '40')
        self.assertValid(calc, ['_entry'])
        self.assertNotVisible(calc, ['_warning'])

        # 50 of discount will make the value invalid on entry
        # (see validate_entry above)
        update_entry(calc._entry, '50')
        self.assertValid(calc, ['_entry'])
        self.assertVisible(calc, ['_warning'])
Example #39
0
    def test_validate(self):
        def validate_entry(entry, value):
            if value == 100:
                return ValidationError()

        # FIXME: For some reason, entry is not emitting 'changed' event
        # on set_text, not even if we call entry.emit('changed') by hand.
        # That only happens here on the test. Figure out why
        def update_entry(entry, value):
            entry.set_text(value)
            entry.validate(force=True)

        entry = ProxyEntry()
        entry.data_type = currency
        entry.connect('validate', validate_entry)
        entry.set_text('150')

        calc = CalculatorPopup(entry, CalculatorPopup.MODE_SUB)
        # calc.popup will not work here, so call _update_ui directly
        calc._update_ui()
        self.assertValid(calc, ['_entry'])
        self.assertNotVisible(calc, ['_warning'])

        for value in ['abc', '+10%', '-10%', '+10', '-10']:
            update_entry(calc._entry, value)
            self.assertInvalid(calc, ['_entry'])
            self.assertNotVisible(calc, ['_warning'])

        update_entry(calc._entry, '40')
        self.assertValid(calc, ['_entry'])
        self.assertNotVisible(calc, ['_warning'])

        # 50 of discount will make the value invalid on entry
        # (see validate_entry above)
        update_entry(calc._entry, '50')
        self.assertValid(calc, ['_entry'])
        self.assertVisible(calc, ['_warning'])
Example #40
0
    def _setup_entry_slave(self, box=None):
        widget = ProxyEntry()
        # Try to simulate insensitive appearance for non-editable entries
        # while keeping them selectable
        widget.set_editable(self.sensitive)
        if not self.sensitive:
            sc = widget.get_style_context()
            sc.add_class('visualmode')
        widget.data_type = str
        widget.model_attribute = "field_value"
        self.proxy.add_widget("field_value", widget)
        if box is None:
            self.container.add(widget)
        else:
            box.pack_start(widget, True, True, 0)

        widget.show()
        widget.connect('validate', self._on_entry__validate)
        widget.connect('validation-changed',
                       self._on_entry__validation_changed)

        self._entry = widget
Example #41
0
class TestSearchEntryGadget(GUITest):
    def _create_interface(self, run_editor=None):
        self.sale = self.create_sale()

        self.window = gtk.Window()
        self.entry = ProxyEntry()
        self.window.add(self.entry)
        self.client_gadget = SearchEntryGadget(self.entry,
                                               self.store,
                                               model=self.sale,
                                               model_property='client',
                                               search_columns=['name'],
                                               search_class=ClientSearch,
                                               parent=self.window,
                                               run_editor=run_editor)
        self.client_gadget.get_model_obj = lambda obj: obj and obj.client

    def test_create(self):
        window = gtk.Window()
        box = gtk.VBox()
        window.add(box)
        entry = ProxyEntry()
        box.pack_start(entry)
        self.check_dialog(window, 'search-entry-before-replace')

        sale = self.create_sale()
        SearchEntryGadget(entry,
                          self.store,
                          model=sale,
                          model_property='client',
                          search_columns=['name'],
                          search_class=ClientSearch,
                          parent=window)
        self.check_dialog(window, 'search-entry-after-replace')

    @mock.patch('stoqlib.gui.widgets.searchentry.run_dialog')
    def test_run_search(self, run_dialog):
        self._create_interface()
        run_dialog.return_value = None
        self.click(self.client_gadget.find_button)
        run_dialog.assert_called_once_with(ClientSearch,
                                           self.window,
                                           self.store,
                                           initial_string='',
                                           double_click_confirm=True)

    @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store')
    @mock.patch('stoqlib.gui.widgets.searchentry.run_person_role_dialog')
    def test_run_editor(self, run_dialog, new_store):
        new_store.return_value = self.store
        self._create_interface()
        client = self.create_client(name=u'Fulano de Tal')
        run_dialog.return_value = self.store.find(
            ClientView, ClientView.id == client.id).one()
        with mock.patch.object(self.store, 'commit'):
            with mock.patch.object(self.store, 'close'):
                self.click(self.client_gadget.edit_button)
                run_dialog.assert_called_once_with(ClientEditor, self.window,
                                                   self.store, None)

        self.assertEquals(self.entry.read(), client)
        self.assertEquals(self.entry.get_text(), u'Fulano de Tal')

    @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store')
    def test_run_editor_override(self, new_store):
        new_store.return_value = self.store
        run_editor = mock.MagicMock()
        run_editor.return_value = None
        self.assertEquals(run_editor.call_count, 0)
        self._create_interface(run_editor=run_editor)
        with mock.patch.object(self.store, 'commit'):
            with mock.patch.object(self.store, 'close'):
                self.click(self.client_gadget.edit_button)

        self.assertEquals(run_editor.call_count, 1)

    @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store')
    @mock.patch('stoqlib.gui.widgets.searchentry.run_dialog')
    def test_entry_activate(self, run_dialog, new_store):
        new_store.return_value = self.store
        self._create_interface()

        fulano = self.create_client(u'Fulano de Tal')
        ciclano = self.create_client(u'Cicrano de Tal')

        # There should be only one match for Fulano, then the entry should be
        # updated with this only match
        self.entry.set_text('Fulano')
        self.entry.activate()

        self.assertEquals(self.entry.get_text(), 'Fulano de Tal')
        self.assertEquals(self.entry.read(), fulano)

        # Now when we use 'de tal', there are two clients that match. The
        # search should be displayed
        run_dialog.return_value = self.store.find(
            ClientView, ClientView.id == ciclano.id).one()

        self.entry.set_text('de tal')
        self.entry.activate()

        run_dialog.assert_called_once_with(ClientSearch,
                                           self.window,
                                           self.store,
                                           initial_string='de tal',
                                           double_click_confirm=True)

        self.assertEquals(self.entry.get_text(), 'Cicrano de Tal')
        self.assertEquals(self.entry.read(), ciclano)

    @mock.patch('stoqlib.gui.widgets.searchentry.api.new_store')
    @mock.patch('stoqlib.gui.widgets.searchentry.run_dialog')
    def test_with_cfop(self, run_dialog, new_store):
        new_store.return_value = self.store
        window = gtk.Window()
        entry = ProxyEntry()
        window.add(entry)

        sale = self.create_sale()
        gadget = SearchEntryGadget(entry,
                                   self.store,
                                   model=sale,
                                   model_property='cfop',
                                   search_columns=['name'],
                                   search_class=CfopSearch,
                                   parent=window)

        with mock.patch.object(self.store, 'commit'):
            with mock.patch.object(self.store, 'close'):
                run_dialog.return_value = None
                self.click(gadget.edit_button)
                run_dialog.assert_called_once_with(CfopEditor, window,
                                                   self.store, sale.cfop)
Example #42
0
    def _setup_entry_slave(self, box=None):
        widget = ProxyEntry()
        # Try to simulate insensitive appearance for non-editable entries
        # while keeping them selectable
        widget.set_editable(self.sensitive)
        if not self.sensitive:
            style = widget.get_style()
            widget.modify_text(gtk.STATE_NORMAL,
                               style.text[gtk.STATE_INSENSITIVE])
            widget.modify_base(gtk.STATE_NORMAL,
                               style.base[gtk.STATE_INSENSITIVE])
        widget.data_type = unicode
        widget.model_attribute = "field_value"
        self.proxy.add_widget("field_value", widget)
        if box is None:
            self.container.add(widget)
        else:
            box.pack_start(widget)

        widget.show()
        widget.connect('validate', self._on_entry__validate)
        widget.connect('validation-changed',
                       self._on_entry__validation_changed)

        self._entry = widget
Example #43
0
class CalculatorPopup(PopupWindow):
    """A popup calculator for entries

    Right now it supports both
    :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` and
    :class:`kiwi.ui.widgets.entry.ProxyEntry`, as long as their
    data types are numeric (e.g. int, currency, Decimal, etc)
    """

    #: The add mode. Any value typed on the entry will be added to the
    #: original value. e.g. 10% means +10%
    MODE_ADD = 0

    #: The sub mode. Any value typed on the entry will be subtracted from the
    #: original value. e.g. 10% means -10%
    MODE_SUB = 1

    _mode = None
    _data_type_mapper = {
        'currency': currency,
        'Decimal': decimal.Decimal,
    }

    def __init__(self, entry, mode):
        """
        :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton`
            or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass
        :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB`
        """
        self._mode = mode
        self._new_value = None
        self._data_type = self._data_type_mapper[entry.data_type]
        self._converter = converter.get_converter(self._data_type)

        super(CalculatorPopup, self).__init__(entry)

    #
    # Public API
    #

    def get_main_widget(self):
        # This is done on entry to check where to put the validation/mandatory
        # icons. We should put the calculator on the other side.
        # Note that spinbuttons are always right aligned and thus
        # xalign will always be 1.0
        if self.attached_widget.get_alignment() > 0.5:
            self._icon_pos = 'secondary-icon'
        else:
            self._icon_pos = 'primary-icon'

        self.attached_widget.set_property(self._icon_pos + '-activatable', True)
        self.attached_widget.set_property(
            self._icon_pos + '-tooltip-text',
            _("Do calculations on top of this value"))
        self.attached_widget.connect(
            'notify::sensitive', self._on_entry_sensitive__notify)
        self.attached_widget.connect('icon-press', self._on_entry__icon_press)
        self._toggle_calculator_icon()

        vbox = Gtk.VBox(spacing=6)
        vbox.show()

        self._main_label = Gtk.Label()
        self._main_label.set_ellipsize(Pango.EllipsizeMode.END)
        vbox.pack_start(self._main_label, True, True, 0)
        self._main_label.show()

        self._entry = ProxyEntry()
        # FIXME: We need a model_attribute here or else the entry.is_valid()
        # will always return None. Check proxywidget.py's FIXME to see why
        self._entry.model_attribute = 'not_used'
        self._entry.connect('validate', self._on_entry__validate)
        self._entry.connect_after('changed', self._after_entry__changed)
        self._entry.set_alignment(1.0)
        vbox.pack_start(self._entry, True, True, 0)
        self._entry.show()

        hbox = Gtk.HBox(spacing=6)
        vbox.pack_start(hbox, True, True, 0)
        hbox.show()

        self._label = Gtk.Label()
        self._label.set_property('xalign', 1.0)
        self._label.set_use_markup(True)
        hbox.pack_start(self._label, True, True, 0)
        self._label.show()

        self._warning = Gtk.Image()
        hbox.pack_start(self._warning, False, False, 0)

        return vbox

    def validate_popup(self):
        try:
            self._new_value = self._data_type(self.attached_widget.get_text())
        except decimal.InvalidOperation:
            return False

        self._entry.set_text('')
        self._entry.set_tooltip_text(_("Use absolute or percentage (%) value"))
        self._preview_new_value()
        self._main_label.set_text(self._get_main_label())

        return True

    def confirm(self):
        self._maybe_apply_new_value()

    #
    #  Private
    #

    def _get_main_label(self):
        if self._mode == self.MODE_ADD:
            return (_("Surcharge") if self._data_type == currency else
                    _("Addition"))
        elif self._mode == self.MODE_SUB:
            return (_("Discount") if self._data_type == currency else
                    _("Subtraction"))
        else:
            raise AssertionError

    def _set_warning(self, warning):
        if warning is None:
            self._warning.hide()
        else:
            self._warning.set_from_stock(Gtk.STOCK_DIALOG_WARNING,
                                         Gtk.IconSize.MENU)
            self._warning.set_tooltip_text(warning)
            self._warning.show()

    def _get_new_value(self):
        operation = self._entry.get_text().strip()
        operation = operation.replace(',', '.')

        if operation.endswith('%'):
            op_value = operation[:-1]
            percentage = True
        else:
            op_value = operation
            percentage = False

        if not operation:
            return

        if operation[0] in ['+', '-']:
            raise ValueError(_("Operator signals are not supported..."))

        if self._mode == self.MODE_SUB:
            op = operator.sub
        elif self._mode == self.MODE_ADD:
            op = operator.add

        try:
            op_value = decimal.Decimal(op_value)
        except decimal.InvalidOperation:
            raise ValueError(
                _("'%s' is not a valid operation...") % (operation,))

        if percentage:
            value = op(self._new_value, self._new_value * (op_value / 100))
        else:
            value = op(self._new_value, op_value)

        return value

    def _update_new_value(self):
        if not self._entry.is_valid():
            return

        self._new_value = self._get_new_value()
        self._entry.set_text('')
        self._preview_new_value()

    def _preview_new_value(self):
        self._label.set_markup('<b>%s</b>' % (
            self._converter.as_string(self._new_value), ))

    def _maybe_apply_new_value(self):
        if self._entry.get_text():
            self._update_new_value()
            return

        self.attached_widget.update(self._new_value)

        self.popdown()

    def _toggle_calculator_icon(self):
        if self.attached_widget.get_sensitive():
            icon = STOQ_CALC
        else:
            icon = None

        self.attached_widget.set_property(self._icon_pos + '-name', icon)

    #
    #  Callbacks
    #

    def _on_entry__validate(self, entry, value):
        try:
            value = self._get_new_value()
        except ValueError as err:
            return ValidationError('%s\n%s' % (err,
                                   _("Use absolute or percentage (%) value")))

        if value:
            warning = self.attached_widget.emit('validate', value)
            warning = warning and str(warning)
        else:
            warning = None

        self._set_warning(warning)

    def _after_entry__changed(self, entry):
        entry.validate(force=True)

    def _on_entry_sensitive__notify(self, obj, pspec):
        self._toggle_calculator_icon()

    def _on_entry__icon_press(self, entry, icon_pos, event):
        if icon_pos != Gtk.EntryIconPosition.SECONDARY:
            return

        self.popup()
Example #44
0
    def _create_entry(self, mandatory=False):
        entry = ProxyEntry()

        entry.data_type = str
        # Set as empty or kiwi will return ValueUnset on entry.read()
        # and we would have to take that in consideration everywhere here
        entry.update(u'')
        entry.mandatory = mandatory
        self.setup_entry(entry)

        entry.connect_after('content-changed',
                            self._after_entry__content_changed)
        entry.connect_after('changed', self._after_entry__changed)
        entry.connect('validate', self._on_entry__validate)
        entry.connect('activate', self._on_entry__activate)

        return entry
Example #45
0
class EditorPropertyName(EditorProperty):

    duplicate_name_msg = _("There is already a widget with this name")
    no_name_msg = _("The widget must have a name")

    def __init__(self, property_class, app):
        EditorProperty.__init__(self, property_class, app)

        self.input = ProxyEntry()
        self.input.set_property('data-type', str)
        self.input.connect('changed', self._changed_text)

    def load(self, widget):
        self._start_load(widget)
        self._internal_load(widget)
        self._finish_load()

    def _internal_load(self, widget):
        value = self.property.value
        pos = self.input.get_position()
        self.input.delete_text(0, -1)
        if value:
            self.input.insert_text(value)

        self.input.set_position(pos)

        # validate that the name exists and is unique
        if not value:
            self.input.set_invalid(self.no_name_msg)
        elif self._project.get_gadget_by_name(value) != widget:
            self.input.set_invalid(self.duplicate_name_msg)
        else:
            self.input.set_valid()

    def _changed_text(self, entry):
        """
        Update the name property of the widget if the name is valid.
        """
        if self.property_loading:
            return

        name = entry.get_chars(0, -1)
        if not name:
            self.input.set_invalid(self.no_name_msg)
        elif self._project.get_gadget_by_name(name):
            self.input.set_invalid(self.duplicate_name_msg)
        else:
            self.input.set_valid()
            self.set(name)
Example #46
0
 def build_widget(self):
     widget = ProxyEntry()
     return widget
    def _create_entry(self, mandatory=False):
        entry = ProxyEntry()

        entry.data_type = unicode
        # Set as empty or kiwi will return ValueUnset on entry.read()
        # and we would have to take that in consideration everywhere here
        entry.update(u'')
        entry.mandatory = mandatory
        self.setup_entry(entry)

        entry.connect_after('content-changed',
                            self._after_entry__content_changed)
        entry.connect_after('changed', self._after_entry__changed)
        entry.connect('validate', self._on_entry__validate)
        entry.connect('activate', self._on_entry__activate)

        return entry
Example #48
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()
Example #49
0
 def build_widget(self):
     entry = ProxyEntry()
     return entry
Example #50
0
    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()
Example #51
0
class StartNewLoanStep(WizardEditorStep):
    gladefile = 'SalesPersonStep'
    model_type = Loan
    proxy_widgets = ['client', 'salesperson', 'expire_date', 'client_category']

    def _setup_widgets(self):
        # Hide total and subtotal
        self.summary_table.hide()
        self.total_box.hide()

        # Hide invoice number details
        self.invoice_number_label.hide()
        self.invoice_number.hide()

        # Hide cost center combobox
        self.cost_center_lbl.hide()
        self.cost_center.hide()

        # Responsible combo
        self.salesperson_lbl.set_text(_(u'Responsible:'))
        self.salesperson.model_attribute = 'responsible'
        users = self.store.find(LoginUser, is_active=True)
        self.salesperson.prefill(api.for_person_combo(users))
        self.salesperson.set_sensitive(False)

        self._setup_clients_widget()
        self._fill_clients_category_combo()

        self.expire_date.mandatory = True

        # CFOP combo
        self.cfop_lbl.hide()
        self.cfop.hide()
        self.create_cfop.hide()

        # Transporter/RemovedBy Combo
        self.transporter_lbl.set_text(_(u'Removed By:'))
        self.create_transporter.hide()

        # removed_by widget
        self.removed_by = ProxyEntry(unicode)
        self.removed_by.model_attribute = 'removed_by'
        if 'removed_by' not in self.proxy_widgets:
            self.proxy_widgets.append('removed_by')
        self.removed_by.show()
        self._replace_widget(self.transporter, self.removed_by)

    def _setup_clients_widget(self):
        self.client.mandatory = True
        self.client_gadget = ClientSearchEntryGadget(entry=self.client,
                                                     store=self.store,
                                                     model=self.model,
                                                     parent=self.wizard)

    def _fill_clients_category_combo(self):
        categories = self.store.find(ClientCategory)
        self.client_category.prefill(api.for_combo(categories, empty=''))

    def _replace_widget(self, old_widget, new_widget):
        # retrieve the position, since we will replace two widgets later.
        parent = old_widget.get_parent()
        top = parent.child_get_property(old_widget, 'top-attach')
        bottom = parent.child_get_property(old_widget, 'bottom-attach')
        left = parent.child_get_property(old_widget, 'left-attach')
        right = parent.child_get_property(old_widget, 'right-attach')
        parent.remove(old_widget)
        parent.attach(new_widget, left, right, top, bottom)

    def _get_client(self):
        client_id = self.client.read()
        return self.store.get(Client, client_id)

    #
    # WizardStep hooks
    #

    def post_init(self):
        self.register_validate_function(self.wizard.refresh_next)
        self.force_validation()

    def next_step(self):
        return LoanItemStep(self.wizard, self, self.store, self.model)

    def has_previous_step(self):
        return False

    def setup_proxies(self):
        self._setup_widgets()
        self.proxy = self.add_proxy(self.model, StartNewLoanStep.proxy_widgets)

    #
    #   Callbacks
    #

    def on_client__changed(self, widget):
        client = self._get_client()
        if not client:
            return
        self.client_category.select(client.category)

    def on_expire_date__validate(self, widget, value):
        if value < localtoday().date():
            msg = _(u"The expire date must be set to today or a future date.")
            return ValidationError(msg)

    def on_observations_button__clicked(self, *args):
        run_dialog(NoteEditor,
                   self.wizard,
                   self.store,
                   self.model,
                   'notes',
                   title=_("Additional Information"))
Example #52
0
    def _setup_entry_slave(self, box=None):
        widget = ProxyEntry()
        # Try to simulate insensitive appearance for non-editable entries
        # while keeping them selectable
        widget.set_editable(self.sensitive)
        if not self.sensitive:
            sc = widget.get_style_context()
            sc.add_class('visualmode')
        widget.data_type = str
        widget.model_attribute = "field_value"
        self.proxy.add_widget("field_value", widget)
        if box is None:
            self.container.add(widget)
        else:
            box.pack_start(widget, True, True, 0)

        widget.show()
        widget.connect('validate', self._on_entry__validate)
        widget.connect('validation-changed',
                       self._on_entry__validation_changed)

        self._entry = widget
Example #53
0
class StartNewLoanStep(WizardEditorStep):
    gladefile = 'SalesPersonStep'
    model_type = Loan
    proxy_widgets = ['client', 'salesperson', 'expire_date',
                     'client_category']

    def _setup_widgets(self):
        # Hide total and subtotal
        self.table1.hide()
        self.hbox4.hide()

        # Hide invoice number details
        self.invoice_number_label.hide()
        self.invoice_number.hide()

        # Hide cost center combobox
        self.cost_center_lbl.hide()
        self.cost_center.hide()

        # Responsible combo
        self.salesperson_lbl.set_text(_(u'Responsible:'))
        self.salesperson.model_attribute = 'responsible'
        users = self.store.find(LoginUser, is_active=True)
        self.salesperson.prefill(api.for_person_combo(users))
        self.salesperson.set_sensitive(False)

        self._fill_clients_combo()
        self._fill_clients_category_combo()

        self.expire_date.mandatory = True

        # CFOP combo
        self.cfop_lbl.hide()
        self.cfop.hide()
        self.create_cfop.hide()

        # Transporter/RemovedBy Combo
        self.transporter_lbl.set_text(_(u'Removed By:'))
        self.create_transporter.hide()

        # removed_by widget
        self.removed_by = ProxyEntry(unicode)
        self.removed_by.model_attribute = 'removed_by'
        if 'removed_by' not in self.proxy_widgets:
            self.proxy_widgets.append('removed_by')
        self.removed_by.show()
        self._replace_widget(self.transporter, self.removed_by)

        # Operation Nature widget
        self.operation_nature.hide()
        self.nature_lbl.hide()

    def _fill_clients_combo(self):
        # FIXME: This should not be using a normal ProxyComboEntry,
        #        we need a specialized widget that does the searching
        #        on demand.
        items = Client.get_active_items(self.store)
        self.client.prefill(items)
        self.client.mandatory = True

    def _fill_clients_category_combo(self):
        categories = self.store.find(ClientCategory)
        self.client_category.prefill(api.for_combo(categories, empty=''))

    def _replace_widget(self, old_widget, new_widget):
        # retrieve the position, since we will replace two widgets later.
        parent = old_widget.get_parent()
        top = parent.child_get_property(old_widget, 'top-attach')
        bottom = parent.child_get_property(old_widget, 'bottom-attach')
        left = parent.child_get_property(old_widget, 'left-attach')
        right = parent.child_get_property(old_widget, 'right-attach')
        parent.remove(old_widget)
        parent.attach(new_widget, left, right, top, bottom)
        parent.child_set_property(new_widget, 'y-padding', 3)
        parent.child_set_property(new_widget, 'x-padding', 3)

    #
    # WizardStep hooks
    #

    def post_init(self):
        self.toogle_client_details()
        self.register_validate_function(self.wizard.refresh_next)
        self.force_validation()

    def next_step(self):
        return LoanItemStep(self.wizard, self, self.store, self.model)

    def has_previous_step(self):
        return False

    def setup_proxies(self):
        self._setup_widgets()
        self.proxy = self.add_proxy(self.model,
                                    StartNewLoanStep.proxy_widgets)

    def toogle_client_details(self):
        client = self.client.read()
        self.client_details.set_sensitive(bool(client))

    #
    #   Callbacks
    #

    def on_create_client__clicked(self, button):
        store = api.new_store()
        client = run_person_role_dialog(ClientEditor, self.wizard, store, None)
        retval = store.confirm(client)
        client = self.store.fetch(client)
        store.close()
        if not retval:
            return
        self._fill_clients_combo()
        self.client.select(client)

    def on_client__changed(self, widget):
        self.toogle_client_details()
        client = self.client.get_selected()
        if not client:
            return
        self.client_category.select(client.category)

    def on_expire_date__validate(self, widget, value):
        if value < localtoday().date():
            msg = _(u"The expire date must be set to today or a future date.")
            return ValidationError(msg)

    def on_observations_button__clicked(self, *args):
        run_dialog(NoteEditor, self.wizard, self.store, self.model, 'notes',
                   title=_("Additional Information"))

    def on_client_details__clicked(self, button):
        client = self.model.client
        run_dialog(ClientDetailsDialog, self.wizard, self.store, client)
Example #54
0
 def __init__(self):
     entry = ProxyEntry()
     ComboEntry.__init__(self, entry=entry)
     ValidatableProxyWidgetMixin.__init__(self)
     entry.connect('content-changed', self._on_entry__content_changed)
     entry.connect('validation-changed', self._on_entry__validation_changed)
Example #55
0
class CalculatorPopup(gtk.Window):
    """A popup calculator for entries

    Right now it supports both
    :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` and
    :class:`kiwi.ui.widgets.entry.ProxyEntry`, as long as their
    data types are numeric (e.g. int, currency, Decimal, etc)
    """

    #: The add mode. Any value typed on the entry will be added to the
    #: original value. e.g. 10% means +10%
    MODE_ADD = 0

    #: The sub mode. Any value typed on the entry will be subtracted from the
    #: original value. e.g. 10% means -10%
    MODE_SUB = 1

    _mode = None
    _data_type_mapper = {
        'currency': currency,
        'Decimal': decimal.Decimal,
    }

    def __init__(self, entry, mode):
        """
        :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton`
            or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass
        :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB`
        """
        self._mode = mode

        self._new_value = None
        self._popped_entry = entry
        self._data_type = self._data_type_mapper[entry.data_type]
        self._converter = converter.get_converter(self._data_type)

        gtk.Window.__init__(self, gtk.WINDOW_POPUP)

        self._create_ui()

    #
    # Public API
    #

    def popup(self):
        if not self._popped_entry.get_realized():
            return

        if not self._update_ui():
            return

        toplevel = self._popped_entry.get_toplevel().get_toplevel()
        if (isinstance(toplevel, (gtk.Window, gtk.Dialog)) and
            toplevel.get_group()):
            toplevel.get_group().add_window(self)

        # width is meant for the popup window
        x, y, width, height = self._get_position()
        self.set_size_request(width, -1)
        self.move(x, y)
        self.show_all()

        if not self._popup_grab_window():
            self.hide()
            return

        self.grab_add()

    def popdown(self):
        if not self._popped_entry.get_realized():
            return

        self.grab_remove()
        self.hide()
        self._popped_entry.grab_focus()

    #
    #  Private
    #

    def _create_ui(self):
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK)
        self.connect('key-press-event', self._on__key_press_event)
        self.connect('button-press-event', self._on__button_press_event)

        # This is done on entry to check where to put the validation/mandatory
        # icons. We should put the calculator on the other side.
        # Note that spinbuttons are always right aligned and thus
        # xalign will always be 1.0
        if self._popped_entry.get_property('xalign') > 0.5:
            self._icon_pos = 'secondary-icon'
        else:
            self._icon_pos = 'primary-icon'

        self._popped_entry.set_property(self._icon_pos + '-activatable', True)
        self._popped_entry.set_property(self._icon_pos + '-tooltip-text',
                                        _("Do calculations on top of this value"))
        self._popped_entry.connect('notify::sensitive',
                                   self._on_popped_entry_sensitive__notify)
        self._popped_entry.connect('icon-press',
                                   self._on_popped_entry__icon_press)
        self._toggle_calculator_icon()

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
        self.add(frame)
        frame.show()

        alignment = gtk.Alignment(0.5, 0.5, 1.0, 1.0)
        alignment.set_padding(6, 6, 2, 2)
        frame.add(alignment)
        alignment.show()

        vbox = gtk.VBox(spacing=6)
        alignment.add(vbox)
        vbox.show()

        self._main_label = gtk.Label()
        self._main_label.set_ellipsize(pango.ELLIPSIZE_END)
        vbox.pack_start(self._main_label, True, True)
        self._main_label.show()

        self._entry = ProxyEntry()
        # FIXME: We need a model_attribute here or else the entry.is_valid()
        # will always return None. Check proxywidget.py's FIXME to see why
        self._entry.model_attribute = 'not_used'
        self._entry.connect('validate', self._on_entry__validate)
        self._entry.connect_after('changed', self._after_entry__changed)
        self._entry.set_alignment(1.0)
        vbox.pack_start(self._entry, True, True)
        self._entry.show()

        hbox = gtk.HBox(spacing=6)
        vbox.pack_start(hbox, True, True)
        hbox.show()

        self._label = gtk.Label()
        self._label.set_property('xalign', 1.0)
        self._label.set_use_markup(True)
        hbox.pack_start(self._label, True, True)
        self._label.show()

        self._warning = gtk.Image()
        hbox.pack_start(self._warning, False, False)

        self.set_resizable(False)
        self.set_screen(self._popped_entry.get_screen())

    def _update_ui(self):
        try:
            self._new_value = self._data_type(self._popped_entry.get_text())
        except decimal.InvalidOperation:
            return False

        self._entry.set_text('')
        self._entry.set_tooltip_text(_("Use absolute or percentage (%) value"))
        self._preview_new_value()
        self._main_label.set_text(self._get_main_label())

        return True

    def _get_main_label(self):
        if self._mode == self.MODE_ADD:
            return (_("Surcharge") if self._data_type == currency else
                    _("Addition"))
        elif self._mode == self.MODE_SUB:
            return (_("Discount") if self._data_type == currency else
                    _("Subtraction"))
        else:
            raise AssertionError

    def _set_warning(self, warning):
        if warning is None:
            self._warning.hide()
        else:
            self._warning.set_from_stock(gtk.STOCK_DIALOG_WARNING,
                                         gtk.ICON_SIZE_MENU)
            self._warning.set_tooltip_text(warning)
            self._warning.show()

    def _popup_grab_window(self):
        activate_time = 0L
        window = self.get_window()
        grab_status = gtk.gdk.pointer_grab(window, True,
                                           (gtk.gdk.BUTTON_PRESS_MASK |
                                            gtk.gdk.BUTTON_RELEASE_MASK |
                                            gtk.gdk.POINTER_MOTION_MASK),
                                           None, None, activate_time)
        if grab_status == gtk.gdk.GRAB_SUCCESS:
            if gtk.gdk.keyboard_grab(window, True, activate_time) == 0:
                return True
            else:
                window.get_display().pointer_ungrab(activate_time)
                return False

        return False

    def _get_position(self):
        widget = self._popped_entry

        allocation = widget.get_allocation()
        window = widget.get_window()

        if hasattr(window, 'get_root_coords'):
            x = 0
            y = 0
            if not widget.get_has_window():
                x += allocation.x
                y += allocation.y
            x, y = window.get_root_coords(x, y)
        else:
            # PyGTK lacks gdk_window_get_root_coords(),
            # but we can use get_origin() instead, which is the
            # same thing in our case.
            x, y = widget.window.get_origin()

        return x, y + allocation.height, allocation.width, allocation.height

    def _get_new_value(self):
        operation = self._entry.get_text().strip()
        operation = operation.replace(',', '.')

        if operation.endswith('%'):
            op_value = operation[:-1]
            percentage = True
        else:
            op_value = operation
            percentage = False

        if not operation:
            return

        if operation[0] in ['+', '-']:
            raise ValueError(_("Operator signals are not supported..."))

        if self._mode == self.MODE_SUB:
            op = operator.sub
        elif self._mode == self.MODE_ADD:
            op = operator.add

        try:
            op_value = decimal.Decimal(op_value)
        except decimal.InvalidOperation:
            raise ValueError(
                _("'%s' is not a valid operation...") % (operation,))

        if percentage:
            value = op(self._new_value, self._new_value * (op_value / 100))
        else:
            value = op(self._new_value, op_value)

        return value

    def _update_new_value(self):
        if not self._entry.is_valid():
            return

        self._new_value = self._get_new_value()
        self._entry.set_text('')
        self._preview_new_value()

    def _preview_new_value(self):
        self._label.set_markup('<b>%s</b>' % (
            self._converter.as_string(self._new_value), ))

    def _maybe_apply_new_value(self):
        if self._entry.get_text():
            self._update_new_value()
            return

        self._popped_entry.update(self._new_value)

        self.popdown()

    def _toggle_calculator_icon(self):
        if self._popped_entry.get_sensitive():
            pixbuf = self.render_icon(STOQ_CALC, gtk.ICON_SIZE_MENU)
        else:
            pixbuf = None

        self._popped_entry.set_property(self._icon_pos + '-pixbuf', pixbuf)

    #
    #  Callbacks
    #

    def _on__key_press_event(self, window, event):
        keyval = event.keyval
        if keyval == gtk.keysyms.Escape:
            self.popdown()
            return True
        elif keyval in [gtk.keysyms.Return,
                        gtk.keysyms.KP_Enter,
                        gtk.keysyms.Tab]:
            self._maybe_apply_new_value()
            return True

        return False

    def _on__button_press_event(self, window, event):
        # If we're clicking outside of the window
        # close the popup
        if (event.window != self.get_window() or
            (tuple(self.allocation.intersect(
                   gtk.gdk.Rectangle(x=int(event.x), y=int(event.y),
                                     width=1, height=1)))) == (0, 0, 0, 0)):
            self.popdown()

    def _on_entry__validate(self, entry, value):
        try:
            value = self._get_new_value()
        except ValueError as err:
            return ValidationError('%s\n%s' % (err,
                                   _("Use absolute or percentage (%) value")))

        if value:
            warning = self._popped_entry.emit('validate', value)
            warning = warning and str(warning)
        else:
            warning = None

        self._set_warning(warning)

    def _after_entry__changed(self, entry):
        entry.validate(force=True)

    def _on_popped_entry_sensitive__notify(self, obj, pspec):
        self._toggle_calculator_icon()

    def _on_popped_entry__icon_press(self, entry, icon_pos, event):
        if icon_pos != gtk.ENTRY_ICON_SECONDARY:
            return

        self.popup()
Example #56
0
import gtk

from kiwi.ui.widgets.entry import ProxyEntry

def on_entry_activate(entry):
    print 'You selected:', entry.read()
    gtk.main_quit()

win = gtk.Window()
win.connect('delete-event', gtk.main_quit)

vbox = gtk.VBox()
win.add(vbox)

# Normal entry
entry = ProxyEntry()
entry.data_type = str
entry.connect('activate', on_entry_activate)
entry.prefill(['Belo Horizonte', u'São Carlos',
               u'São Paulo',  u'Båstad',
               u'Örnsköldsvik', 'sanca', 'sampa'])
vbox.pack_start(entry)

entry = ProxyEntry()
entry.data_type = int
entry.connect('activate', on_entry_activate)
entry.prefill([('Brazil', 186),
               ('Sweden', 9),
               ('China', 1306)])
vbox.pack_start(entry)
Example #57
0
    def _create_ui(self):
        self.add_events(gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.KEY_PRESS_MASK)
        self.connect('key-press-event', self._on__key_press_event)
        self.connect('button-press-event', self._on__button_press_event)

        # This is done on entry to check where to put the validation/mandatory
        # icons. We should put the calculator on the other side.
        # Note that spinbuttons are always right aligned and thus
        # xalign will always be 1.0
        if self._popped_entry.get_property('xalign') > 0.5:
            self._icon_pos = 'secondary-icon'
        else:
            self._icon_pos = 'primary-icon'

        self._popped_entry.set_property(self._icon_pos + '-activatable', True)
        self._popped_entry.set_property(self._icon_pos + '-tooltip-text',
                                        _("Do calculations on top of this value"))
        self._popped_entry.connect('notify::sensitive',
                                   self._on_popped_entry_sensitive__notify)
        self._popped_entry.connect('icon-press',
                                   self._on_popped_entry__icon_press)
        self._toggle_calculator_icon()

        frame = gtk.Frame()
        frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
        self.add(frame)
        frame.show()

        alignment = gtk.Alignment(0.5, 0.5, 1.0, 1.0)
        alignment.set_padding(6, 6, 2, 2)
        frame.add(alignment)
        alignment.show()

        vbox = gtk.VBox(spacing=6)
        alignment.add(vbox)
        vbox.show()

        self._main_label = gtk.Label()
        self._main_label.set_ellipsize(pango.ELLIPSIZE_END)
        vbox.pack_start(self._main_label, True, True)
        self._main_label.show()

        self._entry = ProxyEntry()
        # FIXME: We need a model_attribute here or else the entry.is_valid()
        # will always return None. Check proxywidget.py's FIXME to see why
        self._entry.model_attribute = 'not_used'
        self._entry.connect('validate', self._on_entry__validate)
        self._entry.connect_after('changed', self._after_entry__changed)
        self._entry.set_alignment(1.0)
        vbox.pack_start(self._entry, True, True)
        self._entry.show()

        hbox = gtk.HBox(spacing=6)
        vbox.pack_start(hbox, True, True)
        hbox.show()

        self._label = gtk.Label()
        self._label.set_property('xalign', 1.0)
        self._label.set_use_markup(True)
        hbox.pack_start(self._label, True, True)
        self._label.show()

        self._warning = gtk.Image()
        hbox.pack_start(self._warning, False, False)

        self.set_resizable(False)
        self.set_screen(self._popped_entry.get_screen())
Example #58
0
class CalculatorPopup(PopupWindow):
    """A popup calculator for entries

    Right now it supports both
    :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton` and
    :class:`kiwi.ui.widgets.entry.ProxyEntry`, as long as their
    data types are numeric (e.g. int, currency, Decimal, etc)
    """

    #: The add mode. Any value typed on the entry will be added to the
    #: original value. e.g. 10% means +10%
    MODE_ADD = 0

    #: The sub mode. Any value typed on the entry will be subtracted from the
    #: original value. e.g. 10% means -10%
    MODE_SUB = 1

    _mode = None
    _data_type_mapper = {
        'currency': currency,
        'Decimal': decimal.Decimal,
    }

    def __init__(self, entry, mode):
        """
        :param entry: a :class:`kiwi.ui.widgets.spinbutton.ProxySpinButton`
            or a :class:`kiwi.ui.widgets.entry.ProxyEntry` subclass
        :param mode: one of :attr:`.MODE_ADD`, :attr:`.MODE_SUB`
        """
        self._mode = mode
        self._new_value = None
        self._data_type = self._data_type_mapper[entry.data_type]
        self._converter = converter.get_converter(self._data_type)

        super(CalculatorPopup, self).__init__(entry)

    #
    # Public API
    #

    def get_main_widget(self):
        # This is done on entry to check where to put the validation/mandatory
        # icons. We should put the calculator on the other side.
        # Note that spinbuttons are always right aligned and thus
        # xalign will always be 1.0
        if self.attached_widget.get_alignment() > 0.5:
            self._icon_pos = 'secondary-icon'
        else:
            self._icon_pos = 'primary-icon'

        self.attached_widget.set_property(self._icon_pos + '-activatable',
                                          True)
        self.attached_widget.set_property(
            self._icon_pos + '-tooltip-text',
            _("Do calculations on top of this value"))
        self.attached_widget.connect('notify::sensitive',
                                     self._on_entry_sensitive__notify)
        self.attached_widget.connect('icon-press', self._on_entry__icon_press)
        self._toggle_calculator_icon()

        vbox = Gtk.VBox(spacing=6)
        vbox.show()

        self._main_label = Gtk.Label()
        self._main_label.set_ellipsize(Pango.EllipsizeMode.END)
        vbox.pack_start(self._main_label, True, True, 0)
        self._main_label.show()

        self._entry = ProxyEntry()
        # FIXME: We need a model_attribute here or else the entry.is_valid()
        # will always return None. Check proxywidget.py's FIXME to see why
        self._entry.model_attribute = 'not_used'
        self._entry.connect('validate', self._on_entry__validate)
        self._entry.connect_after('changed', self._after_entry__changed)
        self._entry.set_alignment(1.0)
        vbox.pack_start(self._entry, True, True, 0)
        self._entry.show()

        hbox = Gtk.HBox(spacing=6)
        vbox.pack_start(hbox, True, True, 0)
        hbox.show()

        self._label = Gtk.Label()
        self._label.set_property('xalign', 1.0)
        self._label.set_use_markup(True)
        hbox.pack_start(self._label, True, True, 0)
        self._label.show()

        self._warning = Gtk.Image()
        hbox.pack_start(self._warning, False, False, 0)

        return vbox

    def validate_popup(self):
        try:
            self._new_value = self._data_type(self.attached_widget.get_text())
        except decimal.InvalidOperation:
            return False

        self._entry.set_text('')
        self._entry.set_tooltip_text(_("Use absolute or percentage (%) value"))
        self._preview_new_value()
        self._main_label.set_text(self._get_main_label())

        return True

    def confirm(self):
        self._maybe_apply_new_value()

    #
    #  Private
    #

    def _get_main_label(self):
        if self._mode == self.MODE_ADD:
            return (_("Surcharge")
                    if self._data_type == currency else _("Addition"))
        elif self._mode == self.MODE_SUB:
            return (_("Discount")
                    if self._data_type == currency else _("Subtraction"))
        else:
            raise AssertionError

    def _set_warning(self, warning):
        if warning is None:
            self._warning.hide()
        else:
            self._warning.set_from_stock(Gtk.STOCK_DIALOG_WARNING,
                                         Gtk.IconSize.MENU)
            self._warning.set_tooltip_text(warning)
            self._warning.show()

    def _get_new_value(self):
        operation = self._entry.get_text().strip()
        operation = operation.replace(',', '.')

        if operation.endswith('%'):
            op_value = operation[:-1]
            percentage = True
        else:
            op_value = operation
            percentage = False

        if not operation:
            return

        if operation[0] in ['+', '-']:
            raise ValueError(_("Operator signals are not supported..."))

        if self._mode == self.MODE_SUB:
            op = operator.sub
        elif self._mode == self.MODE_ADD:
            op = operator.add

        try:
            op_value = decimal.Decimal(op_value)
        except decimal.InvalidOperation:
            raise ValueError(
                _("'%s' is not a valid operation...") % (operation, ))

        if percentage:
            value = op(self._new_value, self._new_value * (op_value / 100))
        else:
            value = op(self._new_value, op_value)

        return value

    def _update_new_value(self):
        if not self._entry.is_valid():
            return

        self._new_value = self._get_new_value()
        self._entry.set_text('')
        self._preview_new_value()

    def _preview_new_value(self):
        self._label.set_markup('<b>%s</b>' %
                               (self._converter.as_string(self._new_value), ))

    def _maybe_apply_new_value(self):
        if self._entry.get_text():
            self._update_new_value()
            return

        self.attached_widget.update(self._new_value)

        self.popdown()

    def _toggle_calculator_icon(self):
        if self.attached_widget.get_sensitive():
            pixbuf = self.render_icon(STOQ_CALC, Gtk.IconSize.MENU)
        else:
            pixbuf = None

        self.attached_widget.set_property(self._icon_pos + '-pixbuf', pixbuf)

    #
    #  Callbacks
    #

    def _on_entry__validate(self, entry, value):
        try:
            value = self._get_new_value()
        except ValueError as err:
            return ValidationError(
                '%s\n%s' % (err, _("Use absolute or percentage (%) value")))

        if value:
            warning = self.attached_widget.emit('validate', value)
            warning = warning and str(warning)
        else:
            warning = None

        self._set_warning(warning)

    def _after_entry__changed(self, entry):
        entry.validate(force=True)

    def _on_entry_sensitive__notify(self, obj, pspec):
        self._toggle_calculator_icon()

    def _on_entry__icon_press(self, entry, icon_pos, event):
        if icon_pos != Gtk.EntryIconPosition.SECONDARY:
            return

        self.popup()
Example #59
0
    def _setup_entry_slave(self, box=None):
        widget = ProxyEntry()
        # Try to simulate insensitive appearance for non-editable entries
        # while keeping them selectable
        widget.set_editable(self.sensitive)
        if not self.sensitive:
            style = widget.get_style()
            widget.modify_text(
                gtk.STATE_NORMAL, style.text[gtk.STATE_INSENSITIVE])
            widget.modify_base(
                gtk.STATE_NORMAL, style.base[gtk.STATE_INSENSITIVE])
        widget.data_type = unicode
        widget.model_attribute = "field_value"
        self.proxy.add_widget("field_value", widget)
        if box is None:
            self.container.add(widget)
        else:
            box.pack_start(widget)

        widget.show()
        widget.connect('validate', self._on_entry__validate)
        widget.connect('validation-changed',
                       self._on_entry__validation_changed)

        self._entry = widget
Example #60
0
    def __init__(self, property_class, app):
        EditorProperty.__init__(self, property_class, app)

        self.input = ProxyEntry()
        self.input.set_property('data-type', str)
        self.input.connect('changed', self._changed_text)