38
38
def __init__(self, proxy_object=None, properties=None, *args,
40
# Type conversion mapping
42
dbus.ObjectPath: unicode,
44
dbus.Signature: unicode,
52
dbus.Dictionary: dict,
59
40
self.proxy = proxy_object # Mandos Client proxy object
61
42
if properties is None:
62
43
self.properties = dict()
64
self.properties = dict(self.convert_property(prop, val)
66
properties.iteritems())
45
self.properties = properties
67
46
self.proxy.connect_to_signal("PropertyChanged",
68
47
self.property_changed,
72
51
if properties is None:
73
self.properties.update(
74
self.convert_property(prop, val)
76
self.proxy.GetAll(client_interface,
78
dbus.PROPERTIES_IFACE).iteritems())
52
self.properties.update(self.proxy.GetAll(client_interface,
54
dbus.PROPERTIES_IFACE))
79
55
super(MandosClientPropertyCache, self).__init__(
80
56
proxy_object=proxy_object,
81
57
properties=properties, *args, **kwargs)
83
def convert_property(self, property, value):
84
"""This converts the arguments from a D-Bus signal, which are
85
D-Bus types, into normal Python types, using a conversion
86
function from "self.type_map".
88
property_name = unicode(property) # Always a dbus.String
89
if isinstance(value, dbus.UTF8String):
90
# Should not happen, but prepare for it anyway
91
value = dbus.String(str(value).decode("utf-8"))
93
convfunc = self.type_map[type(value)]
95
# Unknown type, return unmodified
96
return property_name, value
97
return property_name, convfunc(value)
98
59
def property_changed(self, property=None, value=None):
99
60
"""This is called whenever we get a PropertyChanged signal
100
61
It updates the changed property in the "properties" dict.
102
# Convert name and value
103
property_name, cvalue = self.convert_property(property, value)
104
63
# Update properties dict with new value
105
self.properties[property_name] = cvalue
64
self.properties[property] = value
108
67
class MandosClientWidget(urwid.FlowWidget, MandosClientPropertyCache):
160
119
% self.properties)
161
120
if not urwid.supports_unicode():
162
121
self._text = self._text.encode("ascii", "replace")
163
textlist = [(u"normal", u"BLÄRGH: "), (u"bold", self._text)]
122
textlist = [(u"normal", u"BLARGH: "), (u"bold", self._text)]
164
123
self._text_widget.set_text(textlist)
165
124
self._focus_text_widget.set_text([(with_standout[text[0]],
184
class ConstrainedListBox(urwid.ListBox):
185
"""Like a normal urwid.ListBox, but will consume all "up" or
186
"down" key presses, thus not allowing any containing widgets to
187
use them as an excuse to shift focus away from this widget.
189
def keypress(self, (maxcol, maxrow), key):
190
ret = super(ConstrainedListBox, self).keypress((maxcol, maxrow), key)
191
if ret in (u"up", u"down"):
225
196
class UserInterface(object):
226
197
"""This is the entire user interface - the whole screen
227
198
with boxes, lists of client widgets, etc.
230
DBusGMainLoop(set_as_default=True )
200
def __init__(self, max_log_length=1000):
201
DBusGMainLoop(set_as_default=True)
232
203
self.screen = urwid.curses_display.Screen()
225
if urwid.supports_unicode():
226
#self.divider = u"─" # \u2500
227
self.divider = u"━" # \u2501
229
#self.divider = u"-" # \u002d
230
self.divider = u"_" # \u005f
254
232
self.screen.start()
256
234
self.size = self.screen.get_cols_rows()
258
236
self.clients = urwid.SimpleListWalker([])
259
237
self.clients_dict = {}
260
self.topwidget = urwid.LineBox(urwid.ListBox(self.clients))
261
#self.topwidget = urwid.ListBox(clients)
239
# We will add Text widgets to this list
241
self.max_log_length = max_log_length
243
# We keep a reference to the log widget so we can remove it
244
# from the ListWalker without it getting destroyed
245
self.logbox = ConstrainedListBox(self.log)
247
# This keeps track of whether self.uilist currently has
248
# self.logbox in it or not
249
self.log_visible = True
250
self.log_wrap = u"any"
253
self.log_message(u"Message")
254
self.log_message(u"Message0 Message1 Message2 Message3 Message4 Message5 Message6 Message7 Message8 Message9")
255
self.log_message(u"Message10 Message11 Message12 Message13 Message14 Message15 Message16 Message17 Message18 Message19")
256
self.log_message(u"Message20 Message21 Message22 Message23 Message24 Message25 Message26 Message27 Message28 Message29")
263
258
self.busname = domain + '.Mandos'
264
259
self.main_loop = gobject.MainLoop()
284
279
self.add_new_client,
285
280
dbus_interface=server_interface,
286
281
byte_arrays=True))
287
for path, client in (mandos_clients.iteritems()):
282
for path, client in mandos_clients.iteritems():
288
283
client_proxy_object = self.bus.get_object(self.busname,
290
285
self.add_client(MandosClientWidget(server_proxy_object
298
293
=self.remove_client),
297
"""This rebuilds the User Interface.
298
Call this when the widget layout needs to change"""
300
#self.uilist.append(urwid.ListBox(self.clients))
301
self.uilist.append(urwid.Frame(ConstrainedListBox(self.clients),
302
#header=urwid.Divider(),
304
footer=urwid.Divider(div_char=self.divider)))
306
self.uilist.append(self.logbox)
308
self.topwidget = urwid.Pile(self.uilist)
310
def log_message(self, markup):
311
"""Add a log message to the log buffer."""
312
self.log.append(urwid.Text(markup, wrap=self.log_wrap))
313
if (self.max_log_length
314
and len(self.log) > self.max_log_length):
315
del self.log[0:len(self.log)-self.max_log_length-1]
317
def toggle_log_display(self):
318
"""Toggle visibility of the log buffer."""
319
self.log_visible = not self.log_visible
321
self.log_message(u"Log visibility changed to: "
322
+ unicode(self.log_visible))
324
def change_log_display(self):
325
"""Change type of log display.
326
Currently, this toggles wrapping of text lines."""
327
if self.log_wrap == u"clip":
328
self.log_wrap = u"any"
330
self.log_wrap = u"clip"
331
for textwidget in self.log:
332
textwidget.set_wrap_mode(self.log_wrap)
333
self.log_message(u"Wrap mode: " + self.log_wrap)
301
335
def find_and_remove_client(self, path, name):
302
336
"""Find an client from its object path and remove it.
337
371
path = client.proxy.object_path
338
372
del self.clients_dict[path]
373
if not self.clients_dict:
374
# Work around bug in Urwid 0.9.8.3 - if a SimpleListWalker
375
# is completely emptied, we need to recreate it.
376
self.clients = urwid.SimpleListWalker([])
341
380
def refresh(self):
361
400
def process_input(self, source, condition):
362
401
keys = self.screen.get_input()
363
translations = { u"j": u"down",
402
translations = { u"ctrl n": u"down", # Emacs
403
u"ctrl p": u"up", # Emacs
404
u"ctrl v": u"page down", # Emacs
405
u"meta v": u"page up", # Emacs
406
u" ": u"page down", # less
407
u"f": u"page down", # less
408
u"b": u"page up", # less
375
421
elif key == u"window resize":
376
422
self.size = self.screen.get_cols_rows()
424
elif key == u"\f": # Ctrl-L
426
elif key == u"l" or key == u"D":
427
self.toggle_log_display()
429
elif key == u"w" or key == u"i":
430
self.change_log_display()
432
elif key == u"?" or key == u"f1":
433
self.log_message(u"Help!")
436
if self.topwidget.get_focus() is self.logbox:
437
self.topwidget.set_focus(0)
439
self.topwidget.set_focus(self.logbox)
441
elif (key == u"end" or key == u"meta >" or key == u"G"
443
pass # xxx end-of-buffer
444
elif (key == u"home" or key == u"meta <" or key == u"g"
446
pass # xxx beginning-of-buffer
447
elif key == u"ctrl e" or key == u"$":
448
pass # xxx move-end-of-line
449
elif key == u"ctrl a" or key == u"^":
450
pass # xxx move-beginning-of-line
451
elif key == u"ctrl b" or key == u"meta (" or key == u"h":
453
elif key == u"ctrl f" or key == u"meta )" or key == u"l":
458
pass # scroll down log
380
459
elif self.topwidget.selectable():
381
460
self.topwidget.keypress(self.size, key)