aboutsummaryrefslogtreecommitdiffstats
path: root/lvc/widgets/gtk/controls.py
diff options
context:
space:
mode:
Diffstat (limited to 'lvc/widgets/gtk/controls.py')
-rw-r--r--lvc/widgets/gtk/controls.py337
1 files changed, 337 insertions, 0 deletions
diff --git a/lvc/widgets/gtk/controls.py b/lvc/widgets/gtk/controls.py
new file mode 100644
index 0000000..26ce6d6
--- /dev/null
+++ b/lvc/widgets/gtk/controls.py
@@ -0,0 +1,337 @@
+# @Base: Miro - an RSS based video player application
+# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011
+# Participatory Culture Foundation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+# In addition, as a special exception, the copyright holders give
+# permission to link the code of portions of this program with the OpenSSL
+# library.
+#
+# You must obey the GNU General Public License in all respects for all of
+# the code used other than OpenSSL. If you modify file(s) with this
+# exception, you may extend this exception to your version of the file(s),
+# but you are not obligated to do so. If you do not wish to do so, delete
+# this exception statement from your version. If you delete this exception
+# statement from all source files in the program, then also delete it here.
+
+""".controls -- Control Widgets."""
+
+import gtk
+import pango
+
+from lvc.widgets import widgetconst
+import layout
+from .base import Widget
+from .simple import Label
+
+class BinBaselineCalculator(object):
+ """Mixin class that defines the baseline method for gtk.Bin subclasses,
+ where the child is the label that we are trying to get the baseline for.
+ """
+
+ def baseline(self):
+ my_size = self._widget.size_request()
+ child_size = self._widget.child.size_request()
+ ypad = (my_size[1] - child_size[1]) / 2
+
+ pango_context = self._widget.get_pango_context()
+ metrics = pango_context.get_metrics(self._widget.style.font_desc)
+ return pango.PIXELS(metrics.get_descent()) + ypad
+
+class TextEntry(Widget):
+ entry_class = gtk.Entry
+ def __init__(self, initial_text=None):
+ Widget.__init__(self)
+ self.create_signal('activate')
+ self.create_signal('changed')
+ self.create_signal('validate')
+ self.set_widget(self.entry_class())
+ self.forward_signal('activate')
+ self.forward_signal('changed')
+ if initial_text is not None:
+ self.set_text(initial_text)
+
+ def focus(self):
+ self._widget.grab_focus()
+
+ def start_editing(self, text):
+ self.set_text(text)
+ self.focus()
+ self._widget.emit('move-cursor', gtk.MOVEMENT_BUFFER_ENDS, 1, False)
+
+ def set_text(self, text):
+ self._widget.set_text(text)
+
+ def get_text(self):
+ return self._widget.get_text().decode('utf-8')
+
+ def set_max_length(self, chars):
+ self._widget.set_max_length(chars)
+
+ def set_width(self, chars):
+ self._widget.set_width_chars(chars)
+
+ def set_invisible(self, setting):
+ self._widget.props.visibility = not setting
+
+ def set_activates_default(self, setting):
+ self._widget.set_activates_default(setting)
+
+ def baseline(self):
+ layout_height = pango.PIXELS(self._widget.get_layout().get_size()[1])
+ ypad = (self._widget.size_request()[1] - layout_height) / 2
+ pango_context = self._widget.get_pango_context()
+ metrics = pango_context.get_metrics(self._widget.style.font_desc)
+ return pango.PIXELS(metrics.get_descent()) + ypad
+
+
+class NumberEntry(TextEntry):
+ def __init__(self, initial_text=None):
+ TextEntry.__init__(self, initial_text)
+ self._widget.connect('changed', self.validate)
+ self.previous_text = initial_text or ""
+
+ def validate(self, entry):
+ text = self.get_text()
+ if text.isdigit() or not text:
+ self.previous_text = text
+ else:
+ self._widget.set_text(self.previous_text)
+
+class SecureTextEntry(TextEntry):
+ def __init__(self, initial_text=None):
+ TextEntry.__init__(self, initial_text)
+ self.set_invisible(True)
+
+class MultilineTextEntry(Widget):
+ entry_class = gtk.TextView
+ def __init__(self, initial_text=None, border=False):
+ Widget.__init__(self)
+ self.set_widget(self.entry_class())
+ if initial_text is not None:
+ self.set_text(initial_text)
+ self._widget.set_wrap_mode(gtk.WRAP_WORD)
+ self._widget.set_accepts_tab(False)
+ self.border = border
+
+ def focus(self):
+ self._widget.grab_focus()
+
+ def set_text(self, text):
+ self._widget.get_buffer().set_text(text)
+
+ def get_text(self):
+ buffer_ = self._widget.get_buffer()
+ return buffer_.get_text(*(buffer_.get_bounds())).decode('utf-8')
+
+ def baseline(self):
+ # FIXME
+ layout_height = pango.PIXELS(self._widget.get_layout().get_size()[1])
+ ypad = (self._widget.size_request()[1] - layout_height) / 2
+ pango_context = self._widget.get_pango_context()
+ metrics = pango_context.get_metrics(self._widget.style.font_desc)
+ return pango.PIXELS(metrics.get_descent()) + ypad
+
+ def set_editable(self, editable):
+ self._widget.set_editable(editable)
+
+class Checkbox(Widget, BinBaselineCalculator):
+ """Widget that the user can toggle on or off."""
+
+ def __init__(self, text=None, bold=False, color=None):
+ Widget.__init__(self)
+ BinBaselineCalculator.__init__(self)
+ if text is None:
+ text = ''
+ self.set_widget(gtk.CheckButton())
+ self.label = Label(text, color=color)
+ self._widget.add(self.label._widget)
+ self.label._widget.show()
+ self.create_signal('toggled')
+ self.forward_signal('toggled')
+ if bold:
+ self.label.set_bold(True)
+
+ def get_checked(self):
+ return self._widget.get_active()
+
+ def set_checked(self, value):
+ self._widget.set_active(value)
+
+ def set_size(self, scale_factor):
+ self.label.set_size(scale_factor)
+
+ def get_text_padding(self):
+ """
+ Returns the amount of space the checkbox takes up before the label.
+ """
+ indicator_size = self._widget.style_get_property('indicator-size')
+ indicator_spacing = self._widget.style_get_property(
+ 'indicator-spacing')
+ focus_width = self._widget.style_get_property('focus-line-width')
+ focus_padding = self._widget.style_get_property('focus-padding')
+ return (indicator_size + 3 * indicator_spacing + 2 * (focus_width +
+ focus_padding))
+
+class RadioButtonGroup(Widget, BinBaselineCalculator):
+ """RadioButtonGroup.
+
+ Create the group, then create a bunch of RadioButtons passing in the group.
+
+ NB: GTK has built-in radio button grouping functionality, and we should
+ be using that but we need this widget for portable code. We create
+ a dummy GTK radio button and make this the "root" button which gets
+ inherited by all buttons in this radio button group.
+ """
+ def __init__(self):
+ Widget.__init__(self)
+ BinBaselineCalculator.__init__(self)
+ self.set_widget(gtk.RadioButton(label=""))
+ self._widget.set_active(False)
+ self._buttons = []
+
+ def add_button(self, button):
+ self._buttons.append(button)
+
+ def get_buttons(self):
+ return self._buttons
+
+ def get_selected(self):
+ for mem in self._buttons:
+ if mem.get_selected():
+ return mem
+
+ def set_selected(self, button):
+ for mem in self._buttons:
+ if mem is button:
+ mem._widget.set_active(True)
+ else:
+ mem._widget.set_active(False)
+
+class RadioButton(Widget, BinBaselineCalculator):
+ """RadioButton."""
+ def __init__(self, label, group=None, color=None):
+ Widget.__init__(self)
+ BinBaselineCalculator.__init__(self)
+ if group:
+ self.group = group
+ else:
+ self.group = RadioButtonGroup()
+ self.set_widget(gtk.RadioButton(group=self.group._widget))
+ self.label = Label(label, color=color)
+ self._widget.add(self.label._widget)
+ self.label._widget.show()
+ self.create_signal('clicked')
+ self.forward_signal('clicked')
+
+ group.add_button(self)
+
+ def set_size(self, size):
+ self.label.set_size(size)
+
+ def get_group(self):
+ return self.group
+
+ def get_selected(self):
+ return self._widget.get_active()
+
+ def set_selected(self):
+ self.group.set_selected(self)
+
+class Button(Widget, BinBaselineCalculator):
+ def __init__(self, text, style='normal', width=None):
+ Widget.__init__(self)
+ BinBaselineCalculator.__init__(self)
+ # We just ignore style here, GTK users expect their own buttons.
+ self.set_widget(gtk.Button())
+ self.create_signal('clicked')
+ self.forward_signal('clicked')
+ self.label = Label(text)
+ # only honor width if its bigger than the width we need to display the
+ # label (#18994)
+ if width and width > self.label.get_width():
+ alignment = layout.Alignment(0.5, 0.5, 0, 0)
+ alignment.set_size_request(width, -1)
+ alignment.add(self.label)
+ self._widget.add(alignment._widget)
+ else:
+ self._widget.add(self.label._widget)
+ self.label._widget.show()
+
+ def set_text(self, title):
+ self.label.set_text(title)
+
+ def set_bold(self, bold):
+ self.label.set_bold(bold)
+
+ def set_size(self, scale_factor):
+ self.label.set_size(scale_factor)
+
+ def set_color(self, color):
+ self.label.set_color(color)
+
+class OptionMenu(Widget):
+ def __init__(self, options):
+ Widget.__init__(self)
+ self.create_signal('changed')
+
+ self.set_widget(gtk.ComboBox(gtk.ListStore(str, str)))
+ self.cell = gtk.CellRendererText()
+ self._widget.pack_start(self.cell, True)
+ self._widget.add_attribute(self.cell, 'text', 0)
+ if options:
+ for option, value in options:
+ self._widget.get_model().append((option, value))
+ self._widget.set_active(0)
+ self.options = options
+ self.wrapped_widget_connect('changed', self.on_changed)
+
+ def baseline(self):
+ my_size = self._widget.size_request()
+ child_size = self._widget.child.size_request()
+ ypad = self.cell.props.ypad + (my_size[1] - child_size[1]) / 2
+
+ pango_context = self._widget.get_pango_context()
+ metrics = pango_context.get_metrics(self._widget.style.font_desc)
+ return pango.PIXELS(metrics.get_descent()) + ypad
+
+ def set_bold(self, bold):
+ if bold:
+ self.cell.props.weight = pango.WEIGHT_BOLD
+ else:
+ self.cell.props.weight = pango.WEIGHT_NORMAL
+
+ def set_size(self, size):
+ if size == widgetconst.SIZE_NORMAL:
+ self.cell.props.scale = 1
+ else:
+ self.cell.props.scale = 0.75
+
+ def set_color(self, color):
+ self.cell.props.foreground_gdk = self.make_color(color)
+
+ def set_selected(self, index):
+ self._widget.set_active(index)
+
+ def get_selected(self):
+ return self._widget.get_active()
+
+ def on_changed(self, widget):
+ index = widget.get_active()
+ self.emit('changed', index)
+
+ def set_width(self, width):
+ self._widget.set_property('width-request', width)