r25598: Add missing become_root/unbecome_root around calls of add_aliases.
[gd/samba/.git] / source3 / python / gtkdictbrowser.py
1 #!/usr/bin/python
2 #
3 # Browse a Python dictionary in a two pane graphical interface written
4 # in GTK.
5 #
6 # The GtkDictBrowser class is supposed to be generic enough to allow
7 # applications to override enough methods and produce a
8 # domain-specific browser provided the information is presented as a
9 # Python dictionary.
10 #
11 # Possible applications:
12 #
13 #   - Windows registry browser
14 #   - SPOOLSS printerdata browser
15 #   - tdb file browser
16 #
17
18 from gtk import *
19 import string, re
20
21 class GtkDictBrowser:
22
23     def __init__(self, dict):
24         self.dict = dict
25         
26         # This variable stores a list of (regexp, function) used to
27         # convert the raw value data to a displayable string.
28
29         self.get_value_text_fns = []
30         self.get_key_text = lambda x: x
31
32         # We can filter the list of keys displayed using a regex
33
34         self.filter_regex = ""
35
36     # Create and configure user interface widgets.  A string argument is
37     # used to set the window title.
38
39     def build_ui(self, title):
40         win = GtkWindow()
41         win.set_title(title)
42
43         win.connect("destroy", mainquit)
44
45         hpaned = GtkHPaned()
46         win.add(hpaned)
47         hpaned.set_border_width(5)
48         hpaned.show()
49
50         vbox = GtkVBox()
51         hpaned.add1(vbox)
52         vbox.show()
53
54         scrolled_win = GtkScrolledWindow()
55         scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
56         vbox.pack_start(scrolled_win)
57         scrolled_win.show()
58
59         hbox = GtkHBox()
60         vbox.pack_end(hbox, expand = 0, padding = 5)
61         hbox.show()
62
63         label = GtkLabel("Filter:")
64         hbox.pack_start(label, expand = 0, padding = 5)
65         label.show()
66
67         self.entry = GtkEntry()
68         hbox.pack_end(self.entry, padding = 5)
69         self.entry.show()
70
71         self.entry.connect("activate", self.filter_activated)
72         
73         self.list = GtkList()
74         self.list.set_selection_mode(SELECTION_MULTIPLE)
75         self.list.set_selection_mode(SELECTION_BROWSE)
76         scrolled_win.add_with_viewport(self.list)
77         self.list.show()
78
79         self.list.connect("select_child", self.key_selected)
80
81         scrolled_win = GtkScrolledWindow()
82         scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
83         hpaned.add2(scrolled_win)
84         scrolled_win.set_usize(500,400)
85         scrolled_win.show()
86         
87         self.text = GtkText()
88         self.text.set_editable(FALSE)
89         scrolled_win.add_with_viewport(self.text)
90         self.text.show()
91
92         self.text.connect("event", self.event_handler)
93
94         self.menu = GtkMenu()
95         self.menu.show()
96
97         self.font = load_font("fixed")
98
99         self.update_keylist()
100
101         win.show()
102
103     # Add a key to the left hand side of the user interface
104
105     def add_key(self, key):
106         display_key = self.get_key_text(key)
107         list_item = GtkListItem(display_key)
108         list_item.set_data("raw_key", key) # Store raw key in item data
109         self.list.add(list_item)
110         list_item.show()
111
112     # Event handler registered by build_ui()
113
114     def event_handler(self, event, menu):
115         return FALSE
116
117     # Set the text to appear in the right hand side of the user interface 
118
119     def set_value_text(self, item):
120
121         # Clear old old value in text window
122
123         self.text.delete_text(0, self.text.get_length())
124         
125         if type(item) == str:
126
127             # The text widget has trouble inserting text containing NULL
128             # characters.
129             
130             item = string.replace(item, "\x00", ".")
131             
132             self.text.insert(self.font, None, None, item)
133
134         else:
135
136             # A non-text item
137             
138             self.text.insert(self.font, None, None, repr(item))
139             
140     # This function is called when a key is selected in the left hand side
141     # of the user interface.
142
143     def key_selected(self, list, list_item):
144         key = list_item.children()[0].get()
145
146         # Look for a match in the value display function list
147
148         text = self.dict[list_item.get_data("raw_key")]
149
150         for entry in self.get_value_text_fns:
151             if re.match(entry[0], key):
152                 text = entry[1](text)
153                 break
154
155         self.set_value_text(text)
156
157     # Refresh the key list by removing all items and re-inserting them.
158     # Items are only inserted if they pass through the filter regexp.
159
160     def update_keylist(self):
161         self.list.remove_items(self.list.children())
162         self.set_value_text("")
163         for k in self.dict.keys():
164             if re.match(self.filter_regex, k):
165                 self.add_key(k)
166
167     # Invoked when the user hits return in the filter text entry widget.
168
169     def filter_activated(self, entry):
170         self.filter_regex = entry.get_text()
171         self.update_keylist()
172
173     # Register a key display function
174
175     def register_get_key_text_fn(self, fn):
176         self.get_key_text = fn
177
178     # Register a value display function
179
180     def register_get_value_text_fn(self, regexp, fn):
181         self.get_value_text_fns.append((regexp, fn))
182
183 #
184 # A utility function to convert a string to the standard hex + ascii format.
185 # To display all values in hex do:
186 #   register_get_value_text_fn("", gtkdictbrowser.hex_string)
187 #
188
189 def hex_string(data):
190     """Return a hex dump of a string as a string.
191
192     The output produced is in the standard 16 characters per line hex +
193     ascii format:
194
195     00000000: 40 00 00 00 00 00 00 00  40 00 00 00 01 00 04 80  @....... @.......
196     00000010: 01 01 00 00 00 00 00 01  00 00 00 00              ........ ....
197     """
198     
199     pos = 0                             # Position in data
200     line = 0                            # Line of data
201     
202     hex = ""                            # Hex display
203     ascii = ""                          # ASCII display
204
205     result = ""
206     
207     while pos < len(data):
208         
209         # Start with header
210         
211         if pos % 16 == 0:
212             hex = "%08x: " % (line * 16)
213             ascii = ""
214             
215         # Add character
216             
217         hex = hex + "%02x " % (ord(data[pos]))
218         
219         if ord(data[pos]) < 32 or ord(data[pos]) > 176:
220             ascii = ascii + '.'
221         else:
222             ascii = ascii + data[pos]
223                 
224         pos = pos + 1
225             
226         # Add separator if half way
227             
228         if pos % 16 == 8:
229             hex = hex + " "
230             ascii = ascii + " "
231
232         # End of line
233
234         if pos % 16 == 0:
235             result = result + "%s %s\n" % (hex, ascii)
236             line = line + 1
237             
238     # Leftover bits
239
240     if pos % 16 != 0:
241
242         # Pad hex string
243
244         for i in range(0, (16 - (pos % 16))):
245             hex = hex + "   "
246
247         # Half way separator
248
249         if (pos % 16) < 8:
250             hex = hex + " "
251
252         result = result + "%s %s\n" % (hex, ascii)
253
254     return result
255
256 # For testing purposes, create a fixed dictionary to browse with
257
258 if __name__ == "__main__":
259
260     dict = {"chicken": "ham", "spam": "fun", "subdict": {"a": "b", "c": "d"}}
261
262     db = GtkDictBrowser(dict)
263
264     db.build_ui("GtkDictBrowser")
265
266     # Override Python's handling of ctrl-c so we can break out of the
267     # gui from the command line.
268
269     import signal
270     signal.signal(signal.SIGINT, signal.SIG_DFL)
271
272     mainloop()