Commit 549550a7 authored by Actionless Loveless's avatar Actionless Loveless Committed by chrysn

porting to python3 and gtk3, initial commit

parent f539c03d
......@@ -18,16 +18,22 @@
from math import pi
class FileLoadError(Exception): pass
class FileLoadError(Exception):
pass
class FileSyntaxError(FileLoadError):
"""A file's syntax could not be parsed."""
class InadequateConfiguration(Exception):
"""A configuration is incompatible with the current state of X."""
class BetterList(list):
"""List that can be split like a string"""
def indices(self, item):
i = -1
while True:
......@@ -40,7 +46,7 @@ class BetterList(list):
def split(self, item):
indices = list(self.indices(item))
yield self[:indices[0]]
for x in (self[a+1:b] for (a,b) in zip(indices[:-1], indices[1:])):
for x in (self[a+1:b] for (a, b) in zip(indices[:-1], indices[1:])):
yield x
yield self[indices[-1]+1:]
......@@ -48,30 +54,34 @@ class BetterList(list):
class Size(tuple):
"""2-tuple of width and height that can be created from a '<width>x<height>' string"""
def __new__(cls, arg):
if isinstance(arg, basestring):
if isinstance(arg, str):
arg = [int(x) for x in arg.split("x")]
arg = tuple(arg)
assert len(arg)==2
assert len(arg) == 2
return super(Size, cls).__new__(cls, arg)
width = property(lambda self:self[0])
height = property(lambda self:self[1])
width = property(lambda self: self[0])
height = property(lambda self: self[1])
def __str__(self):
return "%dx%d"%self
return "%dx%d" % self
class NamedSize(object):
"""Object that behaves like a size, but has an additional name attribute"""
def __init__(self, size, name):
self._size = size
self.name = name
width = property(lambda self:self[0])
height = property(lambda self:self[1])
width = property(lambda self: self[0])
height = property(lambda self: self[1])
def __str__(self):
if "%dx%d"%(self.width, self.height) in self.name:
if "%dx%d" % (self.width, self.height) in self.name:
return self.name
else:
return "%s (%dx%d)"%(self.name, self.width, self.height)
return "%s (%dx%d)" % (self.name, self.width, self.height)
def __iter__(self):
return self._size.__iter__()
......@@ -82,51 +92,57 @@ class NamedSize(object):
def __len__(self):
return 2
class Position(tuple):
"""2-tuple of left and top that can be created from a '<left>x<top>' string"""
def __new__(cls, arg):
if isinstance(arg, basestring):
if isinstance(arg, str):
arg = [int(x) for x in arg.split("x")]
arg = tuple(arg)
assert len(arg)==2
assert len(arg) == 2
return super(Position, cls).__new__(cls, arg)
left = property(lambda self:self[0])
top = property(lambda self:self[1])
left = property(lambda self: self[0])
top = property(lambda self: self[1])
def __str__(self):
return "%dx%d"%self
return "%dx%d" % self
class Geometry(tuple):
"""4-tuple of width, height, left and top that can be created from an XParseGeometry style string"""
# FIXME: use XParseGeometry instead of an own incomplete implementation
def __new__(cls, width, height=None, left=None, top=None):
if isinstance(width, basestring):
width,rest = width.split("x")
height,left,top = rest.split("+")
if isinstance(width, str):
width, rest = width.split("x")
height, left, top = rest.split("+")
return super(Geometry, cls).__new__(cls, (int(width), int(height), int(left), int(top)))
def __str__(self):
return "%dx%d+%d+%d"%self
return "%dx%d+%d+%d" % self
width = property(lambda self:self[0])
height = property(lambda self:self[1])
left = property(lambda self:self[2])
top = property(lambda self:self[3])
width = property(lambda self: self[0])
height = property(lambda self: self[1])
left = property(lambda self: self[2])
top = property(lambda self: self[3])
position = property(lambda self:Position(self[2:4]))
size = property(lambda self:Size(self[0:2]))
position = property(lambda self: Position(self[2:4]))
size = property(lambda self: Size(self[0:2]))
class Rotation(str):
"""String that represents a rotation by a multiple of 90 degree"""
def __init__(self, original_me):
if self not in ('left','right','normal','inverted'):
if self not in ('left', 'right', 'normal', 'inverted'):
raise Exception("No know rotation.")
is_odd = property(lambda self: self in ('left','right'))
_angles = {'left':pi/2,'inverted':pi,'right':3*pi/2,'normal':0}
is_odd = property(lambda self: self in ('left', 'right'))
_angles = {'left': pi/2, 'inverted': pi, 'right': 3*pi/2, 'normal': 0}
angle = property(lambda self: Rotation._angles[self])
def __repr__(self):
return '<Rotation %s>'%self
return '<Rotation %s>' % self
LEFT = Rotation('left')
RIGHT = Rotation('right')
......
This diff is collapsed.
......@@ -15,8 +15,9 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import gobject
import gtk
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GObject, Gtk
try:
import gconf
......@@ -39,19 +40,19 @@ CYCLINGPATTERN_RECOGNITION = [
""";; esac'""",
]
class MetacityWidget(gtk.Table):
class MetacityWidget(Gtk.Table):
"""Widget that manages bindings of screenlayout scripts to metacity keybindings.
Not related to ARandR except that ARandR scripts are bound."""
def __init__(self):
gtk.Table.__init__(self, rows=13, columns=2)
Gtk.Table.__init__(self, rows=13, columns=2)
c = gconf.client_get_default()
c.add_dir('/apps/metacity/global_keybindings', gconf.CLIENT_PRELOAD_NONE)
c.add_dir('/apps/metacity/keybinding_commands', gconf.CLIENT_PRELOAD_NONE)
self.attach(gtk.Label(_("Accelerator")), 0,1,0,1)
self.attach(gtk.Label(_("Action")), 1,2,0,1)
self.attach(Gtk.Label(_("Accelerator")), 0,1,0,1)
self.attach(Gtk.Label(_("Action")), 1,2,0,1)
self.lines = []
for i in range(1,13):
......@@ -71,7 +72,7 @@ class MetacityWidget(gtk.Table):
a.props.sensitive = enable and k.props.bound
class GConfButton(gtk.Button):
class GConfButton(Gtk.Button):
"""Button connected to a gconfkey via a gconf client c.
Will call self._update when the key is changed; use self.set(value) to change the key's value."""
......@@ -104,7 +105,7 @@ class GConfButton(gtk.Button):
class KeyBindingButton(GConfButton):
"""GConfButton that will interpret the value as a keybinding and ask for a new keybinding when pressed."""
__gproperties__ = {
'bound': (gobject.TYPE_BOOLEAN, 'bound', 'slot is bound to a key', False, gobject.PARAM_READWRITE),
'bound': (GObject.TYPE_BOOLEAN, 'bound', 'slot is bound to a key', False, GObject.PARAM_READWRITE),
}
def __init__(self, *args, **kwords):
......@@ -140,34 +141,34 @@ class KeyBindingButton(GConfButton):
if not self.editing:
return
keymap = gtk.gdk.keymap_get_default()
keymap = Gtk.gdk.keymap_get_default()
translation = keymap.translate_keyboard_state(event.hardware_keycode, event.state, event.group)
if translation == None: # FIXME: metacity can also handle raw keycodes with modifiers (but can compiz?)
accel_name = "%#x"%event.hardware_keycode
else:
(keyval, egroup, level, consumed_modifiers) = translation
upper = event.keyval
accel_keyval = gtk.gdk.keyval_to_lower(upper)
accel_keyval = Gtk.gdk.keyval_to_lower(upper)
# Put shift back if it changed the case of the key, not otherwise.
if upper != accel_keyval and (consumed_modifiers & gtk.gdk.SHIFT_MASK):
consumed_modifiers &= ~(gtk.gdk.SHIFT_MASK)
if upper != accel_keyval and (consumed_modifiers & Gtk.gdk.SHIFT_MASK):
consumed_modifiers &= ~(Gtk.gdk.SHIFT_MASK)
# filter consumed/ignored modifiers
ignored_modifiers = gtk.gdk.MOD2_MASK | gtk.gdk.MOD5_MASK
accel_mods = event.state & gtk.gdk.MODIFIER_MASK & ~(consumed_modifiers | ignored_modifiers)
ignored_modifiers = Gtk.gdk.MOD2_MASK | Gtk.gdk.MOD5_MASK
accel_mods = event.state & Gtk.gdk.MODIFIER_MASK & ~(consumed_modifiers | ignored_modifiers)
if accel_mods == 0 and accel_keyval == gtk.keysyms.Escape:
if accel_mods == 0 and accel_keyval == Gtk.keysyms.Escape:
self.abort_editing()
return
if accel_mods == 0 and accel_keyval == gtk.keysyms.BackSpace:
if accel_mods == 0 and accel_keyval == Gtk.keysyms.BackSpace:
self.set('disabled')
return
if not gtk.accelerator_valid(accel_keyval, accel_mods):
if not Gtk.accelerator_valid(accel_keyval, accel_mods):
return # just modifiers
accel_name = gtk.accelerator_name(accel_keyval, accel_mods)
accel_name = Gtk.accelerator_name(accel_keyval, accel_mods)
#self.set_accelerator(accel_keyval, event.hardware_keycode, accel_mods)
#self.__old_value = None
#self.emit('accel-edited', accel_name, accel_keyval, accel_mods, event.hardware_keycode)
......@@ -177,7 +178,7 @@ class KeyBindingButton(GConfButton):
class ActionWidget(GConfButton):
"""GConfButton that will interpret the value as a command and allow changing it if it is a screenlayout script or a collection thereof."""
__gproperties__ = {
'editable': (gobject.TYPE_BOOLEAN, 'editable', 'true if property can be managed by MetacityWidget', False, gobject.PARAM_READWRITE),
'editable': (GObject.TYPE_BOOLEAN, 'editable', 'true if property can be managed by MetacityWidget', False, GObject.PARAM_READWRITE),
}
def __init__(self, *args, **kwords):
......@@ -237,13 +238,13 @@ class ActionWidget(GConfButton):
self.items = None
def on_clicked(self, widget):
m = gtk.Menu()
m = Gtk.Menu()
try:
for f in os.listdir(SCRIPTSDIR):
if not f.endswith('.sh'):
continue
text = f[:-3]
i = gtk.CheckMenuItem(text)
i = Gtk.CheckMenuItem(text)
if text in self.items:
i.props.active = True
i.connect('activate', lambda menuitem, script: self.toggle(script), text)
......@@ -252,13 +253,13 @@ class ActionWidget(GConfButton):
pass
if not m.get_children():
i = gtk.MenuItem(_("No files in %(folder)r. Save a layout first.")%{'folder':SCRIPTSDIR})
i = Gtk.MenuItem(_("No files in %(folder)r. Save a layout first.")%{'folder':SCRIPTSDIR})
i.props.sensitive = False
m.add(i)
else:
m.add(gtk.MenuItem())
m.add(Gtk.MenuItem())
i = gtk.ImageMenuItem(gtk.STOCK_CLEAR)
i = Gtk.ImageMenuItem(Gtk.STOCK_CLEAR)
i.connect('activate', lambda menuitem: self.set(""))
m.add(i)
......@@ -280,29 +281,29 @@ class ActionWidget(GConfButton):
def show_keybinder():
if not gconf:
d = gtk.MessageDialog(None, gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_CLOSE)
d = Gtk.MessageDialog(None, Gtk.DialogFlags.DESTROY_WITH_PARENT, Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE)
d.props.text = _("gconf not available.")
d.props.secondary_text = _("In order to configure metacity, you need to have the python gconf module installed.")
d.run()
d.destroy()
return
d = gtk.Window()
d = Gtk.Window()
d.props.modal = True
d.props.title = _("Keybindings (via Metacity)")
close = gtk.Button(gtk.STOCK_CLOSE)
close = Gtk.Button(Gtk.STOCK_CLOSE)
close.props.use_stock = True
close.connect('clicked', lambda *args: d.destroy())
buttons = gtk.HBox() # FIXME: use HButtonBox
buttons = Gtk.HBox() # FIXME: use HButtonBox
buttons.props.border_width = 5
buttons.pack_end(close, expand=False)
t = MetacityWidget()
contents = gtk.VBox()
contents = Gtk.VBox()
contents.pack_start(t)
l = gtk.Label(_('Click on a button in the left column and press a key combination you want to bind to a certain screen layout. (Use backspace to clear accelerators, escape to abort editing.) Then, select one or more layouts in the right column.\n\nThis will only work if you use metacity or another program reading its configuration.'))
l = Gtk.Label(_('Click on a button in the left column and press a key combination you want to bind to a certain screen layout. (Use backspace to clear accelerators, escape to abort editing.) Then, select one or more layouts in the right column.\n\nThis will only work if you use metacity or another program reading its configuration.'))
l.props.wrap = True
contents.pack_start(l)
contents.pack_end(buttons, expand=False)
......
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment