import sys import bisect # Sorting of list from enum import Enum import i3_lemonbar_config as config import i3_lemonbar_common as common sr = config.sep_right slr = config.sep_l_right sl = config.sep_left sll = config.sep_l_left class LemonUnit: def __lt__(self, other): return self.order < other.order class IconTextUnit(LemonUnit): def __init__(self, name, order, action = None, alt_scheme=None, external=None): self.name = name self.action = action self.order = order self.alt_scheme = alt_scheme # None means default self.external = external self.items = [] # List of tuples (icon, text) self.modes = [common.bar_mode.normal] def format(self): close = block(click='') if self.action is not None else '' b_color = self.alt_scheme.back_color i_color = self.alt_scheme.icon_color t_color = self.alt_scheme.text_color # Start with a major separator. Keep previous background color, set foreground color # of separator to background color of this unit blocks = [] blocks.append(block(fg=b_color, append=sl)) blocks.append(' ') # TODO perhaps more nice solution than manual spacing if len(self.items) >= 1: (icon, text) = self.items[0] blocks.append(block(click=self.action, fg=i_color, bg=b_color, font='2' , append=' ' + icon)) blocks.append(' ') blocks.append(block(fg=t_color, font='1', append=text)) if len(self.items) >= 2: for item in self.items[1:]: (icon, text) = item # Append minor separator blocks.append(' ') blocks.append(sll) blocks.append(block(fg=i_color, bg=b_color, font='2', append=' ' + icon)) blocks.append(' ') blocks.append(block(fg=t_color, font='1', append=text)) if len(self.items) >= 1: blocks.append(close) return ''.join(blocks) class ButtonsUnit(LemonUnit): def __init__(self, name, order, alt_scheme=None, external=None): self.name = name self.order = order self.alt_scheme = alt_scheme # None means default self.external = external self.items = [] # List of tuples (icon, text, action) self.modes = [common.bar_mode.normal] def format(self): b_color = self.alt_scheme.back_color i_color = self.alt_scheme.icon_color t_color = self.alt_scheme.text_color # Start with a major separator. Keep previous background color, set foreground color # of separator to background color of this unit blocks = [] blocks.append(block(fg=b_color, append=sl)) blocks.append(' ') for item in self.items: (icon, text, action) = item close = block(click='') if action is not None else '' blocks.append(block(click=action, fg=i_color, bg=b_color, font='2' , append=' ' + icon)) blocks.append(' ') blocks.append(block(fg=t_color, font='1', append=text)) blocks.append(close) return ''.join(blocks) class CustomUnit(LemonUnit): def __init__(self, name, format_function, order, action = None, alt_scheme=None, external=None): self.name = name self.action = action self.order = order self.alt_scheme = alt_scheme # None means default self.external = external self.format = format_function self.items = [] # List of tuples (icon, text) self.modes = [common.bar_mode.normal] class LemonParser: # Handle parsing of units # Apply alternating colors # Contains list of units def __init__(self): self.units = [] def register_unit(self, unit): # Keep the list sorted bisect.insort_left(self.units, unit) def remove_unit(self, unit): self.units.remove(unit) def format(self): formatted = ['%{l}'] # Start left-justified previously_left_justified = True # Iterate over all units. Apply alternating color schemes alt = 1 for unit in self.units: # Show only units appropriate for the current mode if common.mode not in unit.modes: continue # Negative order means left justified, positive means right justified if previously_left_justified and unit.order > 0: # Switch to right justified formatted.append(block(fg='-', bg='-')) # Reset so the color doesn't fill formatted.append('%{r}') previously_left_justified = False if unit.alt_scheme is None: # Apply default color scheme, which is alternating unit.alt_scheme = COLOR_SCHEME.A1 if alt == 1 else COLOR_SCHEME.A2 else: # Keep alternate color scheme # INA is a special case if unit.alt_scheme == COLOR_SCHEME.INA: unit.alt_scheme = COLOR_SCHEME.A1_INA if alt == 1 else COLOR_SCHEME.A2_INA formatted.append(unit.format()) # TODO cache last format alt = alt + 1 if alt + 1 <= 2 else 1 formatted.append(block(fg='-', bg='-')) # Not sure if needed return ' '.join(formatted) g_parser = LemonParser() def block(fg = None, bg = None, font = None, click = None, append=''): # Remeber that clickable blocks need to be closed rtn = [] if click is not None: if click == '': rtn.append('A') else: rtn.append('A:'+click+':') if fg is not None: rtn.append('F'+fg) if bg is not None: rtn.append('B'+bg) if font is not None: rtn.append('T'+font) return ''.join(['%{', ' '.join(rtn), '}', append]) class COLOR_SCHEME(Enum): A1 = (config.color_sec_b1, config.color_fore, config.color_icon) A2 = (config.color_sec_b2, config.color_fore, config.color_icon) INA = (None, None, None) # This one is special, dynamically changed to A1_INA or A2_INA A1_INA = (config.color_sec_b1, config.color_disable, config.color_disable) A2_INA = (config.color_sec_b2, config.color_disable, config.color_disable) CPU_ALERT= (config.color_cpu, config.color_back, config.color_back) NET_ALERT= (config.color_net, config.color_back, config.color_back) SPECIAL = (config.color_head, config.color_back, config.color_back) def __init__(self, back_color, text_color, icon_color): self.back_color = back_color self.text_color = text_color self.icon_color = icon_color def format_line(): return g_parser.format() def parse_line(line_in): # Parse lines that external programs have written to fifo for unit in g_parser.units: if unit.external is not None: for key,func in unit.external.items(): l = len(key) if line_in[:l] == key: func(line_in[l:].split()) break formatted_line = format_line() return formatted_line if __name__ == "__main__": for line in sys.stdin: print(parse_line(line))