287 lines
7.7 KiB
Python
287 lines
7.7 KiB
Python
#
|
|
#
|
|
#
|
|
|
|
from gi.repository import (Gtk, Gdk)
|
|
|
|
import ui
|
|
from logitech.devices.constants import (STATUS, PROPS)
|
|
|
|
|
|
_SMALL_DEVICE_ICON_SIZE = Gtk.IconSize.BUTTON
|
|
_DEVICE_ICON_SIZE = Gtk.IconSize.DIALOG
|
|
_STATUS_ICON_SIZE = Gtk.IconSize.LARGE_TOOLBAR
|
|
_PLACEHOLDER = '~'
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
def _toggle_info_button(label, widget):
|
|
toggle = lambda a, w: w.set_visible(a.get_active())
|
|
action = ui.action._toggle_action('info', label, toggle, widget)
|
|
return action.create_tool_item()
|
|
|
|
def _receiver_box(name):
|
|
icon = Gtk.Image.new_from_icon_name(name, _SMALL_DEVICE_ICON_SIZE)
|
|
|
|
label = Gtk.Label('Initializing...')
|
|
label.set_name('label')
|
|
label.set_alignment(0, 0.5)
|
|
|
|
toolbar = Gtk.Toolbar()
|
|
toolbar.set_name('toolbar')
|
|
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
|
toolbar.set_icon_size(Gtk.IconSize.MENU)
|
|
toolbar.set_show_arrow(False)
|
|
|
|
hbox = Gtk.HBox(homogeneous=False, spacing=8)
|
|
hbox.pack_start(icon, False, False, 0)
|
|
hbox.pack_start(label, True, True, 0)
|
|
hbox.pack_end(toolbar, False, False, 0)
|
|
|
|
info_label = Gtk.Label()
|
|
info_label.set_name('info-label')
|
|
info_label.set_alignment(0, 0.5)
|
|
info_label.set_padding(8, 2)
|
|
info_label.set_selectable(True)
|
|
|
|
info_box = Gtk.Frame()
|
|
info_box.add(info_label)
|
|
info_box.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
|
|
|
|
toolbar.insert(_toggle_info_button('Receiver info', info_box), 0)
|
|
toolbar.insert(ui.action.pair.create_tool_item(), -1)
|
|
|
|
vbox = Gtk.VBox(homogeneous=False, spacing=2)
|
|
vbox.set_border_width(4)
|
|
vbox.pack_start(hbox, True, True, 0)
|
|
vbox.pack_start(info_box, True, True, 0)
|
|
|
|
frame = Gtk.Frame()
|
|
frame.add(vbox)
|
|
frame.show_all()
|
|
info_box.set_visible(False)
|
|
return frame
|
|
|
|
|
|
def _device_box():
|
|
icon = Gtk.Image.new_from_icon_name('image-missing', _DEVICE_ICON_SIZE)
|
|
icon.set_name('icon')
|
|
icon.set_alignment(0.5, 0)
|
|
|
|
label = Gtk.Label('Initializing...')
|
|
label.set_name('label')
|
|
label.set_alignment(0, 0.5)
|
|
label.set_padding(4, 4)
|
|
|
|
battery_icon = Gtk.Image.new_from_icon_name('battery_unknown', _STATUS_ICON_SIZE)
|
|
|
|
battery_label = Gtk.Label()
|
|
battery_label.set_width_chars(6)
|
|
battery_label.set_alignment(0, 0.5)
|
|
|
|
light_icon = Gtk.Image.new_from_icon_name('light_unknown', _STATUS_ICON_SIZE)
|
|
|
|
light_label = Gtk.Label()
|
|
light_label.set_alignment(0, 0.5)
|
|
light_label.set_width_chars(8)
|
|
|
|
toolbar = Gtk.Toolbar()
|
|
toolbar.set_name('toolbar')
|
|
toolbar.set_style(Gtk.ToolbarStyle.ICONS)
|
|
toolbar.set_icon_size(Gtk.IconSize.MENU)
|
|
toolbar.set_show_arrow(False)
|
|
|
|
status_box = Gtk.HBox(homogeneous=False, spacing=0)
|
|
status_box.set_name('status')
|
|
status_box.pack_start(battery_icon, False, True, 0)
|
|
status_box.pack_start(battery_label, False, True, 0)
|
|
status_box.pack_start(light_icon, False, True, 0)
|
|
status_box.pack_start(light_label, False, True, 0)
|
|
status_box.pack_end(toolbar, False, False, 0)
|
|
|
|
info_label = Gtk.Label()
|
|
info_label.set_name('info-label')
|
|
info_label.set_alignment(0, 0.5)
|
|
info_label.set_padding(8, 2)
|
|
info_label.set_selectable(True)
|
|
|
|
info_box = Gtk.Frame()
|
|
info_box.add(info_label)
|
|
|
|
toolbar.insert(_toggle_info_button('Device info', info_box), 0)
|
|
toolbar.insert(ui.action.unpair.create_tool_item(), -1)
|
|
|
|
vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
|
vbox.pack_start(label, True, True, 0)
|
|
vbox.pack_start(status_box, True, True, 0)
|
|
vbox.pack_start(info_box, True, True, 0)
|
|
|
|
box = Gtk.HBox(homogeneous=False, spacing=4)
|
|
box.set_border_width(4)
|
|
box.pack_start(icon, False, False, 0)
|
|
box.pack_start(vbox, True, True, 0)
|
|
box.show_all()
|
|
|
|
frame = Gtk.Frame()
|
|
frame.add(box)
|
|
info_box.set_visible(False)
|
|
return frame
|
|
|
|
|
|
def toggle(window, trigger):
|
|
# print 'window toggle', window, trigger
|
|
if window.get_visible():
|
|
position = window.get_position()
|
|
window.hide()
|
|
window.move(*position)
|
|
else:
|
|
if trigger and type(trigger) == Gtk.StatusIcon:
|
|
x, y = window.get_position()
|
|
if x == 0 and y == 0:
|
|
x, y, _ = Gtk.StatusIcon.position_menu(Gtk.Menu(), trigger)
|
|
window.move(x, y)
|
|
window.present()
|
|
return True
|
|
|
|
|
|
def create(title, name, max_devices, systray=False):
|
|
window = Gtk.Window()
|
|
window.set_title(title)
|
|
window.set_icon_name(ui.appicon(0))
|
|
window.set_role('status-window')
|
|
|
|
vbox = Gtk.VBox(homogeneous=False, spacing=4)
|
|
vbox.set_border_width(4)
|
|
|
|
rbox = _receiver_box(name)
|
|
vbox.add(rbox)
|
|
for i in range(1, 1 + max_devices):
|
|
dbox = _device_box()
|
|
vbox.add(dbox)
|
|
vbox.set_visible(True)
|
|
|
|
window.add(vbox)
|
|
|
|
geometry = Gdk.Geometry()
|
|
geometry.min_width = 320
|
|
geometry.min_height = 20
|
|
window.set_geometry_hints(vbox, geometry, Gdk.WindowHints.MIN_SIZE)
|
|
window.set_resizable(False)
|
|
|
|
window.toggle_visible = lambda i: toggle(window, i)
|
|
|
|
if systray:
|
|
window.set_keep_above(True)
|
|
window.connect('delete-event', toggle)
|
|
else:
|
|
window.connect('delete-event', Gtk.main_quit)
|
|
|
|
return window
|
|
|
|
#
|
|
#
|
|
#
|
|
|
|
def _info_text(dev):
|
|
fw_text = '\n'.join(['%-12s\t<tt>%s%s%s</tt>' %
|
|
(f.kind, f.name, ' ' if f.name else '', f.version) for f in dev.firmware])
|
|
return ('<small>'
|
|
'Serial \t\t<tt>%s</tt>\n'
|
|
'%s'
|
|
'</small>' % (dev.serial, fw_text))
|
|
|
|
|
|
def _update_receiver_box(frame, receiver):
|
|
label, toolbar, info_label = ui.find_children(frame, 'label', 'toolbar', 'info-label')
|
|
|
|
label.set_text(receiver.status_text or '')
|
|
|
|
if receiver.status < STATUS.CONNECTED:
|
|
toolbar.set_sensitive(False)
|
|
toolbar.get_children()[0].set_active(False)
|
|
info_label.set_text('')
|
|
else:
|
|
toolbar.set_sensitive(True)
|
|
if not info_label.get_text():
|
|
info_label.set_markup(_info_text(receiver))
|
|
|
|
|
|
def _update_device_box(frame, dev):
|
|
if dev is None:
|
|
frame.set_visible(False)
|
|
frame.set_name(_PLACEHOLDER)
|
|
return
|
|
|
|
icon, label, info_label = ui.find_children(frame, 'icon', 'label', 'info-label')
|
|
|
|
if frame.get_name() != dev.name:
|
|
frame.set_name(dev.name)
|
|
icon.set_from_icon_name(ui.get_icon(dev.name, dev.kind), _DEVICE_ICON_SIZE)
|
|
label.set_markup('<b>' + dev.name + '</b>')
|
|
frame.set_visible(True)
|
|
|
|
status = ui.find_children(frame, 'status')
|
|
status_icons = status.get_children()
|
|
toolbar = status_icons[-1]
|
|
if dev.status < STATUS.CONNECTED:
|
|
icon.set_sensitive(False)
|
|
label.set_sensitive(False)
|
|
status.set_sensitive(False)
|
|
for c in status_icons[1:-1]:
|
|
c.set_visible(False)
|
|
toolbar.get_children()[0].set_active(False)
|
|
return
|
|
|
|
icon.set_sensitive(True)
|
|
label.set_sensitive(True)
|
|
status.set_sensitive(True)
|
|
if not info_label.get_text():
|
|
info_label.set_markup(_info_text(dev))
|
|
|
|
battery_icon, battery_label = status_icons[0:2]
|
|
battery_level = dev.props.get(PROPS.BATTERY_LEVEL)
|
|
if battery_level is None:
|
|
battery_icon.set_from_icon_name('battery_unknown', _STATUS_ICON_SIZE)
|
|
battery_icon.set_sensitive(False)
|
|
battery_label.set_visible(False)
|
|
else:
|
|
icon_name = 'battery_%03d' % (20 * ((battery_level + 10) // 20))
|
|
battery_icon.set_from_icon_name(icon_name, _STATUS_ICON_SIZE)
|
|
battery_icon.set_sensitive(True)
|
|
battery_label.set_text('%d%%' % battery_level)
|
|
battery_label.set_visible(True)
|
|
|
|
battery_status = dev.props.get(PROPS.BATTERY_STATUS)
|
|
battery_icon.set_tooltip_text(battery_status or '')
|
|
|
|
light_icon, light_label = status_icons[2:4]
|
|
light_level = dev.props.get(PROPS.LIGHT_LEVEL)
|
|
if light_level is None:
|
|
light_icon.set_visible(False)
|
|
light_label.set_visible(False)
|
|
else:
|
|
icon_name = 'light_%03d' % (20 * ((light_level + 50) // 100))
|
|
light_icon.set_from_icon_name(icon_name, _STATUS_ICON_SIZE)
|
|
light_icon.set_visible(True)
|
|
light_label.set_text('%d lux' % light_level)
|
|
light_label.set_visible(True)
|
|
|
|
for b in toolbar.get_children()[:-1]:
|
|
b.set_sensitive(True)
|
|
|
|
|
|
def update(window, receiver):
|
|
if window and window.get_child():
|
|
window.set_icon_name(ui.appicon(receiver.status))
|
|
|
|
vbox = window.get_child()
|
|
controls = list(vbox.get_children())
|
|
|
|
_update_receiver_box(controls[0], receiver)
|
|
|
|
for index in range(1, len(controls)):
|
|
dev = receiver.devices[index] if index in receiver.devices else None
|
|
_update_device_box(controls[index], dev)
|