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 if (g
.pygtk_version
< (2, 12, 0)):
121 # gtk.Tooltips deprecated as of pygtk-2.12.0
122 self
.tips
= g
.Tooltips()
123 self
.set_has_separator(False)
125 self
.options
= options_group
126 self
.set_title((_('%s options')) % options_group
.program
)
127 self
.set_position(g
.WIN_POS_CENTER
)
129 button
= rox
.ButtonMixed(g
.STOCK_UNDO
, _('_Revert'))
130 self
.add_action_widget(button
, REVERT
)
131 revert_tooltip
= _('Restore all options to how they were '
132 'when the window was opened')
133 if (g
.pygtk_version
>= (2, 12, 0)):
134 button
.set_tooltip_text(revert_tooltip
)
136 self
.tips
.set_tip(button
, revert_tooltip
)
138 self
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
140 self
.set_default_response(g
.RESPONSE_OK
)
142 doc
= minidom
.parse(options_xml
)
143 assert doc
.documentElement
.localName
== 'options'
145 self
.handlers
= {} # Option -> (get, set)
146 self
.revert
= {} # Option -> old value
147 self
.size_groups
= {} # Name -> GtkSizeGroup
148 self
.current_size_group
= None
151 for section
in doc
.documentElement
.childNodes
:
152 if section
.nodeType
!= Node
.ELEMENT_NODE
:
154 if section
.localName
!= 'section':
155 print "Unknown section", section
157 sections
.append(section
)
159 self
.build_window_frame(add_frame
= len(sections
) > 1)
162 for section
in sections
:
163 self
.build_section(section
, None)
164 if len(sections
) > 1:
165 self
.tree_view
.expand_all()
167 self
.sections_swin
.hide()
171 def destroyed(widget
):
177 rox
.report_exception()
178 self
.connect('destroy', destroyed
)
180 def got_response(widget
, response
):
181 if response
== int(g
.RESPONSE_OK
):
183 elif response
== REVERT
:
184 for o
in self
.options
:
185 o
._set
(self
.revert
[o
])
186 self
.update_widgets()
187 self
.options
.notify()
189 self
.connect('response', got_response
)
192 """Show the window, updating all the widgets at the same
193 time. Use this instead of show()."""
195 for option
in self
.options
:
196 self
.revert
[option
] = option
.value
197 self
.update_widgets()
201 def update_revert(self
):
202 "Shade/unshade the Revert button. Internal."
203 self
.set_response_sensitive(REVERT
, self
.changed())
206 """Check whether any options have different values (ie, whether Revert
207 will do anything)."""
208 for option
in self
.options
:
209 if option
.value
!= self
.revert
[option
]:
213 def update_widgets(self
):
214 "Make widgets show current values. Internal."
215 assert not self
.updating
219 for option
in self
.options
:
221 handler
= self
.handlers
[option
][1]
223 print "No widget for option '%s'!" % option
229 def build_window_frame(self
, add_frame
= True):
230 "Create the main structure of the window."
231 hbox
= g
.HBox(False, 4)
232 self
.vbox
.pack_start(hbox
, True, True, 0)
234 # scrolled window for the tree view
235 sw
= g
.ScrolledWindow()
236 sw
.set_shadow_type(g
.SHADOW_IN
)
237 sw
.set_policy(g
.POLICY_NEVER
, g
.POLICY_AUTOMATIC
)
238 hbox
.pack_start(sw
, False, True, 0)
239 self
.sections_swin
= sw
# Used to hide it...
242 model
= g
.TreeStore(gobject
.TYPE_STRING
, gobject
.TYPE_INT
)
243 tv
= g
.TreeView(model
)
244 sel
= tv
.get_selection()
245 sel
.set_mode(g
.SELECTION_BROWSE
)
246 tv
.set_headers_visible(False)
247 self
.sections
= model
249 tv
.unset_flags(g
.CAN_FOCUS
) # Stop irritating highlight
251 # Add a column to display column 0 of the store...
252 cell
= g
.CellRendererText()
253 column
= g
.TreeViewColumn('Section', cell
, text
= 0)
254 tv
.append_column(column
)
259 notebook
= g
.Notebook()
260 notebook
.set_show_tabs(False)
261 notebook
.set_show_border(False)
262 self
.notebook
= notebook
266 frame
.set_shadow_type(g
.SHADOW_IN
)
267 hbox
.pack_start(frame
, True, True, 0)
270 hbox
.pack_start(notebook
, True, True, 0)
273 def change_page(sel
, notebook
):
274 selected
= sel
.get_selected()
277 model
, titer
= selected
278 page
= model
.get_value(titer
, 1)
279 notebook
.set_current_page(page
)
281 sel
.connect('changed', change_page
, notebook
)
285 def check_widget(self
, option
):
286 "A widgets call this when the user changes its value."
290 assert isinstance(option
, options
.Option
)
292 new
= self
.handlers
[option
][0]()
294 if new
== option
.value
:
298 self
.options
.notify()
301 def build_section(self
, section
, parent
):
302 """Create a new page for the notebook and a new entry in the
303 sections tree, and build all the widgets inside the page."""
304 page
= g
.VBox(False, 4)
305 page
.set_border_width(4)
306 self
.notebook
.append_page(page
, g
.Label('unused'))
308 titer
= self
.sections
.append(parent
)
309 self
.sections
.set(titer
,
310 0, self
.trans(section
.getAttribute('title')),
311 1, self
.notebook
.page_num(page
))
312 for node
in section
.childNodes
:
313 if node
.nodeType
!= Node
.ELEMENT_NODE
:
315 name
= node
.localName
316 if name
== 'section':
317 self
.build_section(node
, titer
)
319 self
.build_widget(node
, page
)
322 def build_widget(self
, node
, box
):
323 """Dispatches the job of dealing with a DOM Node to the
324 appropriate build_* function."""
325 label
= node
.getAttribute('label')
326 name
= node
.getAttribute('name')
328 label
= self
.trans(label
)
330 old_size_group
= self
.current_size_group
331 sg
= node
.getAttributeNode('size-group')
333 self
.current_size_group
= sg
.value
or None
338 option
= self
.options
.options
[name
]
340 raise Exception("Unknown option '%s'" % name
)
342 # Check for a new-style function in the registry...
343 new_fn
= widget_registry
.get(node
.localName
, None)
345 # Wrap it up so it appears old-style
346 fn
= lambda *args
: new_fn(self
, *args
)
348 # Not in the registry... look in the class instead
350 name
= node
.localName
.replace('-', '_')
351 fn
= getattr(self
, 'build_' + name
)
352 except AttributeError:
353 fn
= self
.build_unknown
356 widgets
= fn(node
, label
, option
)
358 widgets
= fn(node
, label
)
360 if hasattr(w
, '_rox_lib_expand'):
361 expand
=w
._rox
_lib
_expand
364 box
.pack_start(w
, expand
, True, 0)
366 self
.current_size_group
= old_size_group
368 def may_add_tip(self
, widget
, node
):
369 """If 'node' contains any text, use that as the tip for 'widget'."""
371 data
= ''.join([n
.nodeValue
for n
in node
.childNodes
if n
.nodeType
== Node
.TEXT_NODE
]).strip()
375 if (g
.pygtk_version
>= (2, 12, 0)):
376 widget
.set_tooltip_text(self
.trans(data
))
378 self
.tips
.set_tip(widget
, self
.trans(data
))
380 def get_size_group(self
, name
):
381 """Return the GtkSizeGroup for this name, creating one
382 if it doesn't currently exist."""
384 return self
.size_groups
[name
]
386 group
= g
.SizeGroup(g
.SIZE_GROUP_HORIZONTAL
)
387 self
.size_groups
[name
] = group
390 def make_sized_label(self
, label
, suffix
= ""):
391 """Create a GtkLabel and add it to the current size-group, if any"""
392 widget
= g
.Label(label
)
393 if self
.current_size_group
:
394 widget
.set_alignment(1.0, 0.5)
395 group
= self
.get_size_group(self
.current_size_group
+ suffix
)
396 group
.add_widget(widget
)
399 # Each type of widget has a method called 'build_NAME' where name is
400 # the XML element name. This method is called as method(node, label,
401 # option) if it corresponds to an Option, or method(node, label)
402 # otherwise. It should return a list of widgets to add to the window
403 # and, if it's for an Option, set self.handlers[option] = (get, set).
405 def build_unknown(self
, node
, label
, option
= None):
406 return [g
.Label("Unknown widget type <%s>" % node
.localName
)]
408 def build_label(self
, node
, label
):
409 help_flag
= int(node
.getAttribute('help') or '0')
410 widget
= self
.make_sized_label(self
.trans(data(node
)))
412 widget
.set_alignment(0, 0.5)
414 widget
.set_alignment(0, 1)
415 widget
.set_justify(g
.JUSTIFY_LEFT
)
416 widget
.set_line_wrap(True)
419 hbox
= g
.HBox(False, 4)
421 image
.set_from_stock(g
.STOCK_DIALOG_INFO
,
423 align
= g
.Alignment(0, 0, 0, 0)
426 hbox
.pack_start(align
, False, True, 0)
427 hbox
.pack_start(widget
, False, True, 0)
429 spacer
= g
.EventBox()
430 spacer
.set_size_request(6, 6)
432 return [hbox
, spacer
]
435 def build_spacer(self
, node
, label
):
438 eb
.set_size_request(8, 8)
441 def build_hbox(self
, node
, label
):
442 """<hbox>...</hbox> to layout child widgets horizontally."""
443 return self
.do_box(node
, label
, g
.HBox(False, 4))
444 def build_vbox(self
, node
, label
):
445 """<vbox>...</vbox> to layout child widgets vertically."""
446 return self
.do_box(node
, label
, g
.VBox(False, 0))
448 def do_box(self
, node
, label
, widget
):
449 "Helper function for building hbox, vbox and frame widgets."
451 widget
.pack_start(self
.make_sized_label(label
),
454 for child
in node
.childNodes
:
455 if child
.nodeType
== Node
.ELEMENT_NODE
:
456 self
.build_widget(child
, widget
)
460 def build_frame(self
, node
, label
):
461 """<frame label='Title'>...</frame> to group options under a heading."""
462 frame
= g
.Frame(label
)
463 frame
.set_shadow_type(g
.SHADOW_NONE
)
465 # Make the label bold...
466 # (bug in pygtk => use set_markup)
467 label_widget
= frame
.get_label_widget()
468 label_widget
.set_markup('<b>' + label
+ '</b>')
469 #attr = pango.AttrWeight(pango.WEIGHT_BOLD)
470 #attr.start_index = 0
472 #list = pango.AttrList()
474 #label_widget.set_attributes(list)
476 vbox
= g
.VBox(False, 4)
477 vbox
.set_border_width(12)
480 self
.do_box(node
, None, vbox
)
484 def do_entry(self
, node
, label
, option
):
485 "Helper function for entry and secretentry widgets"
486 box
= g
.HBox(False, 4)
490 label_wid
= self
.make_sized_label(label
)
491 label_wid
.set_alignment(1.0, 0.5)
492 box
.pack_start(label_wid
, False, True, 0)
493 box
.pack_start(entry
, True, True, 0)
497 self
.may_add_tip(entry
, node
)
499 entry
.connect('changed', lambda e
: self
.check_widget(option
))
502 return entry
.get_chars(0, -1)
504 entry
.set_text(option
.value
)
505 self
.handlers
[option
] = (get
, set)
507 return (entry
, [box
or entry
])
509 def build_entry(self
, node
, label
, option
):
510 "<entry name='...' label='...'>Tooltip</entry>"
511 entry
, result
=self
.do_entry(node
, label
, option
)
514 def build_secretentry(self
, node
, label
, option
):
515 "<secretentry name='...' label='...' char='*'>Tooltip</secretentry>"
516 entry
, result
=self
.do_entry(node
, label
, option
)
518 ch
=node
.getAttribute('char')
526 entry
.set_visibility(False)
527 entry
.set_invisible_char(ch
)
531 def build_font(self
, node
, label
, option
):
532 "<font name='...' label='...'>Tooltip</font>"
533 button
= FontButton(self
, option
, label
)
535 self
.may_add_tip(button
, node
)
537 hbox
= g
.HBox(False, 4)
538 hbox
.pack_start(self
.make_sized_label(label
), False, True, 0)
539 hbox
.pack_start(button
, False, True, 0)
541 self
.handlers
[option
] = (button
.get
, button
.set)
545 def build_colour(self
, node
, label
, option
):
546 "<colour name='...' label='...'>Tooltip</colour>"
547 button
= ColourButton(self
, option
, label
)
549 self
.may_add_tip(button
, node
)
551 hbox
= g
.HBox(False, 4)
552 hbox
.pack_start(self
.make_sized_label(label
), False, True, 0)
553 hbox
.pack_start(button
, False, True, 0)
555 self
.handlers
[option
] = (button
.get
, button
.set)
559 def build_numentry(self
, node
, label
, option
):
560 """<numentry name='...' label='...' min='0' max='100' step='1'>Tooltip</numentry>.
561 Lets the user choose a number from min to max."""
562 minv
= int(node
.getAttribute('min'))
563 maxv
= int(node
.getAttribute('max'))
564 step
= node
.getAttribute('step')
565 unit
= node
.getAttribute('unit')
571 unit
= self
.trans(unit
)
573 hbox
= g
.HBox(False, 4)
575 widget
= self
.make_sized_label(label
)
576 widget
.set_alignment(1.0, 0.5)
577 hbox
.pack_start(widget
, False, True, 0)
579 spin
= g
.SpinButton(g
.Adjustment(minv
, minv
, maxv
, step
))
580 spin
.set_width_chars(max(len(str(minv
)), len(str(maxv
))))
581 hbox
.pack_start(spin
, False, True, 0)
582 self
.may_add_tip(spin
, node
)
585 hbox
.pack_start(g
.Label(unit
), False, True, 0)
587 self
.handlers
[option
] = (
588 lambda: str(spin
.get_value()),
589 lambda: spin
.set_value(option
.int_value
))
591 spin
.connect('value-changed', lambda w
: self
.check_widget(option
))
595 def build_filechooser(self
, node
, label
, option
):
596 """<filechooser name='...' label='...'/>Tooltip</filechooser>.
597 Lets the user choose a file (using a GtkFileChooser or by drag-and-drop).
598 Note: requires GTK >= 2.6
600 filebutton
= g
.FileChooserButton(label
)
603 self
.may_add_tip(eb
, node
)
605 clearbutton
= g
.Button(stock
= g
.STOCK_CLEAR
)
606 hbox
= g
.HBox(False, 4)
608 hbox
.pack_start(g
.Label(label
+ ":"), False, True, 0)
609 hbox
.pack_start(eb
, True, True, 0)
610 hbox
.pack_start(clearbutton
, False, True, 0)
612 self
.handlers
[option
] = (
613 lambda: filebutton
.get_filename(),
614 lambda: filebutton
.set_filename(option
.value
))
615 filebutton
.connect('selection-changed', lambda w
: self
.check_widget(option
))
618 filebutton
.set_filename("")
619 self
.check_widget(option
)
620 clearbutton
.connect('clicked', clear
)
624 def build_menu(self
, node
, label
, option
):
625 """Build an OptionMenu widget, only one item of which may be selected.
626 <menu name='...' label='...'>
627 <item value='...' label='...'/>
628 <item value='...' label='...'/>
633 has_combo
= hasattr(g
, 'combo_box_new_text')
635 option_menu
= g
.combo_box_new_text()
636 option_menu
.get_history
= option_menu
.get_active
637 option_menu
.set_history
= option_menu
.set_active
639 option_menu
= g
.OptionMenu()
643 box
= g
.HBox(False, 4)
644 label_wid
= self
.make_sized_label(label
)
645 label_wid
.set_alignment(1.0, 0.5)
646 box
.pack_start(label_wid
, False, True, 0)
647 box
.pack_start(option_menu
, True, True, 0)
651 #self.may_add_tip(option_menu, node)
653 for item
in node
.getElementsByTagName('item'):
654 assert item
.hasAttribute('value')
655 value
= item
.getAttribute('value')
656 label_item
= self
.trans(item
.getAttribute('label')) or value
659 option_menu
.append_text(label_item
)
661 menu
.append(g
.MenuItem(label_item
))
667 option_menu
.set_menu(menu
)
668 option_menu
.connect('changed', lambda e
: self
.check_widget(option
))
671 return values
[option_menu
.get_history()]
675 option_menu
.set_history(values
.index(option
.value
))
677 print "Value '%s' not in combo list" % option
.value
679 self
.handlers
[option
] = (get
, set)
681 return [box
or option_menu
]
684 def build_radio_group(self
, node
, label
, option
):
685 """Build a list of radio buttons, only one of which may be selected.
686 <radio-group name='...'>
687 <radio value='...' label='...'>Tooltip</radio>
688 <radio value='...' label='...'>Tooltip</radio>
693 for radio
in node
.getElementsByTagName('radio'):
694 label
= self
.trans(radio
.getAttribute('label'))
695 button
= g
.RadioButton(button
, label
)
696 self
.may_add_tip(button
, radio
)
697 radios
.append(button
)
698 values
.append(radio
.getAttribute('value'))
699 button
.connect('toggled', lambda b
: self
.check_widget(option
))
703 i
= values
.index(option
.value
)
705 print "Value '%s' not in radio group!" % option
.value
707 radios
[i
].set_active(True)
709 for r
, v
in zip(radios
, values
):
712 raise Exception('Nothing selected!')
714 self
.handlers
[option
] = (get
, set)
718 def build_toggle(self
, node
, label
, option
):
719 "<toggle name='...' label='...'>Tooltip</toggle>"
720 toggle
= g
.CheckButton(label
)
721 self
.may_add_tip(toggle
, node
)
723 self
.handlers
[option
] = (
724 lambda: str(toggle
.get_active()),
725 lambda: toggle
.set_active(option
.int_value
))
727 toggle
.connect('toggled', lambda w
: self
.check_widget(option
))
731 def build_slider(self
, node
, label
, option
):
732 minv
= int(node
.getAttribute('min'))
733 maxv
= int(node
.getAttribute('max'))
734 fixed
= int(node
.getAttribute('fixed') or "0")
735 showvalue
= int(node
.getAttribute('showvalue') or "0")
736 end
= node
.getAttribute('end')
738 hbox
= g
.HBox(False, 4)
740 widget
= self
.make_sized_label(label
)
741 hbox
.pack_start(widget
, False, True, 0)
744 hbox
.pack_end(self
.make_sized_label(self
.trans(end
),
748 adj
= g
.Adjustment(minv
, minv
, maxv
, 1, 10, 0)
749 slide
= g
.HScale(adj
)
752 slide
.set_size_request(adj
.upper
, 24)
754 slide
.set_size_request(120, -1)
756 slide
.set_draw_value(True)
757 slide
.set_value_pos(g
.POS_LEFT
)
760 slide
.set_draw_value(False)
762 self
.may_add_tip(slide
, node
)
763 hbox
.pack_start(slide
, not fixed
, True, 0)
765 self
.handlers
[option
] = (
766 lambda: str(adj
.get_value()),
767 lambda: adj
.set_value(option
.int_value
))
769 slide
.connect('value-changed',
770 lambda w
: self
.check_widget(option
))
774 def build_fixedlist(self
, node
, label
, option
):
775 """<fixedlist name='...' label='...' selection='single|none|multiple'>Tooltip<listitem label='...'/><listitem label='...'/></fixedlist>"""
776 select
=str_attr(node
, 'selection', 'single')
778 cont
=g
.VBox(False, 4)
779 cont
._rox
_lib
_expand
=True
782 label_wid
= g
.Label(label
)
783 cont
.pack_start(label_wid
, False, True, 0)
786 swin
= g
.ScrolledWindow()
787 swin
.set_border_width(4)
788 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_ALWAYS
)
789 swin
.set_shadow_type(g
.SHADOW_IN
)
790 #swin.set_size_request(-1, 128)
791 cont
.pack_start(swin
, True, True, 0)
793 model
= g
.ListStore(str)
794 view
= g
.TreeView(model
)
797 selection
=view
.get_selection()
799 selection
.set_mode(g
.SELECTION_NONE
)
800 elif select
=='multiple':
801 selection
.set_mode(g
.SELECTION_MULTIPLE
)
803 selection
.set_mode(g
.SELECTION_SINGLE
)
806 def sel_changed(sel
, box
):
807 box
.check_widget(option
)
809 selection
.connect('changed', sel_changed
, self
)
811 cell
= g
.CellRendererText()
812 column
= g
.TreeViewColumn('', cell
, text
= 0)
813 view
.append_column(column
)
815 for item
in node
.getElementsByTagName('listitem'):
816 label
=item
.getAttribute('label')
818 model
.set(iter, 0, label
)
820 self
.may_add_tip(swin
, node
)
822 def make_sel(model
, path
, iter, l
):
823 l
.append(str(model
.get_value(iter, 0)))
826 mode
=view
.get_selection().get_mode()
827 if mode
==int(g
.SELECTION_NONE
):
829 elif mode
==int(g
.SELECTION_SINGLE
):
830 model
, iter=view
.get_selection().get_selected()
831 return [str(model
.get_value(iter, 0))]
834 view
.get_selection().selected_foreach(make_sel
, v
)
838 sel
=view
.get_selection()
841 for v
in option
.list_value
:
842 iter=model
.get_iter_first()
844 if v
==model
.get_value(iter, 0):
845 sel
.select_iter(iter)
848 iter=model
.iter_next(iter)
850 self
.handlers
[option
]=(get
, set)
854 def build_varlist(self
, node
, label
, option
):
855 """<varlist name='...' label='...' edit='yes|no' extend='yes|no' selection='single|none|multiple'>Tooltip</varlist>"""
856 edit
=bool_attr(node
, 'edit')
857 reorder
=bool_attr(node
, 'reorder')
858 extend
=bool_attr(node
, 'extend')
859 select
=str_attr(node
, 'selection', 'single')
861 cont
=rox
.g
.VBox(False, 4)
862 cont
._rox
_lib
_expand
=True
865 label_wid
= rox
.g
.Label(label
)
866 cont
.pack_start(label_wid
, False, True, 0)
869 swin
= g
.ScrolledWindow()
870 swin
.set_border_width(4)
871 swin
.set_policy(g
.POLICY_NEVER
, g
.POLICY_ALWAYS
)
872 swin
.set_shadow_type(g
.SHADOW_IN
)
873 #swin.set_size_request(-1, 128)
874 cont
.pack_start(swin
, True, True, 0)
876 model
= g
.ListStore(str, str)
877 view
= g
.TreeView(model
)
880 selection
=view
.get_selection()
882 selection
.set_mode(g
.SELECTION_NONE
)
883 elif select
=='multiple':
884 selection
.set_mode(g
.SELECTION_MULTIPLE
)
886 selection
.set_mode(g
.SELECTION_SINGLE
)
890 view
.set_reorderable(True)
892 def cell_edited(ell
, path
, new_text
, col
):
893 if col
==0 and new_text
.find('=')>=0:
895 iter=model
.get_iter_from_string(path
)
896 model
.set(iter, col
, new_text
)
897 self
.check_widget(option
)
899 cell
= g
.CellRendererText()
900 column
= g
.TreeViewColumn('Variable', cell
, text
= 0)
901 view
.append_column(column
)
903 cell
.set_property('editable', True)
904 cell
.connect('edited', cell_edited
, 0)
906 cell
= g
.CellRendererText()
907 column
= g
.TreeViewColumn('Value', cell
, text
= 1)
908 view
.append_column(column
)
910 cell
.set_property('editable', True)
911 cell
.connect('edited', cell_edited
, 1)
913 def add(widget
, box
):
915 model
.set(iter, 0, 'newvar', 1, 'new value')
917 view
.get_selection().select_iter(iter)
918 box
.check_widget(option
)
920 hbox
=g
.HBox(False, 2)
921 cont
.pack_start(hbox
, False)
923 but
=g
.Button(stock
=g
.STOCK_ADD
)
924 but
.connect('clicked', add
, self
)
925 hbox
.pack_start(but
, False)
927 self
.may_add_tip(swin
, node
)
931 iter=model
.get_iter_first()
933 var
=model
.get_value(iter, 0)
934 val
=model
.get_value(iter, 1)
935 v
.append(var
+'='+val
)
937 iter=model
.iter_next(iter)
942 for v
in option
.list_value
:
943 var
, val
=v
.split('=', 1)
945 model
.set(iter, 0, var
, 1, val
)
947 self
.handlers
[option
]=(get
, set)
952 class FontButton(g
.Button
):
953 """A button that opens a GtkFontSelectionDialog"""
954 def __init__(self
, option_box
, option
, title
):
955 g
.Button
.__init
__(self
)
956 self
.option_box
= option_box
959 self
.label
= g
.Label('<font>')
962 self
.connect('clicked', self
.clicked
)
965 self
.label
.set_text(self
.option
.value
)
967 self
.dialog
.destroy()
970 return self
.label
.get()
972 def clicked(self
, button
):
974 self
.dialog
.destroy()
979 def response(dialog
, resp
):
980 if resp
!= int(g
.RESPONSE_OK
):
983 self
.label
.set_text(dialog
.get_font_name())
985 self
.option_box
.check_widget(self
.option
)
987 self
.dialog
= g
.FontSelectionDialog(self
.title
)
988 self
.dialog
.set_position(g
.WIN_POS_MOUSE
)
989 self
.dialog
.connect('destroy', closed
)
990 self
.dialog
.connect('response', response
)
992 self
.dialog
.set_font_name(self
.get())
995 class ColourButton(g
.Button
):
996 """A button that opens a GtkColorSelectionDialog"""
997 def __init__(self
, option_box
, option
, title
):
998 g
.Button
.__init
__(self
)
999 self
.c_box
= g
.EventBox()
1000 self
.add(self
.c_box
)
1001 self
.option_box
= option_box
1002 self
.option
= option
1004 self
.set_size_request(64, 14)
1006 self
.connect('clicked', self
.clicked
)
1007 self
.connect('expose-event', self
.expose
)
1009 def expose(self
, widget
, event
):
1010 # Some themes draw images and stuff here, so we have to
1011 # override it manually.
1012 self
.c_box
.window
.draw_rectangle(
1013 self
.c_box
.style
.bg_gc
[g
.STATE_NORMAL
], True,
1015 self
.c_box
.allocation
.width
,
1016 self
.c_box
.allocation
.height
)
1018 def set(self
, c
= None):
1020 c
= g
.gdk
.color_parse(self
.option
.value
)
1021 self
.c_box
.modify_bg(g
.STATE_NORMAL
, c
)
1024 c
= self
.c_box
.get_style().bg
[g
.STATE_NORMAL
]
1025 return '#%04x%04x%04x' % (c
.red
, c
.green
, c
.blue
)
1027 def clicked(self
, button
):
1029 self
.dialog
.destroy()
1034 def response(dialog
, resp
):
1035 if resp
!= int(g
.RESPONSE_OK
):
1038 self
.set(dialog
.colorsel
.get_current_color())
1040 self
.option_box
.check_widget(self
.option
)
1042 self
.dialog
= g
.ColorSelectionDialog(self
.title
)
1043 self
.dialog
.set_position(g
.WIN_POS_MOUSE
)
1044 self
.dialog
.connect('destroy', closed
)
1045 self
.dialog
.connect('response', response
)
1047 c
= self
.c_box
.get_style().bg
[g
.STATE_NORMAL
]
1048 self
.dialog
.colorsel
.set_current_color(c
)
1051 # Add your own options here... (maps element localName to build function)