diff --git a/.no-sublime-package b/.no-sublime-package new file mode 100644 index 0000000..e69de29 diff --git a/HighlightLib/__init__.py b/HighlightLib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/HighlightLib/desktop/__init__.py b/HighlightLib/desktop/__init__.py new file mode 100644 index 0000000..b91fb90 --- /dev/null +++ b/HighlightLib/desktop/__init__.py @@ -0,0 +1,281 @@ +#!/usr/bin/env python + +""" +Simple desktop integration for Python. This module provides desktop environment +detection and resource opening support for a selection of common and +standardised desktop environments. + +Copyright (C) 2005, 2006, 2007, 2008, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Desktop Detection +----------------- + +To detect a specific desktop environment, use the get_desktop function. +To detect whether the desktop environment is standardised (according to the +proposed DESKTOP_LAUNCH standard), use the is_standard function. + +Opening URLs +------------ + +To open a URL in the current desktop environment, relying on the automatic +detection of that environment, use the desktop.open function as follows: + +desktop.open("http://www.python.org") + +To override the detected desktop, specify the desktop parameter to the open +function as follows: + +desktop.open("http://www.python.org", "KDE") # Insists on KDE +desktop.open("http://www.python.org", "GNOME") # Insists on GNOME + +Without overriding using the desktop parameter, the open function will attempt +to use the "standard" desktop opening mechanism which is controlled by the +DESKTOP_LAUNCH environment variable as described below. + +The DESKTOP_LAUNCH Environment Variable +--------------------------------------- + +The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate, +as shown in some of the following examples: + +DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in + their entirety in a KDE message box. + (Command "kdialog" plus parameter.) +DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to + open URLs. + (Command "my opener", no parameters.) +DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to + open URLs. + (Command "my opener" plus parameter.) + +Details of the DESKTOP_LAUNCH environment variable convention can be found here: +http://lists.freedesktop.org/archives/xdg/2004-August/004489.html + +Other Modules +------------- + +The desktop.dialog module provides support for opening dialogue boxes. +The desktop.windows module permits the inspection of desktop windows. +""" + +__version__ = "0.4" + +import os +import sys + +# Provide suitable process creation functions. + +try: + import subprocess + def _run(cmd, shell, wait): + opener = subprocess.Popen(cmd, shell=shell) + if wait: opener.wait() + return opener.pid + + def _readfrom(cmd, shell): + opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + opener.stdin.close() + return opener.stdout.read() + + def _status(cmd, shell): + opener = subprocess.Popen(cmd, shell=shell) + opener.wait() + return opener.returncode == 0 + +except ImportError: + import popen2 + def _run(cmd, shell, wait): + opener = popen2.Popen3(cmd) + if wait: opener.wait() + return opener.pid + + def _readfrom(cmd, shell): + opener = popen2.Popen3(cmd) + opener.tochild.close() + opener.childerr.close() + return opener.fromchild.read() + + def _status(cmd, shell): + opener = popen2.Popen3(cmd) + opener.wait() + return opener.poll() == 0 + +import subprocess + +# Private functions. + +def _get_x11_vars(): + + "Return suitable environment definitions for X11." + + if not os.environ.get("DISPLAY", "").strip(): + return "DISPLAY=:0.0 " + else: + return "" + +def _is_xfce(): + + "Return whether XFCE is in use." + + # XFCE detection involves testing the output of a program. + + try: + return _readfrom(_get_x11_vars() + "xprop -root _DT_SAVE_MODE", shell=1).strip().endswith(' = "xfce4"') + except OSError: + return 0 + +def _is_x11(): + + "Return whether the X Window System is in use." + + return "DISPLAY" in os.environ + +# Introspection functions. + +def get_desktop(): + + """ + Detect the current desktop environment, returning the name of the + environment. If no environment could be detected, None is returned. + """ + + if "KDE_FULL_SESSION" in os.environ or \ + "KDE_MULTIHEAD" in os.environ: + return "KDE" + elif "GNOME_DESKTOP_SESSION_ID" in os.environ or \ + "GNOME_KEYRING_SOCKET" in os.environ: + return "GNOME" + elif sys.platform == "darwin": + return "Mac OS X" + elif hasattr(os, "startfile"): + return "Windows" + elif _is_xfce(): + return "XFCE" + + # KDE, GNOME and XFCE run on X11, so we have to test for X11 last. + + if _is_x11(): + return "X11" + else: + return None + +def use_desktop(desktop): + + """ + Decide which desktop should be used, based on the detected desktop and a + supplied 'desktop' argument (which may be None). Return an identifier + indicating the desktop type as being either "standard" or one of the results + from the 'get_desktop' function. + """ + + # Attempt to detect a desktop environment. + + detected = get_desktop() + + # Start with desktops whose existence can be easily tested. + + if (desktop is None or desktop == "standard") and is_standard(): + return "standard" + elif (desktop is None or desktop == "Windows") and detected == "Windows": + return "Windows" + + # Test for desktops where the overriding is not verified. + + elif (desktop or detected) == "KDE": + return "KDE" + elif (desktop or detected) == "GNOME": + return "GNOME" + elif (desktop or detected) == "XFCE": + return "XFCE" + elif (desktop or detected) == "Mac OS X": + return "Mac OS X" + elif (desktop or detected) == "X11": + return "X11" + else: + return None + +def is_standard(): + + """ + Return whether the current desktop supports standardised application + launching. + """ + + return "DESKTOP_LAUNCH" in os.environ + +# Activity functions. + +def open(url, desktop=None, wait=0): + + """ + Open the 'url' in the current desktop's preferred file browser. If the + optional 'desktop' parameter is specified then attempt to use that + particular desktop environment's mechanisms to open the 'url' instead of + guessing or detecting which environment is being used. + + Suggested values for 'desktop' are "standard", "KDE", "GNOME", "XFCE", + "Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH environment + variable to open the specified 'url'. DESKTOP_LAUNCH should be a command, + possibly followed by arguments, and must have any special characters + shell-escaped. + + The process identifier of the "opener" (ie. viewer, editor, browser or + program) associated with the 'url' is returned by this function. If the + process identifier cannot be determined, None is returned. + + An optional 'wait' parameter is also available for advanced usage and, if + 'wait' is set to a true value, this function will wait for the launching + mechanism to complete before returning (as opposed to immediately returning + as is the default behaviour). + """ + + # Decide on the desktop environment in use. + + desktop_in_use = use_desktop(desktop) + + if desktop_in_use == "standard": + arg = "".join([os.environ["DESKTOP_LAUNCH"], subprocess.mkarg(url)]) + return _run(arg, 1, wait) + + elif desktop_in_use == "Windows": + # NOTE: This returns None in current implementations. + return os.startfile(url) + + elif desktop_in_use == "KDE": + cmd = ["kfmclient", "exec", url] + + elif desktop_in_use == "GNOME": + cmd = ["xdg-open", url] + + elif desktop_in_use == "XFCE": + cmd = ["exo-open", url] + + elif desktop_in_use == "Mac OS X": + cmd = ["open", url] + + elif desktop_in_use == "X11" and "BROWSER" in os.environ: + cmd = [os.environ["BROWSER"], url] + + # Finish with an error where no suitable desktop was identified. + + else: + raise OSError("Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use) + + return _run(cmd, 0, wait) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/HighlightLib/desktop/dialog.py b/HighlightLib/desktop/dialog.py new file mode 100644 index 0000000..00605b7 --- /dev/null +++ b/HighlightLib/desktop/dialog.py @@ -0,0 +1,549 @@ +#!/usr/bin/env python + +""" +Simple desktop dialogue box support for Python. + +Copyright (C) 2007, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Opening Dialogue Boxes (Dialogs) +-------------------------------- + +To open a dialogue box (dialog) in the current desktop environment, relying on +the automatic detection of that environment, use the appropriate dialogue box +class: + +question = desktop.dialog.Question("Are you sure?") +result = question.open() + +To override the detected desktop, specify the desktop parameter to the open +function as follows: + +question.open("KDE") # Insists on KDE +question.open("GNOME") # Insists on GNOME + +The dialogue box options are documented in each class's docstring. + +Available dialogue box classes are listed in the desktop.dialog.available +attribute. + +Supported desktop environments are listed in the desktop.dialog.supported +attribute. +""" + +from desktop import use_desktop, _run, _readfrom, _status + +class _wrapper: + def __init__(self, handler): + self.handler = handler + +class _readvalue(_wrapper): + def __call__(self, cmd, shell): + return self.handler(cmd, shell).strip() + +class _readinput(_wrapper): + def __call__(self, cmd, shell): + return self.handler(cmd, shell)[:-1] + +class _readvalues_kdialog(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip().strip('"') + if result: + return result.split('" "') + else: + return [] + +class _readvalues_zenity(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip() + if result: + return result.split("|") + else: + return [] + +class _readvalues_Xdialog(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip() + if result: + return result.split("/") + else: + return [] + +# Dialogue parameter classes. + +class String: + + "A generic parameter." + + def __init__(self, name): + self.name = name + + def convert(self, value, program): + return [value or ""] + +class Strings(String): + + "Multiple string parameters." + + def convert(self, value, program): + return value or [] + +class StringPairs(String): + + "Multiple string parameters duplicated to make identifiers." + + def convert(self, value, program): + l = [] + for v in value: + l.append(v) + l.append(v) + return l + +class StringKeyword: + + "A keyword parameter." + + def __init__(self, keyword, name): + self.keyword = keyword + self.name = name + + def convert(self, value, program): + return [self.keyword + "=" + (value or "")] + +class StringKeywords: + + "Multiple keyword parameters." + + def __init__(self, keyword, name): + self.keyword = keyword + self.name = name + + def convert(self, value, program): + l = [] + for v in value or []: + l.append(self.keyword + "=" + v) + return l + +class Integer(String): + + "An integer parameter." + + defaults = { + "width" : 40, + "height" : 15, + "list_height" : 10 + } + scale = 8 + + def __init__(self, name, pixels=0): + String.__init__(self, name) + if pixels: + self.factor = self.scale + else: + self.factor = 1 + + def convert(self, value, program): + if value is None: + value = self.defaults[self.name] + return [str(int(value) * self.factor)] + +class IntegerKeyword(Integer): + + "An integer keyword parameter." + + def __init__(self, keyword, name, pixels=0): + Integer.__init__(self, name, pixels) + self.keyword = keyword + + def convert(self, value, program): + if value is None: + value = self.defaults[self.name] + return [self.keyword + "=" + str(int(value) * self.factor)] + +class Boolean(String): + + "A boolean parameter." + + values = { + "kdialog" : ["off", "on"], + "zenity" : ["FALSE", "TRUE"], + "Xdialog" : ["off", "on"] + } + + def convert(self, value, program): + values = self.values[program] + if value: + return [values[1]] + else: + return [values[0]] + +class MenuItemList(String): + + "A menu item list parameter." + + def convert(self, value, program): + l = [] + for v in value: + l.append(v.value) + l.append(v.text) + return l + +class ListItemList(String): + + "A radiolist/checklist item list parameter." + + def __init__(self, name, status_first=0): + String.__init__(self, name) + self.status_first = status_first + + def convert(self, value, program): + l = [] + for v in value: + boolean = Boolean(None) + status = boolean.convert(v.status, program) + if self.status_first: + l += status + l.append(v.value) + l.append(v.text) + if not self.status_first: + l += status + return l + +# Dialogue argument values. + +class MenuItem: + + "A menu item which can also be used with radiolists and checklists." + + def __init__(self, value, text, status=0): + self.value = value + self.text = text + self.status = status + +# Dialogue classes. + +class Dialogue: + + commands = { + "KDE" : "kdialog", + "GNOME" : "zenity", + "XFCE" : "zenity", # NOTE: Based on observations with Xubuntu. + "X11" : "Xdialog" + } + + def open(self, desktop=None): + + """ + Open a dialogue box (dialog) using a program appropriate to the desktop + environment in use. + + If the optional 'desktop' parameter is specified then attempt to use + that particular desktop environment's mechanisms to open the dialog + instead of guessing or detecting which environment is being used. + + Suggested values for 'desktop' are "standard", "KDE", "GNOME", + "Mac OS X", "Windows". + + The result of the dialogue interaction may be a string indicating user + input (for Input, Password, Menu, Pulldown), a list of strings + indicating selections of one or more items (for RadioList, CheckList), + or a value indicating true or false (for Question, Warning, Message, + Error). + + Where a string value may be expected but no choice is made, an empty + string may be returned. Similarly, where a list of values is expected + but no choice is made, an empty list may be returned. + """ + + # Decide on the desktop environment in use. + + desktop_in_use = use_desktop(desktop) + + # Get the program. + + try: + program = self.commands[desktop_in_use] + except KeyError: + raise OSError("Desktop '%s' not supported (no known dialogue box command could be suggested)" % desktop_in_use) + + # The handler is one of the functions communicating with the subprocess. + # Some handlers return boolean values, others strings. + + handler, options = self.info[program] + + cmd = [program] + for option in options: + if isinstance(option, str): + cmd.append(option) + else: + value = getattr(self, option.name, None) + cmd += option.convert(value, program) + + return handler(cmd, 0) + +class Simple(Dialogue): + def __init__(self, text, width=None, height=None): + self.text = text + self.width = width + self.height = height + +class Question(Simple): + + """ + A dialogue asking a question and showing response buttons. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "question" + info = { + "kdialog" : (_status, ["--yesno", String("text")]), + "zenity" : (_status, ["--question", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), + } + +class Warning(Simple): + + """ + A dialogue asking a question and showing response buttons. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "warning" + info = { + "kdialog" : (_status, ["--warningyesno", String("text")]), + "zenity" : (_status, ["--warning", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), + } + +class Message(Simple): + + """ + A message dialogue. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "message" + info = { + "kdialog" : (_status, ["--msgbox", String("text")]), + "zenity" : (_status, ["--info", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), + } + +class Error(Simple): + + """ + An error dialogue. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "error" + info = { + "kdialog" : (_status, ["--error", String("text")]), + "zenity" : (_status, ["--error", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), + } + +class Menu(Simple): + + """ + A menu of options, one of which being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects) + Response: a value corresponding to the chosen item + """ + + name = "menu" + info = { + "kdialog" : (_readvalue(_readfrom), ["--menu", String("text"), MenuItemList("items")]), + "zenity" : (_readvalue(_readfrom), ["--list", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + MenuItemList("items")] + ), + "Xdialog" : (_readvalue(_readfrom), ["--stdout", "--menubox", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), MenuItemList("items")] + ), + } + item = MenuItem + number_of_titles = 2 + + def __init__(self, text, titles, items=None, width=None, height=None, list_height=None): + + """ + Initialise a menu with the given heading 'text', column 'titles', and + optional 'items' (which may be added later), 'width' (in characters), + 'height' (in characters) and 'list_height' (in items). + """ + + Simple.__init__(self, text, width, height) + self.titles = ([""] * self.number_of_titles + titles)[-self.number_of_titles:] + self.items = items or [] + self.list_height = list_height + + def add(self, *args, **kw): + + """ + Add an item, passing the given arguments to the appropriate item class. + """ + + self.items.append(self.item(*args, **kw)) + +class RadioList(Menu): + + """ + A list of radio buttons, one of which being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects), titles + Response: a list of values corresponding to chosen items (since some + programs, eg. zenity, appear to support multiple default + selections) + """ + + name = "radiolist" + info = { + "kdialog" : (_readvalues_kdialog(_readfrom), ["--radiolist", String("text"), ListItemList("items")]), + "zenity" : (_readvalues_zenity(_readfrom), + ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + ListItemList("items", 1)] + ), + "Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--radiolist", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] + ), + } + number_of_titles = 3 + +class CheckList(Menu): + + """ + A list of checkboxes, many being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects), titles + Response: a list of values corresponding to chosen items + """ + + name = "checklist" + info = { + "kdialog" : (_readvalues_kdialog(_readfrom), ["--checklist", String("text"), ListItemList("items")]), + "zenity" : (_readvalues_zenity(_readfrom), + ["--list", "--checklist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + ListItemList("items", 1)] + ), + "Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--checklist", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] + ), + } + number_of_titles = 3 + +class Pulldown(Menu): + + """ + A pull-down menu of options, one of which being selectable. + Options: text, width (in characters), height (in characters), + items (list of values) + Response: a value corresponding to the chosen item + """ + + name = "pulldown" + info = { + "kdialog" : (_readvalue(_readfrom), ["--combobox", String("text"), Strings("items")]), + "zenity" : (_readvalue(_readfrom), + ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + StringPairs("items")] + ), + "Xdialog" : (_readvalue(_readfrom), + ["--stdout", "--combobox", String("text"), Integer("height"), Integer("width"), Strings("items")]), + } + item = str + number_of_titles = 2 + +class Input(Simple): + + """ + An input dialogue, consisting of an input field. + Options: text, input, width (in characters), height (in characters) + Response: the text entered into the dialogue by the user + """ + + name = "input" + info = { + "kdialog" : (_readinput(_readfrom), + ["--inputbox", String("text"), String("data")]), + "zenity" : (_readinput(_readfrom), + ["--entry", StringKeyword("--text", "text"), StringKeyword("--entry-text", "data")]), + "Xdialog" : (_readinput(_readfrom), + ["--stdout", "--inputbox", String("text"), Integer("height"), Integer("width"), String("data")]), + } + + def __init__(self, text, data="", width=None, height=None): + Simple.__init__(self, text, width, height) + self.data = data + +class Password(Input): + + """ + A password dialogue, consisting of a password entry field. + Options: text, width (in characters), height (in characters) + Response: the text entered into the dialogue by the user + """ + + name = "password" + info = { + "kdialog" : (_readinput(_readfrom), + ["--password", String("text")]), + "zenity" : (_readinput(_readfrom), + ["--entry", StringKeyword("--text", "text"), "--hide-text"]), + "Xdialog" : (_readinput(_readfrom), + ["--stdout", "--password", "--inputbox", String("text"), Integer("height"), Integer("width")]), + } + +class TextFile(Simple): + + """ + A text file input box. + Options: filename, text, width (in characters), height (in characters) + Response: any text returned by the dialogue program (typically an empty + string) + """ + + name = "textfile" + info = { + "kdialog" : (_readfrom, ["--textbox", String("filename"), Integer("width", pixels=1), Integer("height", pixels=1)]), + "zenity" : (_readfrom, ["--text-info", StringKeyword("--filename", "filename"), IntegerKeyword("--width", "width", pixels=1), + IntegerKeyword("--height", "height", pixels=1)] + ), + "Xdialog" : (_readfrom, ["--stdout", "--textbox", String("filename"), Integer("height"), Integer("width")]), + } + + def __init__(self, filename, text="", width=None, height=None): + Simple.__init__(self, text, width, height) + self.filename = filename + +# Available dialogues. + +available = [Question, Warning, Message, Error, Menu, CheckList, RadioList, Input, Password, Pulldown, TextFile] + +# Supported desktop environments. + +supported = list(Dialogue.commands.keys()) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/HighlightLib/desktop/windows.py b/HighlightLib/desktop/windows.py new file mode 100644 index 0000000..029ad34 --- /dev/null +++ b/HighlightLib/desktop/windows.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python + +""" +Simple desktop window enumeration for Python. + +Copyright (C) 2007, 2008, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Finding Open Windows on the Desktop +----------------------------------- + +To obtain a list of windows, use the desktop.windows.list function as follows: + +windows = desktop.windows.list() + +To obtain the root window, typically the desktop background, use the +desktop.windows.root function as follows: + +root = desktop.windows.root() + +Each window object can be inspected through a number of methods. For example: + +name = window.name() +width, height = window.size() +x, y = window.position() +child_windows = window.children() + +See the desktop.windows.Window class for more information. +""" + +from desktop import _is_x11, _get_x11_vars, _readfrom, use_desktop +import re + +# System functions. + +def _xwininfo(identifier, action): + if identifier is None: + args = "-root" + else: + args = "-id " + identifier + + s = _readfrom(_get_x11_vars() + "xwininfo %s -%s" % (args, action), shell=1) + + # Return a mapping of keys to values for the "stats" action. + + if action == "stats": + d = {} + for line in s.split("\n"): + fields = line.split(":") + if len(fields) < 2: + continue + key, value = fields[0].strip(), ":".join(fields[1:]).strip() + d[key] = value + + return d + + # Otherwise, return the raw output. + + else: + return s + +def _get_int_properties(d, properties): + results = [] + for property in properties: + results.append(int(d[property])) + return results + +# Finder functions. + +def find_all(name): + return 1 + +def find_named(name): + return name is not None + +def find_by_name(name): + return lambda n, t=name: n == t + +# Window classes. +# NOTE: X11 is the only supported desktop so far. + +class Window: + + "A window on the desktop." + + _name_pattern = re.compile(r':\s+\(.*?\)\s+[-0-9x+]+\s+[-0-9+]+$') + _absent_names = "(has no name)", "(the root window) (has no name)" + + def __init__(self, identifier): + + "Initialise the window with the given 'identifier'." + + self.identifier = identifier + + # Finder methods (from above). + + self.find_all = find_all + self.find_named = find_named + self.find_by_name = find_by_name + + def __repr__(self): + return "Window(%r)" % self.identifier + + # Methods which deal with the underlying commands. + + def _get_handle_and_name(self, text): + fields = text.strip().split(" ") + handle = fields[0] + + # Get the "" part, stripping off the quotes. + + name = " ".join(fields[1:]) + if len(name) > 1 and name[0] == '"' and name[-1] == '"': + name = name[1:-1] + + if name in self._absent_names: + return handle, None + else: + return handle, name + + def _get_this_handle_and_name(self, line): + fields = line.split(":") + return self._get_handle_and_name(":".join(fields[1:])) + + def _get_descendant_handle_and_name(self, line): + match = self._name_pattern.search(line) + if match: + return self._get_handle_and_name(line[:match.start()].strip()) + else: + raise OSError("Window information from %r did not contain window details." % line) + + def _descendants(self, s, fn): + handles = [] + adding = 0 + for line in s.split("\n"): + if line.endswith("child:") or line.endswith("children:"): + if not adding: + adding = 1 + elif adding and line: + handle, name = self._get_descendant_handle_and_name(line) + if fn(name): + handles.append(handle) + return [Window(handle) for handle in handles] + + # Public methods. + + def children(self, all=0): + + """ + Return a list of windows which are children of this window. If the + optional 'all' parameter is set to a true value, all such windows will + be returned regardless of whether they have any name information. + """ + + s = _xwininfo(self.identifier, "children") + return self._descendants(s, all and self.find_all or self.find_named) + + def descendants(self, all=0): + + """ + Return a list of windows which are descendants of this window. If the + optional 'all' parameter is set to a true value, all such windows will + be returned regardless of whether they have any name information. + """ + + s = _xwininfo(self.identifier, "tree") + return self._descendants(s, all and self.find_all or self.find_named) + + def find(self, callable): + + """ + Return windows using the given 'callable' (returning a true or a false + value when invoked with a window name) for descendants of this window. + """ + + s = _xwininfo(self.identifier, "tree") + return self._descendants(s, callable) + + def name(self): + + "Return the name of the window." + + d = _xwininfo(self.identifier, "stats") + + # Format is 'xwininfo: Window id: "" + + return self._get_this_handle_and_name(d["xwininfo"])[1] + + def size(self): + + "Return a tuple containing the width and height of this window." + + d = _xwininfo(self.identifier, "stats") + return _get_int_properties(d, ["Width", "Height"]) + + def position(self): + + "Return a tuple containing the upper left co-ordinates of this window." + + d = _xwininfo(self.identifier, "stats") + return _get_int_properties(d, ["Absolute upper-left X", "Absolute upper-left Y"]) + + def displayed(self): + + """ + Return whether the window is displayed in some way (but not necessarily + visible on the current screen). + """ + + d = _xwininfo(self.identifier, "stats") + return d["Map State"] != "IsUnviewable" + + def visible(self): + + "Return whether the window is displayed and visible." + + d = _xwininfo(self.identifier, "stats") + return d["Map State"] == "IsViewable" + +def list(desktop=None): + + """ + Return a list of windows for the current desktop. If the optional 'desktop' + parameter is specified then attempt to use that particular desktop + environment's mechanisms to look for windows. + """ + + root_window = root(desktop) + window_list = [window for window in root_window.descendants() if window.displayed()] + window_list.insert(0, root_window) + return window_list + +def root(desktop=None): + + """ + Return the root window for the current desktop. If the optional 'desktop' + parameter is specified then attempt to use that particular desktop + environment's mechanisms to look for windows. + """ + + # NOTE: The desktop parameter is currently ignored and X11 is tested for + # NOTE: directly. + + if _is_x11(): + return Window(None) + else: + raise OSError("Desktop '%s' not supported" % use_desktop(desktop)) + +def find(callable, desktop=None): + + """ + Find and return windows using the given 'callable' for the current desktop. + If the optional 'desktop' parameter is specified then attempt to use that + particular desktop environment's mechanisms to look for windows. + """ + + return root(desktop).find(callable) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/HighlightLib/pygments/__init__.py b/HighlightLib/pygments/__init__.py new file mode 100644 index 0000000..1ce34b2 --- /dev/null +++ b/HighlightLib/pygments/__init__.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +""" + Pygments + ~~~~~~~~ + + Pygments is a syntax highlighting package written in Python. + + It is a generic syntax highlighter for general use in all kinds of software + such as forum systems, wikis or other applications that need to prettify + source code. Highlights are: + + * a wide range of common languages and markup formats is supported + * special attention is paid to details, increasing quality by a fair amount + * support for new languages and formats are added easily + * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image + formats that PIL supports, and ANSI sequences + * it is usable as a command-line tool and as a library + * ... and it highlights even Brainfuck! + + The `Pygments tip`_ is installable with ``easy_install Pygments==dev``. + + .. _Pygments tip: + http://bitbucket.org/birkenfeld/pygments-main/get/tip.zip#egg=Pygments-dev + + :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +__version__ = '2.1a0' +__docformat__ = 'restructuredtext' + +__all__ = ['lex', 'format', 'highlight'] + + +import sys + +from pygments.util import StringIO, BytesIO + + +def lex(code, lexer): + """ + Lex ``code`` with ``lexer`` and return an iterable of tokens. + """ + try: + return lexer.get_tokens(code) + except TypeError as err: + if isinstance(err.args[0], str) and \ + ('unbound method get_tokens' in err.args[0] or + 'missing 1 required positional argument' in err.args[0]): + raise TypeError('lex() argument must be a lexer instance, ' + 'not a class') + raise + + +def format(tokens, formatter, outfile=None): + """ + Format a tokenlist ``tokens`` with the formatter ``formatter``. + + If ``outfile`` is given and a valid file object (an object + with a ``write`` method), the result will be written to it, otherwise + it is returned as a string. + """ + try: + if not outfile: + realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO() + formatter.format(tokens, realoutfile) + return realoutfile.getvalue() + else: + formatter.format(tokens, outfile) + except TypeError as err: + if isinstance(err.args[0], str) and \ + ('unbound method format' in err.args[0] or + 'missing 1 required positional argument' in err.args[0]): + raise TypeError('format() argument must be a formatter instance, ' + 'not a class') + raise + + +def highlight(code, lexer, formatter, outfile=None): + """ + Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``. + + If ``outfile`` is given and a valid file object (an object + with a ``write`` method), the result will be written to it, otherwise + it is returned as a string. + """ + return format(lex(code, lexer), formatter, outfile) + + +if __name__ == '__main__': # pragma: no cover + from pygments.cmdline import main + sys.exit(main(sys.argv)) diff --git a/HighlightLib/pygments/cmdline.py b/HighlightLib/pygments/cmdline.py new file mode 100644 index 0000000..f5ea565 --- /dev/null +++ b/HighlightLib/pygments/cmdline.py @@ -0,0 +1,528 @@ +# -*- coding: utf-8 -*- +""" + pygments.cmdline + ~~~~~~~~~~~~~~~~ + + Command line interface. + + :copyright: Copyright 2006-2015 by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +from __future__ import print_function + +import sys +import getopt +from textwrap import dedent + +from pygments import __version__, highlight +from pygments.util import ClassNotFound, OptionError, docstring_headline, \ + guess_decode, guess_decode_from_terminal, terminal_encoding +from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \ + get_lexer_for_filename, find_lexer_class_for_filename, TextLexer +from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter +from pygments.formatters import get_all_formatters, get_formatter_by_name, \ + get_formatter_for_filename, find_formatter_class, \ + TerminalFormatter # pylint:disable-msg=E0611 +from pygments.filters import get_all_filters, find_filter_class +from pygments.styles import get_all_styles, get_style_by_name + + +USAGE = """\ +Usage: %s [-l | -g] [-F [:]] [-f ] + [-O ] [-P ] [-s] [-v] [-o ] [] + + %s -S + + +

%(title)s

+ +''' + +DOC_HEADER_EXTERNALCSS = '''\ + + + + + %(title)s + + + + +

%(title)s

+ +''' + +DOC_FOOTER = '''\ + + +''' + + +class HtmlFormatter(Formatter): + r""" + Format tokens as HTML 4 ```` tags within a ``
`` tag, wrapped
+    in a ``
`` tag. The ``
``'s CSS class can be set by the `cssclass` + option. + + If the `linenos` option is set to ``"table"``, the ``
`` is
+    additionally wrapped inside a ```` which has one row and two
+    cells: one containing the line numbers and one containing the code.
+    Example:
+
+    .. sourcecode:: html
+
+        
+
+ + +
+
1
+            2
+
+
def foo(bar):
+              pass
+            
+
+ + (whitespace added to improve clarity). + + Wrapping can be disabled using the `nowrap` option. + + A list of lines can be specified using the `hl_lines` option to make these + lines highlighted (as of Pygments 0.11). + + With the `full` option, a complete HTML 4 document is output, including + the style definitions inside a ``