1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the OpenSSL
22
# You must obey the GNU General Public License in all respects for all of
23
# the code used other than OpenSSL. If you modify file(s) with this
24
# exception, you may extend this exception to your version of the file(s),
25
# but you are not obligated to do so. If you do not wish to do so, delete
26
# this exception statement from your version. If you delete this exception
27
# statement from all source files in the program, then also delete it here.
29
"""miro.frontends.widgets.gtk.controls -- Control Widgets."""
37
from miro import searchengines
38
from miro.frontends.widgets import widgetconst
39
from miro.frontends.widgets.gtk.base import Widget
40
from miro.frontends.widgets.gtk.simple import Label
42
class BinBaselineCalculator(object):
43
"""Mixin class that defines the baseline method for gtk.Bin subclasses,
44
where the child is the label that we are trying to get the baseline for.
48
my_size = self._widget.size_request()
49
child_size = self._widget.child.size_request()
50
ypad = (my_size[1] - child_size[1]) / 2
52
pango_context = self._widget.get_pango_context()
53
metrics = pango_context.get_metrics(self._widget.style.font_desc)
54
return pango.PIXELS(metrics.get_descent()) + ypad
56
class TextEntry(Widget):
57
entry_class = gtk.Entry
58
def __init__(self, initial_text=None):
60
self.create_signal('activate')
61
self.create_signal('changed')
62
self.create_signal('validate')
63
self.set_widget(self.entry_class())
64
self.forward_signal('activate')
65
self.forward_signal('changed')
66
if initial_text is not None:
67
self.set_text(initial_text)
70
self._widget.grab_focus()
72
def set_text(self, text):
73
self._widget.set_text(text)
76
return self._widget.get_text().decode('utf-8')
78
def set_width(self, chars):
79
self._widget.set_width_chars(chars)
81
def set_invisible(self, setting):
82
self._widget.props.visibility = not setting
84
def set_activates_default(self, setting):
85
self._widget.set_activates_default(setting)
88
layout_height = pango.PIXELS(self._widget.get_layout().get_size()[1])
89
ypad = (self._widget.size_request()[1] - layout_height) / 2
90
pango_context = self._widget.get_pango_context()
91
metrics = pango_context.get_metrics(self._widget.style.font_desc)
92
return pango.PIXELS(metrics.get_descent()) + ypad
94
class SecureTextEntry(TextEntry):
95
def __init__(self, initial_text=None):
96
TextEntry.__init__(self, initial_text)
97
self.set_invisible(True)
99
class MultilineTextEntry(Widget):
100
entry_class = gtk.TextView
101
def __init__(self, initial_text=None):
102
Widget.__init__(self)
103
self.set_widget(self.entry_class())
104
if initial_text is not None:
105
self.set_text(initial_text)
106
self._widget.set_wrap_mode(gtk.WRAP_WORD)
109
self._widget.grab_focus()
111
def set_text(self, text):
112
self._widget.get_buffer().set_text(text)
115
buffer_ = self._widget.get_buffer()
116
return buffer_.get_text(*(buffer_.get_bounds())).decode('utf-8')
120
layout_height = pango.PIXELS(self._widget.get_layout().get_size()[1])
121
ypad = (self._widget.size_request()[1] - layout_height) / 2
122
pango_context = self._widget.get_pango_context()
123
metrics = pango_context.get_metrics(self._widget.style.font_desc)
124
return pango.PIXELS(metrics.get_descent()) + ypad
126
class Checkbox(Widget, BinBaselineCalculator):
127
"""Widget that the user can toggle on or off."""
129
def __init__(self, label):
130
Widget.__init__(self)
131
self.set_widget(gtk.CheckButton(label))
132
self.create_signal('toggled')
133
self.forward_signal('toggled')
135
def get_checked(self):
136
return self._widget.get_active()
138
def set_checked(self, value):
139
self._widget.set_active(value)
141
class RadioButtonGroup:
144
Create the group, then create a bunch of RadioButtons passing in the group.
149
def add_button(self, button):
150
self._buttons.append(button)
152
def get_buttons(self):
155
def get_selected(self):
156
for mem in self._buttons:
157
if mem.get_selected():
160
def set_selected(self, button):
161
for mem in self._buttons:
163
mem._widget.set_active(True)
165
mem._widget.set_active(False)
168
# use a weakref so that we're not creating circular references between
169
# RadioButtons and RadioButtonGroups
170
radio_button_to_group_mapping = weakref.WeakValueDictionary()
172
class RadioButton(Widget, BinBaselineCalculator):
174
def __init__(self, label, group=None):
175
Widget.__init__(self)
176
self.set_widget(gtk.RadioButton(label=label))
177
self.create_signal('clicked')
178
self.forward_signal('clicked')
181
buttons = group.get_buttons()
183
self._widget.set_group(buttons[0]._widget)
185
group = RadioButtonGroup()
187
group.add_button(self)
189
radio_button_to_group_mapping[oid] = group
192
return radio_button_to_group_mapping[id(self)]
194
def get_selected(self):
195
return self._widget.get_active()
197
def set_selected(self):
198
radio_button_to_group_mapping[id(self)].set_selected(self)
200
class Button(Widget, BinBaselineCalculator):
201
def __init__(self, text, style='normal'):
202
Widget.__init__(self)
203
# We just ignore style here, GTK users expect their own buttons.
204
self.set_widget(gtk.Button())
205
self.create_signal('clicked')
206
self.forward_signal('clicked')
207
self.label = Label(text)
208
self._widget.add(self.label._widget)
209
self.label._widget.show()
211
def set_text(self, title):
212
self.label.set_text(title)
214
def set_bold(self, bold):
215
self.label.set_bold(bold)
217
def set_size(self, scale_factor):
218
self.label.set_size(scale_factor)
220
def set_color(self, color):
221
self.label.set_color(color)
223
class OptionMenu(Widget):
224
def __init__(self, options):
225
Widget.__init__(self)
226
self.create_signal('changed')
228
self.set_widget(gtk.ComboBox(gtk.ListStore(str, str)))
229
self.cell = gtk.CellRendererText()
230
self._widget.pack_start(self.cell, True)
231
self._widget.add_attribute(self.cell, 'text', 0)
233
for option in options:
234
self._widget.get_model().append((option, 'booya'))
235
self._widget.set_active(0)
236
self.options = options
237
self.wrapped_widget_connect('changed', self.on_changed)
240
my_size = self._widget.size_request()
241
child_size = self._widget.child.size_request()
242
ypad = self.cell.props.ypad + (my_size[1] - child_size[1]) / 2
244
pango_context = self._widget.get_pango_context()
245
metrics = pango_context.get_metrics(self._widget.style.font_desc)
246
return pango.PIXELS(metrics.get_descent()) + ypad
248
def set_bold(self, bold):
250
self.cell.props.weight = pango.WEIGHT_BOLD
252
self.cell.props.weight = pango.WEIGHT_NORMAL
254
def set_size(self, size):
255
if size == widgetconst.SIZE_NORMAL:
256
self.cell.props.scale = 1
258
self.cell.props.scale = 0.85
260
def set_color(self, color):
261
self.cell.props.foreground_gdk = self.make_color(color)
263
def set_selected(self, index):
264
self._widget.set_active(index)
266
def get_selected(self):
267
return self._widget.get_active()
269
def on_changed(self, widget):
270
index = widget.get_active()
271
self.emit('changed', index)