# Source code for powerline.segments.i3wm

    # vim:fileencoding=utf-8:noet
    from __future__ import (unicode_literals, division, absolute_import, print_function)

    import re

    from powerline.theme import requires_segment_info
    from powerline.bindings.wm import get_i3_connection

    WORKSPACE_REGEX = re.compile(r'^[0-9]+: ?')

    def workspace_groups(w):
    	group = []
    	if w.focused:
    		group.append('workspace:focused')
    		group.append('w_focused')
    	if w.urgent:
    		group.append('workspace:urgent')
    		group.append('w_urgent')
    	if w.visible:
    		group.append('workspace:visible')
    		group.append('w_visible')
    	group.append('workspace')
    	return group

    def format_name(name, strip=False):
    	if strip:
    		return WORKSPACE_REGEX.sub('', name, count=1)
    	return name

    def is_empty_workspace(workspace, containers):
    	if workspace.focused or workspace.visible:
    		return False
    	wins = [win for win in containers[workspace.name].leaves()]
    	return False if len(wins) > 0 else True

    WS_ICONS = {"multiple": "M"}

    def get_icon(workspace, separator, icons, show_multiple_icons, ws_containers):
    	icons_tmp = WS_ICONS
    	icons_tmp.update(icons)
    	icons = icons_tmp

    	wins = [win for win in ws_containers[workspace.name].leaves() \
    		if win.parent.scratchpad_state == 'none']
    	if len(wins) == 0:
    		return ''

    	result = ''
    	cnt = 0
    	for key in icons:
    		if not icons[key] or len(icons[key]) < 1:
    			continue
    		if any(key in win.window_class for win in wins if win.window_class):
    			result += (separator if cnt > 0 else '') + icons[key]
    			cnt += 1
    	if not show_multiple_icons and cnt > 1:
    		if 'multiple' in icons:
    			return icons['multiple']
    		else:
    			return ''
    	return result

    @requires_segment_info
    def workspaces(pl, segment_info, only_show=None, output=None, strip=0, format='{name}',
    	icons=WS_ICONS, sort_workspaces=False, show_output=False, priority_workspaces=[],
    	hide_empty_workspaces=False):
    	'''Return list of used workspaces

    	:param list only_show:
    		Specifies which workspaces to show. Valid entries are ``"visible"``,
    		``"urgent"`` and ``"focused"``. If omitted or ``null`` all workspaces
    		are shown.
    	:param str output:
    		May be set to the name of an X output. If specified, only workspaces
    		on that output are shown. Overrides automatic output detection by
    		the lemonbar renderer and bindings.
    		Use "__all__" to show workspaces on all outputs.
    	:param int strip:
    		Specifies how many characters from the front of each workspace name
    		should be stripped (e.g. to remove workspace numbers). Defaults to zero.
    	:param str format:
    		Specifies the format used to display workspaces; defaults to ``{name}``.
    		Valid fields are: ``name`` (workspace name), ``number`` (workspace number
    		if present), `stipped_name`` (workspace name stripped of leading number),
    		``icon`` (if available, icon for application running in the workspace,
    		uses the ``multiple`` icon instead of multiple different icons), ``multi_icon``
    		(similar to ``icon``, but does not use ``multiple``, instead joins all icons
    		with a single space)
    	:param dict icons:
    		A dictionary mapping a substring of window classes to strings to be used as an
    		icon for that window class.
    		Further, there is a ``multiple`` icon for workspaces containing more than one
    		window.
    	:param bool sort_workspaces:
    		Sort the workspaces displayed by their name according to the natural ordering.
    	:param bool show_output:
    		Shows the name of the output if more than one output is connected.
    	:param list priority_workspaces:
    		A list of workspace names to be sorted before any other workspaces in the given
    		order.
    	:param bool hide_empty_workspaces:
    		Hides all workspaces without any open window.
    		Also hides non-focussed workspaces containing only an open scratchpad.

    	Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace`` or ``output``.
    	'''
    	conn = get_i3_connection()

    	if not output == "__all__":
    		output = output or segment_info.get('output')
    	else:
    		output = None

    	if output:
    		output = [output]
    	else:
    		output = [o.name for o in conn.get_outputs() if o.active]

    	def sort_ws(ws):
    		if sort_workspaces:
    			def natural_key(ws):
    				str = ws.name
    				return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', str)]
    			ws = sorted(ws, key=natural_key)
    		result = []
    		for n in priority_workspaces:
    			result += [w for w in ws if w.name == n]
    		return result + [w for w in ws if not w.name in priority_workspaces]

    	ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()}

    	if len(output) <= 1:
    		res = []
    		if show_output:
    			res += [{
    				'contents': output[0],
    				'highlight_groups': ['output']
    			}]
    		res += [{
    			'contents': format.format(name = w.name[min(len(w.name), strip):],
    				stripped_name = format_name(w.name, strip=True),
    				number = w.num,
    				icon = get_icon(w, '', icons, False, ws_containers),
    				multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
    			'highlight_groups': workspace_groups(w)
    			} for w in sort_ws(conn.get_workspaces()) \
    					if (not only_show or any(getattr(w, tp) for tp in only_show)) \
    					if w.output == output[0] \
    					if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))]
    		return res
    	else:
    		res = []
    		for n in output:
    			if show_output:
    				res += [{
    					'contents': n,
    					'highlight_groups': ['output']
    				}]
    			res += [{
    				'contents': format.format(name = w.name[min(len(w.name), strip):],
    					stripped_name = format_name(w.name, strip=True),
    					number = w.num,
    					icon = get_icon(w, '', icons, False, ws_containers),
    					multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
    				'highlight_groups': workspace_groups(w)
    				} for w in sort_ws(conn.get_workspaces()) \
    						if (not only_show or any(getattr(w, tp) for tp in only_show)) \
    						if w.output == n \
    						if not (hide_empty_workspaces and is_empty_workspace(w, ws_containers))]
    		return res

    @requires_segment_info
    def workspace(pl, segment_info, workspace=None, strip=False, format=None, icons=WS_ICONS):
    	'''Return the specified workspace name

    	:param str workspace:
    		Specifies which workspace to show. If unspecified, may be set by the
    		``list_workspaces`` lister if used, otherwise falls back to
    		currently focused workspace.

    	:param bool strip:
    		Specifies whether workspace numbers (in the ``1: name`` format) should
    		be stripped from workspace names before being displayed. Defaults to false.
    		Deprecated: Use {name} or {stripped_name} of format instead.

    	:param str format:
    		Specifies the format used to display workspaces; defaults to ``{name}``.
    		Valid fields are: ``name`` (workspace name), ``number`` (workspace number
    		if present), `stipped_name`` (workspace name stripped of leading number),
    		``icon`` (if available, icon for application running in the workspace,
    		uses the ``multiple`` icon instead of multiple different icons), ``multi_icon``
    		(similar to ``icon``, but does not use ``multiple``, instead joins all icons
    		with a single space)

    	:param dict icons:
    		A dictionary mapping a substring of window classes to strings to be used as an
    		icon for that window class.
    		Further, there is a ``multiple`` icon for workspaces containing more than one
    		window.

    	Highlight groups used: ``workspace`` or ``w_visible``, ``workspace:visible``, ``workspace`` or ``w_focused``, ``workspace:focused``, ``workspace`` or ``w_urgent``, ``workspace:urgent``, ``workspace``.
    	'''
    	if format == None:
    		format = '{stripped_name}' if strip else '{name}'

    	conn = get_i3_connection()
    	ws_containers = {w_con.name : w_con for w_con in conn.get_tree().workspaces()}

    	if workspace:
    		try:
    			w = next((
    				w for w in conn.get_workspaces()
    				if w.name == workspace
    			))
    		except StopIteration:
    			return None
    	elif segment_info.get('workspace'):
    		w = segment_info['workspace']
    	else:
    		try:
    			w = next((
    				w for w in conn.get_workspaces()
    				if w.focused
    			))
    		except StopIteration:
    			return None

    	return [{
    		'contents': format.format(name = w.name,
    			stripped_name = format_name(w.name, strip=True),
    			number = w.num,
    			icon = get_icon(w, '', icons, False, ws_containers),
    			multi_icon = get_icon(w, ' ', icons, True, ws_containers)),
    		'highlight_groups': workspace_groups(w)
    		}]

    @requires_segment_info
    def mode(pl, segment_info, names={'default': None}):
    	'''Returns current i3 mode

    	:param dict names:
    		Specifies the string to show for various modes.
    		Use ``null`` to hide a mode (``default`` is hidden by default).

    	Highligh groups used: ``mode``
    	'''
    	mode = segment_info['mode']
    	if mode in names:
    		return names[mode]
    	return mode

    def scratchpad_groups(w):
    	group = []
    	if w.urgent:
    		group.append('scratchpad:urgent')
    	if w.nodes[0].focused:
    		group.append('scratchpad:focused')
    	if w.workspace().name != '__i3_scratch':
    		group.append('scratchpad:visible')
    	group.append('scratchpad')
    	return group

    SCRATCHPAD_ICONS = {
    	'fresh': 'O',
    	'changed': 'X',
    }

    def scratchpad(pl, icons=SCRATCHPAD_ICONS):
    	'''Returns the windows currently on the scratchpad

    	:param dict icons:
    		Specifies the strings to show for the different scratchpad window states. Must
    		contain the keys ``fresh`` and ``changed``.

    	Highlight groups used: ``scratchpad`` or ``scratchpad:visible``, ``scratchpad`` or ``scratchpad:focused``, ``scratchpad`` or ``scratchpad:urgent``.
    	'''

    	return [
    		{
    			'contents': icons.get(w.scratchpad_state, icons['changed']),
    			'highlight_groups': scratchpad_groups(w)
    		}
    		for w in get_i3_connection().get_tree().descendants()
    		if w.scratchpad_state != 'none'
    	]

    def active_window(pl, cutoff=100):
    	'''Returns the title of the currently active window.

    	:param int cutoff:
    		Maximum title length. If the title is longer, the window_class is used instead.

    	Highlight groups used: ``active_window_title``.
    	'''

    	focused = get_i3_connection().get_tree().find_focused()
    	cont = focused.name
    	if len(cont) > cutoff:
    		cont = focused.window_class

    	return [{'contents': cont, 'highlight_groups': ['active_window_title']}] if focused.name != focused.workspace().name else []