1 """The OptionsBox widget is used to edit an OptionGroup.
2 For simple applications, rox.edit_options() provides an
3 easy way to edit the options.
5 You can add new types of option by appending to widget_registry (new
6 in ROX-Lib 1.9.13). Return a list of widgets (which are packed into either an
7 HBox or a VBox). For example, to add a button widget:
9 def build_button(box, node, label):
10 button = g.Button(label)
11 box.may_add_tip(button, node)
12 button.connect('clicked', my_button_handler)
14 OptionsBox.widget_registry['button'] = build_button
16 You can then create such a button in Options.xml with:
18 <button label='...'>Tooltip</button>
20 Any element may have a 'size-group' attribute. Certain widgets (labels in
21 particular) in the same size group all have the same size.
23 For widgets that have options, your build function will be called with
24 the option as a third parameter. You should register get and set methods,
25 and arrange for box.check_widget to be called when the user changes the
28 def build_toggle(box, node, label, option):
29 toggle = g.CheckButton(label)
30 box.may_add_tip(toggle, node)
32 box.handlers[option] = (
33 lambda: str(toggle.get_active()),
34 lambda: toggle.set_active(option.int_value))
36 toggle.connect('toggled', lambda w: box.check_widget(option))
39 OptionsBox.widget_registry['mytoggle'] = build_toggle
42 from rox
import g
, options
, _
44 from xml
.dom
import Node
, minidom
49 # Functions for extracting data from XML nodes
51 """Return all the text directly inside this DOM Node."""
52 return ''.join([text
.nodeValue
for text
in node
.childNodes
53 if text
.nodeType
== Node
.TEXT_NODE
])
55 def bool_attr(node
, name
, val
=False):
56 """Interpret node attribute as a boolean value"""
58 v
=node
.getAttribute(name
)
67 def str_attr(node
, name
, val
=''):
68 """Get string value of node attribute"""
70 val
=node
.getAttribute(name
)
75 class OptionsBox(g
.Dialog
):
76 """A dialog box which lets the user edit the options. The file
77 Options.xml specifies the layout of this box."""
79 tips
= None # GtkTooltips
80 options
= None # The OptionGroup we are editing
81 revert
= None # Option -> old value
82 handlers
= None # Option -> (get, set)
83 trans
= None # Translation function (application's, not ROX-Lib's)
85 def __init__(self
, options_group
, options_xml
, translation
= None):
86 """options_xml is an XML file, usually <app_dir>/Options.xml,
87 which defines the layout of the OptionsBox.
89 It contains an <options> root element containing (nested)
90 <section> elements. Each <section> contains a number of widgets,
91 some of which correspond to options. The build_* functions are
98 <section title='First section'>
99 <label>Here are some options</label>
100 <entry name='default_name' label='Default file name'>
101 When saving an untitled file, use this name as the default.
103 <section title='Nested section'>
109 assert isinstance(options_group
, options
.OptionGroup
)
111 if translation
is None:
113 if hasattr(__main__
.__builtins
__, '_'):
114 translation
= __main__
.__builtins
__._
116 translation
= lambda x
: x
117 self
.trans
= translation
119 g
.Dialog
.__init
__(self
)
120 self
.tips
= g
.Tooltips()
121 self
.set_has_separator(False)
123 self
.options
= options_group
124 self
.set_title((_('%s options')) % options_group
.program
)
125 self
.set_position(g
.WIN_POS_CENTER
)
127 button
= rox
.ButtonMixed(g
.STOCK_UNDO
, _('_Revert'))
128 self
.add_action_widget(button
, REVERT
)
129 self
.tips
.set_tip(button
, _('Restore all options to how they were '
130 'when the window was opened'))
132 self
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
134 self
.set_default_response(g
.RESPONSE_OK
)
136 doc
= minidom
.parse(options_xml
)
137 assert doc
.documentElement
.localName
== 'options'
139 self
.handlers
= {} # Option -> (get, set)
140 self
.revert
= {} # Option -> old value
141 self
.size_groups
= {} # Name -> GtkSizeGroup
142 self
.current_size_group
= None
145 for section
in doc
.documentElement
.childNodes
:
146 if section
.nodeType
!= Node
.ELEMENT_NODE
:
148 if section
.localName
!= 'section':
149 print "Unknown section", section
151 sections
.append(section
)
153 self
.build_window_frame(add_frame
= len(sections
) > 1)
156 for section
in sections
:
157 self
.build_section(section
, None)
158 if len(sections
) > 1:
159 self
.tree_view
.expand_all()
161 self
.sections_swin
.hide()
165 def destroyed(widget
):
171 rox
.report_exception()
172 self
.connect('destroy', destroyed
)
174 def got_response(widget
, response
):
175 if response
== int(g
.RESPONSE_OK
):
177 elif response
== REVERT
:
178 for o
in self
.options
:
179 o
._set
(self
.revert
[o
])
180 self
.update_widgets()
181 self
.options
.notify()
183 self
.connect('response', got_response
)
186 """Show the window, updating all the widgets at the same
187 time. Use this instead of show()."""
189 for option
in self
.options
:
190 self
.revert
[option
] = option
.value
191 self
.update_widgets()
195 def update_revert(self
):
196 "Shade/unshade the Revert button. Internal."
197 self
.set_response_sensitive(REVERT
, self
.changed())
200 """Check whether any options have different values (ie, whether Revert
201 will do anything)."""
202 for option
in self
.options
:
203 if option
.value
!= self
.revert
[option
]:
207 def update_widgets(self
):
208 "Make widgets show current values. Internal."
209 assert not self
.updating
213 for option
in self
.options
:
215 handler
= self
.handlers
[option
][1]
217 print "No widget for option '%s'!" % option
223 def build_window_frame(self
, add_frame
= True):
224 "Create the main structure of the window."
225 hbox
= g
.HBox(False, 4)
226 self
.vbox
.pack_start(hbox
, True, True, 0)
228 # scrolled window for the tree view
229 sw
= g
.ScrolledWindow()
230 sw
.set_shadow_type(g
.SHADOW_IN
)
231 sw
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
232 hbox
.pack_start(sw
, False, True, 0)
233 self
.sections_swin
= sw
# Used to hide it...
236 model
= g
.TreeStore(gobject
.TYPE_STRING
, gobject
.TYPE_INT
)
237 tv
= g
.TreeView(model
)
238 sel
= tv
.get_selection()
239 sel
.set_mode(g
.SELECTION_BROWSE
)
240 tv
.set_headers_visible(False)
241 self
.sections
= model
243 tv
.unset_flags(g
.CAN_FOCUS
) # Stop irritating highlight
245 # Add a column to display column 0 of the store...
246 cell
= g
.CellRendererText()
247 column
= g
.TreeViewColumn('Section', cell
, text
= 0)
248 tv
.append_column(column
)
253 notebook
= g
.Notebook()
254 notebook
.set_show_tabs(False)
255 notebook
.set_show_border(False)
256 self
.notebook
= notebook
260 frame
.set_shadow_type(g
.SHADOW_IN
)
261 hbox
.pack_start(frame
, True, True, 0)
264 hbox
.pack_start(notebook
, True, True, 0)
267 # (sel = sel; pygtk bug?)
268 def change_page(tv
, sel
= sel
, notebook
= notebook
):
269 selected
= sel
.get_selected()
272 model
, titer
= selected
273 page
= model
.get_value(titer
, 1)
275 notebook
.set_current_page(page
)
276 sel
.connect('changed', change_page
)
280 def check_widget(self
, option
):
281 "A widgets call this when the user changes its value."
285 assert isinstance(option
, options
.Option
)
287 new
= self
.handlers
[option
][0]()
289 if new
== option
.value
:
293 self
.options
.notify()
296 def build_section(self
, section
, parent
):
297 """Create a new page for the notebook and a new entry in the
298 sections tree, and build all the widgets inside the page."""
299 page
= g
.VBox(False, 4)
300 page
.set_border_width(4)
301 self
.notebook
.append_page(page
, g
.Label('unused'))
303 titer
= self
.sections
.append(parent
)
304 self
.sections
.set(titer
,
305 0, self
.trans(section
.getAttribute('title')),
306 1, self
.notebook
.page_num(page
))
307 for node
in section
.childNodes
:
308 if node
.nodeType
!= Node
.ELEMENT_NODE
:
310 name
= node
.localName
311 if name
== 'section':
312 self
.build_section(node
, titer
)
314 self
.build_widget(node
, page
)
317 def build_widget(self
, node
, box
):
318 """Dispatches the job of dealing with a DOM Node to the
319 appropriate build_* function."""
320 label
= node
.getAttribute('label')
321 name
= node
.getAttribute('name')
323 label
= self
.trans(label
)
325 old_size_group
= self
.current_size_group
326 sg
= node
.getAttributeNode('size-group')
328 self
.current_size_group
= sg
.value
or None
333 option
= self
.options
.options
[name
]
335 raise Exception("Unknown option '%s'" % name
)
337 # Check for a new-style function in the registry...
338 new_fn
= widget_registry
.get(node
.localName
, None)
340 # Wrap it up so it appears old-style
341 fn
= lambda *args
: new_fn(self
, *args
)
343 # Not in the registry... look in the class instead
345 name
= node
.localName
.replace('-', '_')
346 fn
= getattr(self
, 'build_' + name
)
347 except AttributeError:
348 fn
= self
.build_unknown
351 widgets
= fn(node
, label
, option
)
353 widgets
= fn(node
, label
)
355 box
.pack_start(w
, False, True, 0)
357 self
.current_size_group
= old_size_group
359 def may_add_tip(self
, widget
, node
):
360 """If 'node' contains any text, use that as the tip for 'widget'."""
362 data
= ''.join([n
.nodeValue
for n
in node
.childNodes
if n
.nodeType
== Node
.TEXT_NODE
]).strip()
366 self
.tips
.set_tip(widget
, self
.trans(data
))
368 def get_size_group(self
, name
):
369 """Return the GtkSizeGroup for this name, creating one
370 if it doesn't currently exist."""
372 return self
.size_groups
[name
]
374 group
= g
.SizeGroup(g
.SIZE_GROUP_HORIZONTAL
)
375 self
.size_groups
[name
] = group
378 def make_sized_label(self
, label
, suffix
= ""):
379 """Create a GtkLabel and add it to the current size-group, if any"""
380 widget
= g
.Label(label
)
381 if self
.current_size_group
:
382 widget
.set_alignment(1.0, 0.5)
383 group
= self
.get_size_group(self
.current_size_group
+ suffix
)
384 group
.add_widget(widget
)
387 # Each type of widget has a method called 'build_NAME' where name is
388 # the XML element name. This method is called as method(node, label,
389 # option) if it corresponds to an Option, or method(node, label)
390 # otherwise. It should return a list of widgets to add to the window
391 # and, if it's for an Option, set self.handlers[option] = (get, set).
393 def build_unknown(self
, node
, label
, option
= None):
394 return [g
.Label("Unknown widget type <%s>" % node
.localName
)]
396 def build_label(self
, node
, label
):
397 help_flag
= int(node
.getAttribute('help') or '0')
398 widget
= self
.make_sized_label(self
.trans(data(node
)))
400 widget
.set_alignment(0, 0.5)
402 widget
.set_alignment(0, 1)
403 widget
.set_justify(g
.JUSTIFY_LEFT
)
404 widget
.set_line_wrap(True)
407 hbox
= g
.HBox(False, 4)
409 image
.set_from_stock(g
.STOCK_DIALOG_INFO
,
411 align
= g
.Alignment(0, 0, 0, 0)
414 hbox
.pack_start(align
, False, True, 0)
415 hbox
.pack_start(widget
, False, True, 0)
417 spacer
= g
.EventBox()
418 spacer
.set_size_request(6, 6)
420 return [hbox
, spacer
]
423 def build_spacer(self
, node
, label
):
426 eb
.set_size_request(8, 8)
429 def build_hbox(self
, node
, label
):
430 """<hbox>...</hbox> to layout child widgets horizontally."""
431 return self
.do_box(node
, label
, g
.HBox(False, 4))
432 def build_vbox(self
, node
, label
):
433 """<vbox>...</vbox> to layout child widgets vertically."""
434 return self
.do_box(node
, label
, g
.VBox(False, 0))
436 def do_box(self
, node
, label
, widget
):
437 "Helper function for building hbox, vbox and frame widgets."
439 widget
.pack_start(self
.make_sized_label(label
),
442 for child
in node
.childNodes
:
443 if child
.nodeType
== Node
.ELEMENT_NODE
:
444 self
.build_widget(child
, widget
)
448 def build_frame(self
, node
, label
):
449 """<frame label='Title'>...</frame> to group options under a heading."""
450 frame
= g
.Frame(label
)
451 frame
.set_shadow_type(g
.SHADOW_NONE
)
453 # Make the label bold...
454 # (bug in pygtk => use set_markup)
455 label_widget
= frame
.get_label_widget()
456 label_widget
.set_markup('<b>' + label
+ '</b>')
457 #attr = pango.AttrWeight(pango.WEIGHT_BOLD)
458 #attr.start_index = 0
460 #list = pango.AttrList()
462 #label_widget.set_attributes(list)
464 vbox
= g
.VBox(False, 4)
465 vbox
.set_border_width(12)
468 self
.do_box(node
, None, vbox
)
472 def do_entry(self
, node
, label
, option
):
473 "Helper function for entry and secretentry widgets"
474 box
= g
.HBox(False, 4)
478 label_wid
= self
.make_sized_label(label
)
479 label_wid
.set_alignment(1.0, 0.5)
480 box
.pack_start(label_wid
, False, True, 0)
481 box
.pack_start(entry
, True, True, 0)
485 self
.may_add_tip(entry
, node
)
487 entry
.connect('changed', lambda e
: self
.check_widget(option
))
490 return entry
.get_chars(0, -1)
492 entry
.set_text(option
.value
)
493 self
.handlers
[option
] = (get
, set)
495 return (entry
, [box
or entry
])
497 def build_entry(self
, node
, label
, option
):
498 "<entry name='...' label='...'>Tooltip</entry>"
499 entry
, result
=self
.do_entry(node
, label
, option
)
502 def build_secretentry(self
, node
, label
, option
):
503 "<secretentry name='...' label='...' char='*'>Tooltip</secretentry>"
504 entry
, result
=self
.do_entry(node
, label
, option
)
506 ch
=node
.getAttribute('char')
514 entry
.set_visibility(False)
515 entry
.set_invisible_char(ch
)
519 def build_font(self
, node
, label
, option
):
520 "<font name='...' label='...'>Tooltip</font>"
521 button
= FontButton(self
, option
, label
)
523 self
.may_add_tip(button
, node
)
525 hbox
= g
.HBox(False, 4)
526 hbox
.pack_start(self
.make_sized_label(label
), False, True, 0)
527 hbox
.pack_start(button
, False, True, 0)
529 self
.handlers
[option
] = (button
.get
, button
.set)
533 def build_colour(self
, node
, label
, option
):
534 "<colour name='...' label='...'>Tooltip</colour>"
535 button
= ColourButton(self
, option
, label
)
537 self
.may_add_tip(button
, node
)
539 hbox
= g
.HBox(False, 4)
540 hbox
.pack_start(self
.make_sized_label(label
), False, True, 0)
541 hbox
.pack_start(button
, False, True, 0)
543 self
.handlers
[option
] = (button
.get
, button
.set)
547 def build_numentry(self
, node
, label
, option
):
548 """<numentry name='...' label='...' min='0' max='100' step='1'>Tooltip</numentry>.
549 Lets the user choose a number from min to max."""
550 minv
= int(node
.getAttribute('min'))
551 maxv
= int(node
.getAttribute('max'))
552 step
= node
.getAttribute('step')
553 unit
= node
.getAttribute('unit')
559 unit
= self
.trans(unit
)
561 hbox
= g
.HBox(False, 4)
563 widget
= self
.make_sized_label(label
)
564 widget
.set_alignment(1.0, 0.5)
565 hbox
.pack_start(widget
, False, True, 0)
567 spin
= g
.SpinButton(g
.Adjustment(minv
, minv
, maxv
, step
))
568 spin
.set_width_chars(max(len(str(minv
)), len(str(maxv
))))
569 hbox
.pack_start(spin
, False, True, 0)
570 self
.may_add_tip(spin
, node
)
573 hbox
.pack_start(g
.Label(unit
), False, True, 0)
575 self
.handlers
[option
] = (
576 lambda: str(spin
.get_value()),
577 lambda: spin
.set_value(option
.int_value
))
579 spin
.connect('value-changed', lambda w
: self
.check_widget(option
))
583 def build_menu(self
, node
, label
, option
):
584 """Build an OptionMenu widget, only one item of which may be selected.
585 <menu name='...' label='...'>
586 <item value='...' label='...'/>
587 <item value='...' label='...'/>
592 has_combo
= hasattr(g
, 'combo_box_new_text')
594 option_menu
= g
.combo_box_new_text()
595 option_menu
.get_history
= option_menu
.get_active
596 option_menu
.set_history
= option_menu
.set_active
598 option_menu
= g
.OptionMenu()
602 box
= g
.HBox(False, 4)
603 label_wid
= self
.make_sized_label(label
)
604 label_wid
.set_alignment(1.0, 0.5)
605 box
.pack_start(label_wid
, False, True, 0)
606 box
.pack_start(option_menu
, True, True, 0)
610 #self.may_add_tip(option_menu, node)
612 for item
in node
.getElementsByTagName('item'):
613 assert item
.hasAttribute('value')
614 value
= item
.getAttribute('value')
615 label_item
= self
.trans(item
.getAttribute('label')) or value
618 option_menu
.append_text(label_item
)
620 menu
.append(g
.MenuItem(label_item
))
626 option_menu
.set_menu(menu
)
627 option_menu
.connect('changed', lambda e
: self
.check_widget(option
))
630 return values
[option_menu
.get_history()]
634 option_menu
.set_history(values
.index(option
.value
))
636 print "Value '%s' not in combo list" % option
.value
638 self
.handlers
[option
] = (get
, set)
640 return [box
or option_menu
]
643 def build_radio_group(self
, node
, label
, option
):
644 """Build a list of radio buttons, only one of which may be selected.
645 <radio-group name='...'>
646 <radio value='...' label='...'>Tooltip</radio>
647 <radio value='...' label='...'>Tooltip</radio>
652 for radio
in node
.getElementsByTagName('radio'):
653 label
= self
.trans(radio
.getAttribute('label'))
654 button
= g
.RadioButton(button
, label
)
655 self
.may_add_tip(button
, radio
)
656 radios
.append(button
)
657 values
.append(radio
.getAttribute('value'))
658 button
.connect('toggled', lambda b
: self
.check_widget(option
))
662 i
= values
.index(option
.value
)
664 print "Value '%s' not in radio group!" % option
.value
666 radios
[i
].set_active(True)
668 for r
, v
in zip(radios
, values
):
671 raise Exception('Nothing selected!')
673 self
.handlers
[option
] = (get
, set)
677 def build_toggle(self
, node
, label
, option
):
678 "<toggle name='...' label='...'>Tooltip</toggle>"
679 toggle
= g
.CheckButton(label
)
680 self
.may_add_tip(toggle
, node
)
682 self
.handlers
[option
] = (
683 lambda: str(toggle
.get_active()),
684 lambda: toggle
.set_active(option
.int_value
))
686 toggle
.connect('toggled', lambda w
: self
.check_widget(option
))
690 def build_slider(self
, node
, label
, option
):
691 minv
= int(node
.getAttribute('min'))
692 maxv
= int(node
.getAttribute('max'))
693 fixed
= int(node
.getAttribute('fixed') or "0")
694 showvalue
= int(node
.getAttribute('showvalue') or "0")
695 end
= node
.getAttribute('end')
697 hbox
= g
.HBox(False, 4)
699 widget
= self
.make_sized_label(label
)
700 hbox
.pack_start(widget
, False, True, 0)
703 hbox
.pack_end(self
.make_sized_label(self
.trans(end
),
707 adj
= g
.Adjustment(minv
, minv
, maxv
, 1, 10, 0)
708 slide
= g
.HScale(adj
)
711 slide
.set_size_request(adj
.upper
, 24)
713 slide
.set_size_request(120, -1)
715 slide
.set_draw_value(True)
716 slide
.set_value_pos(g
.POS_LEFT
)
719 slide
.set_draw_value(False)
721 self
.may_add_tip(slide
, node
)
722 hbox
.pack_start(slide
, not fixed
, True, 0)
724 self
.handlers
[option
] = (
725 lambda: str(adj
.get_value()),
726 lambda: adj
.set_value(option
.int_value
))
728 slide
.connect('value-changed',
729 lambda w
: self
.check_widget(option
))
733 def build_fixedlist(self
, node
, label
, option
):
734 """<fixedlist name='...' label='...' selection='single|none|multiple'>Tooltip<listitem label='...'/><listitem label='...'/></fixedlist>"""
735 select
=str_attr(node
, 'selection', 'single')
737 cont
=g
.VBox(False, 4)
740 label_wid
= g
.Label(label
)
741 cont
.pack_start(label_wid
, False, True, 0)
744 swin
= g
.ScrolledWindow()
745 swin
.set_border_width(4)
746 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_ALWAYS
)
747 swin
.set_shadow_type(g
.SHADOW_IN
)
748 swin
.set_size_request(-1, 128)
749 cont
.pack_start(swin
, True, True, 0)
751 model
= g
.ListStore(str)
752 view
= g
.TreeView(model
)
755 selection
=view
.get_selection()
757 selection
.set_mode(g
.SELECTION_NONE
)
758 elif select
=='multiple':
759 selection
.set_mode(g
.SELECTION_MULTIPLE
)
761 selection
.set_mode(g
.SELECTION_SINGLE
)
764 def sel_changed(sel
, box
):
765 box
.check_widget(option
)
767 selection
.connect('changed', sel_changed
, self
)
769 cell
= g
.CellRendererText()
770 column
= g
.TreeViewColumn('', cell
, text
= 0)
771 view
.append_column(column
)
773 for item
in node
.getElementsByTagName('listitem'):
774 label
=item
.getAttribute('label')
776 model
.set(iter, 0, label
)
778 self
.may_add_tip(swin
, node
)
780 def make_sel(model
, path
, iter, l
):
781 l
.append(str(model
.get_value(iter, 0)))
784 mode
=view
.get_selection().get_mode()
785 if mode
==int(g
.SELECTION_NONE
):
787 elif mode
==int(g
.SELECTION_SINGLE
):
788 model
, iter=view
.get_selection().get_selected()
789 return [str(model
.get_value(iter, 0))]
792 view
.get_selection().selected_foreach(make_sel
, v
)
796 sel
=view
.get_selection()
799 for v
in option
.list_value
:
800 iter=model
.get_iter_first()
802 if v
==model
.get_value(iter, 0):
803 sel
.select_iter(iter)
806 iter=model
.iter_next(iter)
808 self
.handlers
[option
]=(get
, set)
812 def build_varlist(self
, node
, label
, option
):
813 """<varlist name='...' label='...' edit='yes|no' extend='yes|no' selection='single|none|multiple'>Tooltip</varlist>"""
814 edit
=bool_attr(node
, 'edit')
815 reorder
=bool_attr(node
, 'reorder')
816 extend
=bool_attr(node
, 'extend')
817 select
=str_attr(node
, 'selection', 'single')
819 cont
=rox
.g
.VBox(False, 4)
822 label_wid
= rox
.g
.Label(label
)
823 cont
.pack_start(label_wid
, False, True, 0)
826 swin
= g
.ScrolledWindow()
827 swin
.set_border_width(4)
828 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_ALWAYS
)
829 swin
.set_shadow_type(g
.SHADOW_IN
)
830 swin
.set_size_request(-1, 128)
831 cont
.pack_start(swin
, True, True, 0)
833 model
= g
.ListStore(str, str)
834 view
= g
.TreeView(model
)
837 selection
=view
.get_selection()
839 selection
.set_mode(g
.SELECTION_NONE
)
840 elif select
=='multiple':
841 selection
.set_mode(g
.SELECTION_MULTIPLE
)
843 selection
.set_mode(g
.SELECTION_SINGLE
)
847 view
.set_reorderable(True)
849 def cell_edited(ell
, path
, new_text
, col
):
850 if col
==0 and new_text
.find('=')>=0:
852 iter=model
.get_iter_from_string(path
)
853 model
.set(iter, col
, new_text
)
854 self
.check_widget(option
)
856 cell
= g
.CellRendererText()
857 column
= g
.TreeViewColumn('Variable', cell
, text
= 0)
858 view
.append_column(column
)
860 cell
.set_property('editable', True)
861 cell
.connect('edited', cell_edited
, 0)
863 cell
= g
.CellRendererText()
864 column
= g
.TreeViewColumn('Value', cell
, text
= 1)
865 view
.append_column(column
)
867 cell
.set_property('editable', True)
868 cell
.connect('edited', cell_edited
, 1)
870 def add(widget
, box
):
872 model
.set(iter, 0, 'newvar', 1, 'new value')
874 view
.get_selection().select_iter(iter)
875 box
.check_widget(option
)
877 hbox
=g
.HBox(False, 2)
878 cont
.pack_start(hbox
, False)
880 but
=g
.Button(stock
=g
.STOCK_ADD
)
881 but
.connect('clicked', add
, self
)
882 hbox
.pack_start(but
, False)
884 self
.may_add_tip(swin
, node
)
888 iter=model
.get_iter_first()
890 var
=model
.get_value(iter, 0)
891 val
=model
.get_value(iter, 1)
892 v
.append(var
+'='+val
)
894 iter=model
.iter_next(iter)
899 for v
in option
.list_value
:
900 var
, val
=v
.split('=', 1)
902 model
.set(iter, 0, var
, 1, val
)
904 self
.handlers
[option
]=(get
, set)
909 class FontButton(g
.Button
):
910 """A button that opens a GtkFontSelectionDialog"""
911 def __init__(self
, option_box
, option
, title
):
912 g
.Button
.__init
__(self
)
913 self
.option_box
= option_box
916 self
.label
= g
.Label('<font>')
919 self
.connect('clicked', self
.clicked
)
922 self
.label
.set_text(self
.option
.value
)
924 self
.dialog
.destroy()
927 return self
.label
.get()
929 def clicked(self
, button
):
931 self
.dialog
.destroy()
936 def response(dialog
, resp
):
937 if resp
!= int(g
.RESPONSE_OK
):
940 self
.label
.set_text(dialog
.get_font_name())
942 self
.option_box
.check_widget(self
.option
)
944 self
.dialog
= g
.FontSelectionDialog(self
.title
)
945 self
.dialog
.set_position(g
.WIN_POS_MOUSE
)
946 self
.dialog
.connect('destroy', closed
)
947 self
.dialog
.connect('response', response
)
949 self
.dialog
.set_font_name(self
.get())
952 class ColourButton(g
.Button
):
953 """A button that opens a GtkColorSelectionDialog"""
954 def __init__(self
, option_box
, option
, title
):
955 g
.Button
.__init
__(self
)
956 self
.c_box
= g
.EventBox()
958 self
.option_box
= option_box
961 self
.set_size_request(64, 14)
963 self
.connect('clicked', self
.clicked
)
964 self
.connect('expose-event', self
.expose
)
966 def expose(self
, widget
, event
):
967 # Some themes draw images and stuff here, so we have to
968 # override it manually.
969 self
.c_box
.window
.draw_rectangle(
970 self
.c_box
.style
.bg_gc
[g
.STATE_NORMAL
], True,
972 self
.c_box
.allocation
.width
,
973 self
.c_box
.allocation
.height
)
975 def set(self
, c
= None):
977 c
= g
.gdk
.color_parse(self
.option
.value
)
978 self
.c_box
.modify_bg(g
.STATE_NORMAL
, c
)
981 c
= self
.c_box
.get_style().bg
[g
.STATE_NORMAL
]
982 return '#%04x%04x%04x' % (c
.red
, c
.green
, c
.blue
)
984 def clicked(self
, button
):
986 self
.dialog
.destroy()
991 def response(dialog
, resp
):
992 if resp
!= int(g
.RESPONSE_OK
):
995 self
.set(dialog
.colorsel
.get_current_color())
997 self
.option_box
.check_widget(self
.option
)
999 self
.dialog
= g
.ColorSelectionDialog(self
.title
)
1000 self
.dialog
.set_position(g
.WIN_POS_MOUSE
)
1001 self
.dialog
.connect('destroy', closed
)
1002 self
.dialog
.connect('response', response
)
1004 c
= self
.c_box
.get_style().bg
[g
.STATE_NORMAL
]
1005 self
.dialog
.colorsel
.set_current_color(c
)
1008 # Add your own options here... (maps element localName to build function)