feat(SublimeText2.UtilPackages): cache packages
This commit is contained in:
@@ -0,0 +1,480 @@
|
||||
# coding=utf8
|
||||
import sublime
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
|
||||
from SideBarProject import SideBarProject
|
||||
|
||||
try:
|
||||
import desktop
|
||||
except:
|
||||
pass
|
||||
|
||||
class Object():
|
||||
pass
|
||||
|
||||
def expand_vars(path):
|
||||
for k, v in os.environ.iteritems():
|
||||
try:
|
||||
# dirty hack, this should be autofixed in python3
|
||||
k = unicode(k.encode('utf8'))
|
||||
v = unicode(v.encode('utf8'))
|
||||
path = path.replace(u'%'+k+'%', v).replace(u'%'+k.lower()+'%', v)
|
||||
except:
|
||||
pass
|
||||
return path
|
||||
|
||||
class SideBarItem:
|
||||
|
||||
def __init__(self, path, is_directory):
|
||||
self._path = path
|
||||
self._is_directory = is_directory
|
||||
|
||||
def path(self, path = ''):
|
||||
if path == '':
|
||||
return self._path
|
||||
else:
|
||||
self._path = path
|
||||
self._is_directory = os.path.isdir(path)
|
||||
return path
|
||||
|
||||
def pathSystem(self):
|
||||
import sys
|
||||
return self.path().encode(sys.getfilesystemencoding())
|
||||
|
||||
def pathWithoutProject(self):
|
||||
path = self.path()
|
||||
for directory in SideBarProject().getDirectories():
|
||||
path = path.replace(directory, '', 1)
|
||||
return path.replace('\\', '/')
|
||||
|
||||
def pathProject(self):
|
||||
path = self.path()
|
||||
for directory in SideBarProject().getDirectories():
|
||||
path2 = path.replace(directory, '', 1)
|
||||
if path2 != path:
|
||||
return directory
|
||||
return False
|
||||
|
||||
def projectURL(self, type):
|
||||
filename = os.path.normpath(os.path.join(sublime.packages_path(), '..', 'Settings', 'SideBarEnhancements.json'))
|
||||
if os.path.lexists(filename):
|
||||
#try:
|
||||
import json
|
||||
data = file(filename, 'r').read()
|
||||
data = data.replace('\t', ' ').replace('\\', '/').replace('\\', '/').replace('//', '/').replace('//', '/').replace('http:/', 'http://').replace('https:/', 'https://')
|
||||
data = json.loads(data, strict=False)
|
||||
|
||||
for path in data.keys():
|
||||
path2 = expand_vars(path)
|
||||
print '-------------------------------------------------------'
|
||||
print 'searching:'
|
||||
path2 = path2.replace('\\', '/').replace('\\', '/').replace('//', '/').replace('//', '/')
|
||||
print path2
|
||||
print 'in:'
|
||||
path3 = self.path().replace('\\', '/').replace('\\', '/').replace('//', '/').replace('//', '/')
|
||||
print path3
|
||||
print '-------------------------------------------------------'
|
||||
path4 = re.sub(re.compile("^"+re.escape(path2), re.IGNORECASE), '', path3);
|
||||
print path4
|
||||
if path4 != path3:
|
||||
url = data[path][type]
|
||||
if url:
|
||||
if url[-1:] != '/':
|
||||
url = url+'/'
|
||||
import urllib
|
||||
return url+(re.sub("^/", '', urllib.quote(path4.encode('utf-8'))));
|
||||
|
||||
#except:
|
||||
# return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def isUnderCurrentProject(self):
|
||||
path = self.path()
|
||||
path2 = self.path()
|
||||
for directory in SideBarProject().getDirectories():
|
||||
path2 = path2.replace(directory, '', 1)
|
||||
return path != path2
|
||||
|
||||
def pathRelativeFromProject(self):
|
||||
return re.sub('^/+', '', self.pathWithoutProject())
|
||||
|
||||
def pathRelativeFromProjectEncoded(self):
|
||||
import urllib
|
||||
return urllib.quote(self.pathRelativeFromProject().encode('utf-8'))
|
||||
|
||||
def pathRelativeFromView(self):
|
||||
return os.path.relpath(self.path(), os.path.dirname(sublime.active_window().active_view().file_name())).replace('\\', '/')
|
||||
|
||||
def pathRelativeFromViewEncoded(self):
|
||||
import urllib
|
||||
return urllib.quote(os.path.relpath(self.path(), os.path.dirname(sublime.active_window().active_view().file_name())).replace('\\', '/').encode('utf-8'))
|
||||
|
||||
def pathAbsoluteFromProject(self):
|
||||
return self.pathWithoutProject()
|
||||
|
||||
def pathAbsoluteFromProjectEncoded(self):
|
||||
import urllib
|
||||
return urllib.quote(self.pathAbsoluteFromProject().encode('utf-8'))
|
||||
|
||||
def uri(self):
|
||||
import urllib
|
||||
return 'file:'+urllib.pathname2url(self.path().encode('utf-8'));
|
||||
|
||||
def join(self, name):
|
||||
return os.path.join(self.path(), name)
|
||||
|
||||
def dirname(self):
|
||||
branch, leaf = os.path.split(self.path())
|
||||
return branch;
|
||||
|
||||
def forCwdSystemPath(self):
|
||||
if self.isDirectory():
|
||||
return self.pathSystem()
|
||||
else:
|
||||
return self.dirnameSystem()
|
||||
|
||||
def forCwdSystemName(self):
|
||||
if self.isDirectory():
|
||||
return '.'
|
||||
else:
|
||||
path = self.pathSystem()
|
||||
branch = self.dirnameSystem()
|
||||
leaf = path.replace(branch, '', 1).replace('\\', '').replace('/', '')
|
||||
return leaf
|
||||
|
||||
def forCwdSystemPathRelativeFrom(self, relativeFrom):
|
||||
relative = SideBarItem(relativeFrom, os.path.isdir(relativeFrom))
|
||||
path = self.pathSystem().replace(relative.pathSystem(), '', 1).replace('\\', '/')
|
||||
if path == '':
|
||||
return '.'
|
||||
else:
|
||||
return re.sub('^/+', '', path)
|
||||
|
||||
def forCwdSystemPathRelativeFromRecursive(self, relativeFrom):
|
||||
relative = SideBarItem(relativeFrom, os.path.isdir(relativeFrom))
|
||||
path = self.pathSystem().replace(relative.pathSystem(), '', 1).replace('\\', '/')
|
||||
if path == '':
|
||||
return '.'
|
||||
else:
|
||||
if self.isDirectory():
|
||||
return re.sub('^/+', '', path)+'/'
|
||||
else:
|
||||
return re.sub('^/+', '', path)
|
||||
|
||||
def dirnameSystem(self):
|
||||
import sys
|
||||
return self.dirname().encode(sys.getfilesystemencoding())
|
||||
|
||||
def dirnameCreate(self):
|
||||
try:
|
||||
os.makedirs(self.dirname())
|
||||
except:
|
||||
pass
|
||||
|
||||
def name(self):
|
||||
branch, leaf = os.path.split(self.path())
|
||||
return leaf;
|
||||
|
||||
def nameSystem(self):
|
||||
import sys
|
||||
return self.name().encode(sys.getfilesystemencoding())
|
||||
|
||||
def nameEncoded(self):
|
||||
import urllib
|
||||
return urllib.quote(self.name().encode('utf-8'));
|
||||
|
||||
def namePretty(self):
|
||||
return self.name().replace(self.extension(), '').replace('-', ' ').replace('_', ' ').strip();
|
||||
|
||||
def open(self):
|
||||
if sublime.platform() == 'osx':
|
||||
import subprocess
|
||||
subprocess.Popen(['open', self.nameSystem()], cwd=self.dirnameSystem())
|
||||
elif sublime.platform() == 'windows':
|
||||
import subprocess
|
||||
subprocess.Popen([self.nameSystem()], cwd=self.dirnameSystem(), shell=True)
|
||||
else:
|
||||
desktop.open(self.path())
|
||||
|
||||
def edit(self):
|
||||
return sublime.active_window().open_file(self.path())
|
||||
|
||||
def isDirectory(self):
|
||||
return self._is_directory
|
||||
|
||||
def isFile(self):
|
||||
return self.isDirectory() == False
|
||||
|
||||
def contentUTF8(self):
|
||||
import codecs
|
||||
return codecs.open(self.path(), 'r', 'utf-8').read()
|
||||
|
||||
def contentBinary(self):
|
||||
return file(self.path(), "rb").read()
|
||||
|
||||
def contentBase64(self):
|
||||
return 'data:'+self.mime()+';base64,'+(file(self.path(), "rb").read().encode("base64").replace('\n', ''))
|
||||
|
||||
def reveal(self):
|
||||
sublime.active_window().run_command("open_dir", {"dir": self.dirname(), "file": self.name()} )
|
||||
|
||||
def write(self, content):
|
||||
file(self.path(), 'w+').write(content)
|
||||
|
||||
def mime(self):
|
||||
import mimetypes
|
||||
return mimetypes.guess_type(self.path())[0] or 'application/octet-stream'
|
||||
|
||||
def extension(self):
|
||||
return os.path.splitext('name'+self.name())[1].lower()
|
||||
|
||||
def exists(self):
|
||||
return os.path.isdir(self.path()) or os.path.isfile(self.path())
|
||||
|
||||
def create(self):
|
||||
if self.isDirectory():
|
||||
self.dirnameCreate()
|
||||
os.makedirs(self.path())
|
||||
else:
|
||||
self.dirnameCreate()
|
||||
self.write('')
|
||||
|
||||
def copy(self, location, replace = False):
|
||||
location = SideBarItem(location, os.path.isdir(location));
|
||||
if location.exists() and replace == False:
|
||||
return False
|
||||
elif location.exists() and location.isFile():
|
||||
os.remove(location.path())
|
||||
|
||||
location.dirnameCreate();
|
||||
if self.isDirectory():
|
||||
if location.exists():
|
||||
self.copy_recursive(self.path(), location.path())
|
||||
else:
|
||||
shutil.copytree(self.path(), location.path())
|
||||
else:
|
||||
shutil.copy2(self.path(), location.path())
|
||||
return True
|
||||
|
||||
def copy_recursive(self, _from, _to):
|
||||
|
||||
if os.path.isfile(_from) or os.path.islink(_from):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(_to));
|
||||
except:
|
||||
pass
|
||||
if os.path.exists(_to):
|
||||
os.remove(_to)
|
||||
shutil.copy2(_from, _to)
|
||||
else:
|
||||
try:
|
||||
os.makedirs(_to);
|
||||
except:
|
||||
pass
|
||||
for content in os.listdir(_from):
|
||||
__from = os.path.join(_from, content)
|
||||
__to = os.path.join(_to, content)
|
||||
self.copy_recursive(__from, __to)
|
||||
|
||||
def move(self, location, replace = False):
|
||||
location = SideBarItem(location, os.path.isdir(location));
|
||||
if location.exists() and replace == False:
|
||||
if self.path().lower() == location.path().lower():
|
||||
pass
|
||||
else:
|
||||
return False
|
||||
elif location.exists() and location.isFile():
|
||||
os.remove(location.path())
|
||||
|
||||
if self.path().lower() == location.path().lower():
|
||||
location.dirnameCreate();
|
||||
os.rename(self.path(), location.path()+'.sublime-temp')
|
||||
os.rename(location.path()+'.sublime-temp', location.path())
|
||||
self._move_moveViews(self.path(), location.path())
|
||||
else:
|
||||
location.dirnameCreate();
|
||||
if location.exists():
|
||||
self.move_recursive(self.path(), location.path())
|
||||
else:
|
||||
os.rename(self.path(), location.path())
|
||||
self._move_moveViews(self.path(), location.path())
|
||||
return True
|
||||
|
||||
def move_recursive(self, _from, _to):
|
||||
if os.path.isfile(_from) or os.path.islink(_from):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(_to));
|
||||
except:
|
||||
pass
|
||||
if os.path.exists(_to):
|
||||
os.remove(_to)
|
||||
os.rename(_from, _to)
|
||||
else:
|
||||
try:
|
||||
os.makedirs(_to);
|
||||
except:
|
||||
pass
|
||||
for content in os.listdir(_from):
|
||||
__from = os.path.join(_from, content)
|
||||
__to = os.path.join(_to, content)
|
||||
self.move_recursive(__from, __to)
|
||||
os.rmdir(_from)
|
||||
|
||||
def _move_moveViews(self, old, location):
|
||||
for window in sublime.windows():
|
||||
active_view = window.active_view()
|
||||
views = []
|
||||
for view in window.views():
|
||||
if view.file_name():
|
||||
views.append(view)
|
||||
views.reverse();
|
||||
for view in views:
|
||||
if old == view.file_name():
|
||||
active_view = self._move_moveView(window, view, location, active_view)
|
||||
elif view.file_name().find(old+'\\') == 0:
|
||||
active_view = self._move_moveView(window, view, view.file_name().replace(old+'\\', location+'\\', 1), active_view)
|
||||
elif view.file_name().find(old+'/') == 0:
|
||||
active_view = self._move_moveView(window, view, view.file_name().replace(old+'/', location+'/', 1), active_view)
|
||||
|
||||
def _move_moveView(self, window, view, location, active_view):
|
||||
if active_view == view:
|
||||
is_active_view = True
|
||||
else:
|
||||
is_active_view = False
|
||||
|
||||
options = Object()
|
||||
|
||||
options.scroll = view.viewport_position()
|
||||
|
||||
options.selections = [[item.a, item.b] for item in view.sel()]
|
||||
|
||||
options.marks = [[item.a, item.b] for item in view.get_regions("mark")]
|
||||
|
||||
options.bookmarks = [[item.a, item.b] for item in view.get_regions("bookmarks")]
|
||||
|
||||
if int(sublime.version()) >= 2167:
|
||||
options.folds = [[item.a, item.b] for item in view.folded_regions()]
|
||||
else:
|
||||
options.folds = [[item.a, item.b] for item in view.unfold(sublime.Region(0, view.size()))]
|
||||
|
||||
options.syntax = view.settings().get('syntax')
|
||||
|
||||
try:
|
||||
_window = window or view.window() or sublime.active_window()
|
||||
options.position = _window.get_view_index(view)
|
||||
except:
|
||||
options.position = False
|
||||
|
||||
window.focus_view(view)
|
||||
if view.is_dirty():
|
||||
options.content = view.substr(sublime.Region(0, view.size()))
|
||||
view.window().run_command('revert')
|
||||
else:
|
||||
options.content = False
|
||||
|
||||
_view = view
|
||||
view = window.open_file(location)
|
||||
window.focus_view(_view)
|
||||
window.run_command('close')
|
||||
|
||||
sublime.set_timeout(lambda: self._move_restoreView(view, options, window), 200)
|
||||
|
||||
if is_active_view:
|
||||
window.focus_view(view)
|
||||
return view
|
||||
else:
|
||||
window.focus_view(active_view)
|
||||
return active_view
|
||||
|
||||
def _move_restoreView(self, view, options, window):
|
||||
if view.is_loading():
|
||||
sublime.set_timeout(lambda: self._move_restoreView(view, options, window), 100)
|
||||
else:
|
||||
if options.content != False:
|
||||
edit = view.begin_edit()
|
||||
view.replace(edit, sublime.Region(0, view.size()), options.content);
|
||||
view.sel().clear()
|
||||
view.sel().add(sublime.Region(0))
|
||||
view.end_edit(edit)
|
||||
|
||||
if options.position != False:
|
||||
try:
|
||||
_window = window or view.window() or sublime.active_window()
|
||||
group, index = options.position
|
||||
_window.set_view_index(view, group, index)
|
||||
except:
|
||||
pass
|
||||
|
||||
if options.syntax:
|
||||
view.settings().set('syntax', options.syntax);
|
||||
|
||||
for r in options.folds:
|
||||
view.fold(sublime.Region(r[0], r[1]))
|
||||
|
||||
view.sel().clear()
|
||||
for r in options.selections:
|
||||
view.sel().add(sublime.Region(r[0], r[1]))
|
||||
|
||||
rs = []
|
||||
for r in options.marks:
|
||||
rs.append(sublime.Region(r[0], r[1]))
|
||||
if len(rs):
|
||||
view.add_regions("mark", rs, "mark", "dot", sublime.HIDDEN | sublime.PERSISTENT)
|
||||
|
||||
rs = []
|
||||
for r in options.bookmarks:
|
||||
rs.append(sublime.Region(r[0], r[1]))
|
||||
if len(rs):
|
||||
view.add_regions("bookmarks", rs, "bookmarks", "bookmark", sublime.HIDDEN | sublime.PERSISTENT)
|
||||
|
||||
view.set_viewport_position(options.scroll, False)
|
||||
|
||||
def close_associated_buffers(self):
|
||||
path = self.path()
|
||||
closed_items = []
|
||||
for window in sublime.windows():
|
||||
active_view = window.active_view()
|
||||
views = []
|
||||
for view in window.views():
|
||||
if view.file_name():
|
||||
views.append(view)
|
||||
views.reverse();
|
||||
for view in views:
|
||||
if path == view.file_name():
|
||||
if view.window():
|
||||
closed_items.append([view.file_name(), view.window(), view.window().get_view_index(view)])
|
||||
if len(window.views()) == 1:
|
||||
window.new_file()
|
||||
window.focus_view(view)
|
||||
window.run_command('revert')
|
||||
window.run_command('close')
|
||||
elif view.file_name().find(path+'\\') == 0:
|
||||
if view.window():
|
||||
closed_items.append([view.file_name(), view.window(), view.window().get_view_index(view)])
|
||||
if len(window.views()) == 1:
|
||||
window.new_file()
|
||||
window.focus_view(view)
|
||||
window.run_command('revert')
|
||||
window.run_command('close')
|
||||
elif view.file_name().find(path+'/') == 0:
|
||||
if view.window():
|
||||
closed_items.append([view.file_name(), view.window(), view.window().get_view_index(view)])
|
||||
if len(window.views()) == 1:
|
||||
window.new_file()
|
||||
window.focus_view(view)
|
||||
window.run_command('revert')
|
||||
window.run_command('close')
|
||||
|
||||
# try to repaint
|
||||
try:
|
||||
window.focus_view(active_view)
|
||||
window.focus_view(window.active_view())
|
||||
except:
|
||||
try:
|
||||
window.focus_view(window.active_view())
|
||||
except:
|
||||
pass
|
||||
return closed_items
|
@@ -0,0 +1,119 @@
|
||||
import sublime
|
||||
import re
|
||||
import os
|
||||
|
||||
class SideBarProject:
|
||||
|
||||
def getDirectories(self):
|
||||
return sublime.active_window().folders()
|
||||
|
||||
def hasOpenedProject(self):
|
||||
return self.getProjectFile() != None
|
||||
|
||||
def getDirectoryFromPath(self, path):
|
||||
for directory in self.getDirectories():
|
||||
maybe_path = path.replace(directory, '', 1)
|
||||
if maybe_path != path:
|
||||
return directory
|
||||
|
||||
def getProjectFile(self):
|
||||
if not self.getDirectories():
|
||||
return None
|
||||
import json
|
||||
data = file(os.path.normpath(os.path.join(sublime.packages_path(), '..', 'Settings', 'Session.sublime_session')), 'r').read()
|
||||
data = data.replace('\t', ' ')
|
||||
data = json.loads(data, strict=False)
|
||||
projects = data['workspaces']['recent_workspaces']
|
||||
|
||||
if os.path.lexists(os.path.join(sublime.packages_path(), '..', 'Settings', 'Auto Save Session.sublime_session')):
|
||||
data = file(os.path.normpath(os.path.join(sublime.packages_path(), '..', 'Settings', 'Auto Save Session.sublime_session')), 'r').read()
|
||||
data = data.replace('\t', ' ')
|
||||
data = json.loads(data, strict=False)
|
||||
if 'workspaces' in data and 'recent_workspaces' in data['workspaces'] and data['workspaces']['recent_workspaces']:
|
||||
projects += data['workspaces']['recent_workspaces']
|
||||
projects = list(set(projects))
|
||||
for project_file in projects:
|
||||
project_file = re.sub(r'^/([^/])/', '\\1:/', project_file);
|
||||
project_json = json.loads(file(project_file, 'r').read(), strict=False)
|
||||
if 'folders' in project_json:
|
||||
folders = project_json['folders']
|
||||
found_all = True
|
||||
for directory in self.getDirectories():
|
||||
found = False
|
||||
for folder in folders:
|
||||
folder_path = re.sub(r'^/([^/])/', '\\1:/', folder['path']);
|
||||
if folder_path == directory.replace('\\', '/'):
|
||||
found = True
|
||||
break;
|
||||
if found == False:
|
||||
found_all = False
|
||||
break;
|
||||
if found_all:
|
||||
return project_file
|
||||
return None
|
||||
|
||||
def getProjectJson(self):
|
||||
if not self.hasOpenedProject():
|
||||
return None
|
||||
import json
|
||||
return json.loads(file(self.getProjectFile(), 'r').read(), strict=False)
|
||||
|
||||
def excludeDirectory(self, path):
|
||||
import json
|
||||
project_file = self.getProjectFile();
|
||||
project = self.getProjectJson()
|
||||
|
||||
path = re.sub(r'^([^/])\:/', '/\\1/', path.replace('\\', '/'))
|
||||
|
||||
for folder in project['folders']:
|
||||
if path.find(folder['path']) == 0:
|
||||
try:
|
||||
folder['folder_exclude_patterns'].append(re.sub(r'/+$', '', path.replace(folder['path']+'/', '', 1)))
|
||||
except:
|
||||
folder['folder_exclude_patterns'] = [re.sub(r'/+$', '', path.replace(folder['path']+'/', '', 1))]
|
||||
file(project_file, 'w+').write(json.dumps(project, indent=1))
|
||||
return
|
||||
|
||||
def excludeFile(self, path):
|
||||
import json
|
||||
project_file = self.getProjectFile();
|
||||
project = self.getProjectJson()
|
||||
|
||||
path = re.sub(r'^([^/])\:/', '/\\1/', path.replace('\\', '/'))
|
||||
|
||||
for folder in project['folders']:
|
||||
if path.find(folder['path']) == 0:
|
||||
try:
|
||||
folder['file_exclude_patterns'].append(path.replace(folder['path']+'/', '', 1))
|
||||
except:
|
||||
folder['file_exclude_patterns'] = [path.replace(folder['path']+'/', '', 1)]
|
||||
file(project_file, 'w+').write(json.dumps(project, indent=1))
|
||||
return
|
||||
|
||||
def rootAdd(self, path):
|
||||
import json
|
||||
project_file = self.getProjectFile();
|
||||
project = self.getProjectJson()
|
||||
|
||||
path = re.sub(r'^([^/])\:/', '/\\1/', path.replace('\\', '/'))
|
||||
project['folders'].append({'path':path});
|
||||
|
||||
file(project_file, 'w+').write(json.dumps(project, indent=1))
|
||||
|
||||
def refresh(self):
|
||||
try:
|
||||
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 200);
|
||||
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 600);
|
||||
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 1300);
|
||||
sublime.set_timeout(lambda:sublime.active_window().run_command('refresh_folder_list'), 2300);
|
||||
except:
|
||||
pass
|
||||
|
||||
def getPreference(self, name):
|
||||
if not self.hasOpenedProject():
|
||||
return None
|
||||
project = self.getProjectJson()
|
||||
try:
|
||||
return project[name]
|
||||
except:
|
||||
return None
|
@@ -0,0 +1,186 @@
|
||||
# coding=utf8
|
||||
import sublime
|
||||
import os
|
||||
import re
|
||||
|
||||
from SideBarProject import SideBarProject
|
||||
from SideBarItem import SideBarItem
|
||||
|
||||
class SideBarSelection:
|
||||
|
||||
def __init__(self, paths = []):
|
||||
|
||||
if len(paths) < 1:
|
||||
try:
|
||||
path = sublime.active_window().active_view().file_name()
|
||||
if self.isNone(path):
|
||||
paths = []
|
||||
else:
|
||||
paths = [path]
|
||||
except:
|
||||
paths = []
|
||||
self._paths = paths
|
||||
self._paths.sort()
|
||||
self._obtained_selection_information_basic = False
|
||||
self._obtained_selection_information_extended = False
|
||||
|
||||
def len(self):
|
||||
return len(self._paths)
|
||||
|
||||
def hasDirectories(self):
|
||||
self._obtainSelectionInformationBasic()
|
||||
return self._has_directories
|
||||
|
||||
def hasFiles(self):
|
||||
self._obtainSelectionInformationBasic()
|
||||
return self._has_files
|
||||
|
||||
def hasOnlyDirectories(self):
|
||||
self._obtainSelectionInformationBasic()
|
||||
return self._only_directories
|
||||
|
||||
def hasOnlyFiles(self):
|
||||
self._obtainSelectionInformationBasic()
|
||||
return self._only_files
|
||||
|
||||
def hasProjectDirectories(self):
|
||||
if self.hasDirectories():
|
||||
project_directories = SideBarProject().getDirectories()
|
||||
for item in self.getSelectedDirectories():
|
||||
if item.path() in project_directories:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def hasItemsUnderProject(self):
|
||||
for item in self.getSelectedItems():
|
||||
if item.isUnderCurrentProject():
|
||||
return True
|
||||
return False
|
||||
|
||||
def hasImages(self):
|
||||
return self.hasFilesWithExtension('gif|jpg|jpeg|png')
|
||||
|
||||
def hasFilesWithExtension(self, extensions):
|
||||
extensions = re.compile('('+extensions+')$', re.I);
|
||||
for item in self.getSelectedFiles():
|
||||
if extensions.search(item.path()):
|
||||
return True;
|
||||
return False
|
||||
|
||||
def getSelectedItems(self):
|
||||
self._obtainSelectionInformationExtended()
|
||||
return self._files + self._directories;
|
||||
|
||||
def getSelectedItemsWithoutChildItems(self):
|
||||
self._obtainSelectionInformationExtended()
|
||||
items = []
|
||||
for item in self._items_without_containing_child_items:
|
||||
items.append(SideBarItem(item, os.path.isdir(item)))
|
||||
return items
|
||||
|
||||
def getSelectedDirectories(self):
|
||||
self._obtainSelectionInformationExtended()
|
||||
return self._directories;
|
||||
|
||||
def getSelectedFiles(self):
|
||||
self._obtainSelectionInformationExtended()
|
||||
return self._files;
|
||||
|
||||
def getSelectedDirectoriesOrDirnames(self):
|
||||
self._obtainSelectionInformationExtended()
|
||||
return self._directories_or_dirnames;
|
||||
|
||||
def getSelectedImages(self):
|
||||
return self.getSelectedFilesWithExtension('gif|jpg|jpeg|png')
|
||||
|
||||
def getSelectedFilesWithExtension(self, extensions):
|
||||
items = []
|
||||
extensions = re.compile('('+extensions+')$', re.I);
|
||||
for item in self.getSelectedFiles():
|
||||
if extensions.search(item.path()):
|
||||
items.append(item)
|
||||
return items
|
||||
|
||||
def _obtainSelectionInformationBasic(self):
|
||||
if not self._obtained_selection_information_basic:
|
||||
self._obtained_selection_information_basic = True
|
||||
|
||||
self._has_directories = False
|
||||
self._has_files = False
|
||||
self._only_directories = False
|
||||
self._only_files = False
|
||||
|
||||
for path in self._paths:
|
||||
if self._has_directories == False and os.path.isdir(path):
|
||||
self._has_directories = True
|
||||
if self._has_files == False and os.path.isdir(path) == False:
|
||||
self._has_files = True
|
||||
if self._has_files and self._has_directories:
|
||||
break
|
||||
|
||||
if self._has_files and self._has_directories:
|
||||
self._only_directories = False
|
||||
self._only_files = False
|
||||
elif self._has_files:
|
||||
self._only_files = True
|
||||
elif self._has_directories:
|
||||
self._only_directories = True
|
||||
|
||||
def _obtainSelectionInformationExtended(self):
|
||||
if not self._obtained_selection_information_extended:
|
||||
self._obtained_selection_information_extended = True
|
||||
|
||||
self._directories = []
|
||||
self._files = []
|
||||
self._directories_or_dirnames = []
|
||||
self._items_without_containing_child_items = []
|
||||
|
||||
_directories = []
|
||||
_files = []
|
||||
_directories_or_dirnames = []
|
||||
_items_without_containing_child_items = []
|
||||
|
||||
for path in self._paths:
|
||||
if os.path.isdir(path):
|
||||
item = SideBarItem(path, True)
|
||||
if item.path() not in _directories:
|
||||
_directories.append(item.path())
|
||||
self._directories.append(item)
|
||||
if item.path() not in _directories_or_dirnames:
|
||||
_directories_or_dirnames.append(item.path())
|
||||
self._directories_or_dirnames.append(item)
|
||||
_items_without_containing_child_items = self._itemsWithoutContainingChildItems(_items_without_containing_child_items, item.path())
|
||||
else:
|
||||
item = SideBarItem(path, False)
|
||||
if item.path() not in _files:
|
||||
_files.append(item.path())
|
||||
self._files.append(item)
|
||||
_items_without_containing_child_items = self._itemsWithoutContainingChildItems(_items_without_containing_child_items, item.path())
|
||||
item = SideBarItem(os.path.dirname(path), True)
|
||||
if item.path() not in _directories_or_dirnames:
|
||||
_directories_or_dirnames.append(item.path())
|
||||
self._directories_or_dirnames.append(item)
|
||||
|
||||
self._items_without_containing_child_items = _items_without_containing_child_items
|
||||
|
||||
def _itemsWithoutContainingChildItems(self, items, item):
|
||||
new_list = []
|
||||
add = True
|
||||
for i in items:
|
||||
if i.find(item+'\\') == 0 or i.find(item+'/') == 0:
|
||||
continue
|
||||
else:
|
||||
new_list.append(i)
|
||||
if (item+'\\').find(i+'\\') == 0 or (item+'/').find(i+'/') == 0:
|
||||
add = False
|
||||
if add:
|
||||
new_list.append(item)
|
||||
return new_list
|
||||
|
||||
def isNone(self, path):
|
||||
if path == None or path == '' or path == '.' or path == '..' or path == './' or path == '/' or path == '//' or path == '\\' or path == '\\\\' or path == '\\\\\\\\':
|
||||
return True
|
||||
else:
|
||||
return False
|
@@ -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 <paul@boddie.org.uk>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------
|
||||
|
||||
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 commands
|
||||
|
||||
# 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 os.environ.has_key("DISPLAY")
|
||||
|
||||
# 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 os.environ.has_key("KDE_FULL_SESSION") or \
|
||||
os.environ.has_key("KDE_MULTIHEAD"):
|
||||
return "KDE"
|
||||
elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \
|
||||
os.environ.has_key("GNOME_KEYRING_SOCKET"):
|
||||
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 os.environ.has_key("DESKTOP_LAUNCH")
|
||||
|
||||
# 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"], commands.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 = ["gnome-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 os.environ.has_key("BROWSER"):
|
||||
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
|
@@ -0,0 +1,549 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple desktop dialogue box support for Python.
|
||||
|
||||
Copyright (C) 2007, 2009 Paul Boddie <paul@boddie.org.uk>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------
|
||||
|
||||
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 = unicode
|
||||
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 = Dialogue.commands.keys()
|
||||
|
||||
# vim: tabstop=4 expandtab shiftwidth=4
|
@@ -0,0 +1,273 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Simple desktop window enumeration for Python.
|
||||
|
||||
Copyright (C) 2007, 2008, 2009 Paul Boddie <paul@boddie.org.uk>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
--------
|
||||
|
||||
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 "<name>" 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: <handle> "<name>"
|
||||
|
||||
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
|
Reference in New Issue
Block a user