1# ARandR -- Another XRandR GUI 2# Copyright (C) 2008 -- 2011 chrysn <chrysn@fsfe.org> 3# 4# This program is free software: you can redistribute it and/or modify 5# it under the terms of the GNU General Public License as published by 6# the Free Software Foundation, either version 3 of the License, or 7# (at your option) any later version. 8# 9# This program is distributed in the hope that it will be useful, 10# but WITHOUT ANY WARRANTY; without even the implied warranty of 11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12# GNU General Public License for more details. 13# 14# You should have received a copy of the GNU General Public License 15# along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17"""Exceptions and generic classes""" 18 19# pylint: disable=fixme 20 21from math import pi 22 23 24class FileLoadError(Exception): 25 pass 26 27 28class FileSyntaxError(FileLoadError): 29 """A file's syntax could not be parsed.""" 30 31 32class InadequateConfiguration(Exception): 33 """A configuration is incompatible with the current state of X.""" 34 35 36class BetterList(list): 37 """List that can be split like a string""" 38 39 def indices(self, item): 40 i = -1 41 while True: 42 try: 43 i = self.index(item, i + 1) 44 except ValueError: 45 break 46 yield i 47 48 def split(self, item): 49 indices = list(self.indices(item)) 50 yield self[:indices[0]] 51 for x in (self[a + 1:b] for (a, b) in zip(indices[:-1], indices[1:])): 52 yield x 53 yield self[indices[-1] + 1:] 54 55 56class Size(tuple): 57 """2-tuple of width and height that can be created from a '<width>x<height>' string""" 58 def __new__(cls, arg): 59 if isinstance(arg, str): 60 arg = [int(x) for x in arg.split("x")] 61 arg = tuple(arg) 62 assert len(arg) == 2 63 return super(Size, cls).__new__(cls, arg) 64 65 width = property(lambda self: self[0]) 66 height = property(lambda self: self[1]) 67 68 def __str__(self): 69 return "%dx%d" % self 70 71 72class NamedSize: 73 """Object that behaves like a size, but has an additional name attribute""" 74 75 def __init__(self, size, name): 76 self._size = size 77 self.name = name 78 79 width = property(lambda self: self[0]) 80 height = property(lambda self: self[1]) 81 82 def __str__(self): 83 if "%dx%d" % (self.width, self.height) in self.name: 84 return self.name 85 return "%s (%dx%d)" % (self.name, self.width, self.height) 86 87 def __iter__(self): 88 return self._size.__iter__() 89 90 def __getitem__(self, i): 91 return self._size[i] 92 93 def __len__(self): 94 return 2 95 96 97class Position(tuple): 98 """2-tuple of left and top that can be created from a '<left>x<top>' string""" 99 def __new__(cls, arg): 100 if isinstance(arg, str): 101 arg = [int(x) for x in arg.split("x")] 102 arg = tuple(arg) 103 assert len(arg) == 2 104 return super(Position, cls).__new__(cls, arg) 105 106 left = property(lambda self: self[0]) 107 top = property(lambda self: self[1]) 108 109 def __str__(self): 110 return "%dx%d" % self 111 112 113class Geometry(tuple): 114 """4-tuple of width, height, left and top that can be created from an XParseGeometry style string""" 115 # FIXME: use XParseGeometry instead of an own incomplete implementation 116 def __new__(cls, width, height=None, left=None, top=None): 117 if isinstance(width, str): 118 width, rest = width.split("x") 119 height, left, top = rest.split("+") 120 return super(Geometry, cls).__new__(cls, (int(width), int(height), int(left), int(top))) 121 122 def __str__(self): 123 return "%dx%d+%d+%d" % self 124 125 width = property(lambda self: self[0]) 126 height = property(lambda self: self[1]) 127 left = property(lambda self: self[2]) 128 top = property(lambda self: self[3]) 129 130 position = property(lambda self: Position(self[2:4])) 131 size = property(lambda self: Size(self[0:2])) 132 133 134class Rotation(str): 135 """String that represents a rotation by a multiple of 90 degree""" 136 137 def __init__(self, _original_me): 138 super().__init__() 139 if self not in ('left', 'right', 'normal', 'inverted'): 140 raise Exception("No know rotation.") 141 is_odd = property(lambda self: self in ('left', 'right')) 142 _angles = {'left': pi / 2, 'inverted': pi, 'right': 3 * pi / 2, 'normal': 0} 143 angle = property(lambda self: Rotation._angles[self]) 144 145 def __repr__(self): 146 return '<Rotation %s>' % self 147 148 149LEFT = Rotation('left') 150RIGHT = Rotation('right') 151INVERTED = Rotation('inverted') 152NORMAL = Rotation('normal') 153ROTATIONS = (NORMAL, RIGHT, INVERTED, LEFT) 154