feat(SublimeText2.UtilPackages): cache packages

This commit is contained in:
Iristyle
2013-04-04 08:54:47 -04:00
parent 1e6f643a1b
commit d65666cdfc
541 changed files with 26347 additions and 0 deletions

View File

@@ -0,0 +1,3 @@
*.pyc
*.cache
*.sublime-project

View File

@@ -0,0 +1,84 @@
[
{
"caption": "File: New File Relative to Current View",
"command": "side_bar_new_file"
},
{
"caption": "File: Rename",
"command": "side_bar_rename"
},
{
"caption": "File: Duplicate",
"command": "side_bar_duplicate"
},
{
"caption": "File: Reveal",
"command": "side_bar_reveal"
},
{
"caption": "File: Move",
"command": "side_bar_move"
},
{
"caption": "File: Delete",
"command": "side_bar_delete"
},
{
"caption": "Project: Edit",
"command": "side_bar_project_open_file"
},
{
"caption": "File: Copy Name",
"command": "side_bar_copy_name"
},
{
"caption": "File: Copy Name Encoded",
"command": "side_bar_copy_name_encoded"
},
{
"caption": "File: Copy Path",
"command": "side_bar_copy_path"
},
{
"caption": "File: Copy Path as URI",
"command": "side_bar_copy_path_encoded"
},
{
"caption": "File: Copy Path From Project",
"command": "side_bar_copy_path_absolute_from_project"
},
{
"caption": "File: Copy Path From Project Encoded",
"command": "side_bar_copy_path_absolute_from_project_encoded"
},
{
"caption": "File: Copy as Tag a",
"command": "side_bar_copy_tag_ahref"
},
{
"caption": "File: Copy as Tag script",
"command": "side_bar_copy_tag_script"
},
{
"caption": "File: Copy as Tag style",
"command": "side_bar_copy_tag_style"
},
{
"caption": "File: Copy URL",
"command": "side_bar_copy_url"
},
{
"caption": "File: Search Files",
"command": "side_bar_find_files_path_containing"
},
{
"caption": "File: Open In Browser - Testing Server",
"command": "side_bar_open_in_browser",
"args":{"paths":[], "type":"testing"}
},
{
"caption": "File: Open In Browser - Production Server",
"command": "side_bar_open_in_browser",
"args":{"paths":[], "type":"production"}
}
]

View File

@@ -0,0 +1,10 @@
[
{ "keys": ["f12"],
"command": "side_bar_open_in_browser" ,
"args":{"paths":[], "type":"testing"}
},
{ "keys": ["alt+f12"],
"command": "side_bar_open_in_browser",
"args":{"paths":[], "type":"production"}
}
]

View File

@@ -0,0 +1,88 @@
[
{
"caption": "Preferences",
"mnemonic": "n",
"id": "preferences",
"children":
[
{
"caption": "Package Settings",
"mnemonic": "P",
"id": "package-settings",
"children":
[
{
"caption": "Side Bar",
"children":
[
{
"command": "open_file", "args":
{
"file": "${packages}/SideBarEnhancements/Side Bar.sublime-settings"
},
"caption": "Settings Default"
},
{
"command": "open_file", "args":
{
"file": "${packages}/User/Side Bar.sublime-settings"
},
"caption": "Settings User"
},
{ "caption": "-" },
{
"command": "open_file",
"args": {
"file": "${packages}/SideBarEnhancements/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings Default"
},
{
"command": "open_file",
"args": {
"file": "${packages}/SideBarEnhancements/Default (Linux).sublime-keymap",
"platform": "Linux"
},
"caption": "Key Bindings Default"
},
{
"command": "open_file",
"args": {
"file": "${packages}/SideBarEnhancements/Default (Windows).sublime-keymap",
"platform": "Windows"
},
"caption": "Key Bindings Default"
},
{
"command": "open_file",
"args": {
"file": "${packages}/User/Default (OSX).sublime-keymap",
"platform": "OSX"
},
"caption": "Key Bindings User"
},
{
"command": "open_file",
"args": {
"file": "${packages}/User/Default (Linux).sublime-keymap",
"platform": "Linux"
},
"caption": "Key Bindings User"
},
{
"command": "open_file",
"args": {
"file": "${packages}/User/Default (Windows).sublime-keymap",
"platform": "Windows"
},
"caption": "Key Bindings User"
},
{ "caption": "-" }
]
}
]
}
]
}
]

View File

@@ -0,0 +1,116 @@
[
{"caption":"-", "id":"side-bar-start-separator"},
{ "caption": "New File…", "id": "side-bar-new-file", "command": "side_bar_new_file", "args": {"paths": []} },
{ "caption": "New Folder…", "id": "side-bar-new-directory", "command": "side_bar_new_directory", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-new-separator"},
{ "caption": "Edit", "id": "side-bar-edit", "command": "side_bar_edit", "args": {"paths": []} },
{ "caption": "Open / Run", "id": "side-bar-open", "command": "side_bar_open", "args": {"paths": []} },
{ "caption": "Open In Browser", "id": "side-bar-open-in-browser", "command": "side_bar_open_in_browser", "args": {"paths": []} },
{ "caption": "Open In New Window", "id": "side-bar-open-in-new-window", "command": "side_bar_open_in_new_window", "args": {"paths": []} },
{ "caption": "Open With Finder", "id": "side-bar-open-with-finde", "command": "side_bar_open_with_finder", "args": {"paths": []} },
{ "caption": "Open With", "id": "side-bar-files-open-with",
"children":
[
{ "caption": "-", "id": "side-bar-files-open-with-edit-application-separator"},
{ "caption": "Edit Applications…", "id": "side-bar-files-open-with-edit-applications", "command":"side_bar_files_open_with_edit_applications","args": {"paths": []} },
{ "caption": "-", "id": "side-bar-files-open-with-edit-applications-separator"}
]
},
{ "caption": "Reveal", "id": "side-bar-reveal", "command": "side_bar_reveal", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-edit-open-separator" },
{ "caption": "Find & Replace…", "id": "side-bar-find-selected", "command": "side_bar_find_in_selected", "args": {"paths": []} },
{ "caption": "Find In Project…", "id": "side-bar-find-in-project", "command": "side_bar_find_in_project", "args": {"paths": []} },
{ "caption": "Find Advanced", "id": "side-bar-find-advanced",
"children":
[
{ "caption": "In Parent Folder…", "id": "side-bar-find-parent", "command": "side_bar_find_in_parent", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-find-parent-separator"},
{ "caption": "In Project Folder…", "id": "side-bar-find-project-folder", "command": "side_bar_find_in_project_folder", "args": {"paths": []} },
{ "caption": "In Project Folders…", "id": "side-bar-find-project-folders", "command": "side_bar_find_in_project_folders"},
{ "caption": "-", "id": "side-bar-find-project-separator"},
{ "id": "side-bar-find-in-files-with-extension", "command": "side_bar_find_in_files_with_extension", "args": {"paths": []}},
{ "caption": "In Paths Containing…", "id": "side-bar-find-files-path-containing", "command": "side_bar_find_files_path_containing", "args": {"paths": []} }
]
},
{ "caption": "-", "id": "side-bar-find-separator"},
{ "caption": "Cut", "id": "side-bar-clip-cut", "command": "side_bar_cut", "args": {"paths": []} },
{ "caption": "Copy", "id": "side-bar-clip-copy", "command": "side_bar_copy", "args": {"paths": []} },
{ "caption": "Copy Name", "id": "side-bar-clip-copy-name", "command": "side_bar_copy_name", "args": {"paths": []} },
{ "caption": "Copy Path", "id": "side-bar-clip-copy-path", "command": "side_bar_copy_path_absolute_from_project_encoded", "args": {"paths": []} },
{ "caption": "Copy Dir Path", "id": "side-bar-clip-copy-dir-path", "command": "side_bar_copy_dir_path", "args": {"paths": []} },
{ "caption": "Copy as Text", "id": "side-bar-clip-copy-as",
"children":
[
{ "caption": "Relative Path From View Encoded", "id": "side-bar-clip-copy-path-relative-from-view-encoded", "command": "side_bar_copy_path_relative_from_view_encoded", "args": {"paths": []} },
{ "caption": "Relative Path From View", "id": "side-bar-clip-copy-path-relative-from-view", "command": "side_bar_copy_path_relative_from_view", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-path-relative-from-view-separator"},
{ "caption": "Relative Path From Project Encoded", "id": "side-bar-clip-copy-path-relative-from-project-encoded", "command": "side_bar_copy_path_relative_from_project_encoded", "args": {"paths": []} },
{ "caption": "Relative Path From Project", "id": "side-bar-clip-copy-path-relative-from-project", "command": "side_bar_copy_path_relative_from_project", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-path-relative-from-project-separator"},
{ "caption": "Absolute Path From Project Encoded", "id": "side-bar-clip-copy-path-absolute-from-project-encoded", "command": "side_bar_copy_path_absolute_from_project_encoded", "args": {"paths": []} },
{ "caption": "Absolute Path From Project", "id": "side-bar-clip-copy-path-absolute-from-project", "command": "side_bar_copy_path_absolute_from_project", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-path-absolute-from-project-separator"},
{ "caption": "Path as URI", "id": "side-bar-clip-copy-path-encoded", "command": "side_bar_copy_path_encoded", "args": {"paths": []} },
{ "caption": "Path", "id": "side-bar-clip-copy-path", "command": "side_bar_copy_path", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-path-separator"},
{ "caption": "Name Encoded", "id": "side-bar-clip-copy-name-encoded", "command": "side_bar_copy_name_encoded", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-name-encoded-separator"},
{ "caption": "URL", "id": "side-bar-clip-copy-url", "command": "side_bar_copy_url", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-url-separator"},
{ "caption": "Tag a", "id": "side-bar-clip-copy-tag-a", "command": "side_bar_copy_tag_ahref", "args": {"paths": []} },
{ "caption": "Tag img", "id": "side-bar-clip-copy-tag-img", "command": "side_bar_copy_tag_img", "args": {"paths": []} },
{ "caption": "Tag script", "id": "side-bar-clip-copy-tag-script", "command": "side_bar_copy_tag_script", "args": {"paths": []} },
{ "caption": "Tag style", "id": "side-bar-clip-copy-tag-style", "command": "side_bar_copy_tag_style", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-tag-separator"},
{ "caption": "Project Folders", "id": "side-bar-clip-copy-project-directories", "command": "side_bar_copy_project_directories", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-clip-copy-project-directories-separator"},
{ "caption": "Content as UTF-8", "id": "side-bar-clip-copy-content-utf8", "command": "side_bar_copy_content_utf8", "args": {"paths": []} },
{ "caption": "Content as Data URI", "id": "side-bar-clip-copy-content-base-64", "command": "side_bar_copy_content_base64", "args": {"paths": []} }
]
},
{ "caption": "Paste", "id": "side-bar-clip-paste", "command": "side_bar_paste", "args": {"paths": [], "in_parent":"False"} },
{ "caption": "Paste in Parent", "id": "side-bar-clip-paste-in-parent", "command": "side_bar_paste", "args": {"paths": [], "in_parent":"True"} },
{ "caption": "-", "id": "side-bar-clip-separator" },
{ "caption": "Duplicate…", "id": "side-bar-duplicate", "command": "side_bar_duplicate", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-duplicate-separator" },
{ "caption": "Rename…", "id": "side-bar-rename", "command": "side_bar_rename", "args": {"paths": []} },
{ "caption": "Move…", "id": "side-bar-move", "command": "side_bar_move", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-rename-move-separator" },
{ "caption": "Delete", "id": "side-bar-delete", "command": "side_bar_delete", "args": {"paths": []} },
{ "caption": "Empty", "id": "side-bar-emptry", "command": "side_bar_empty", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-delete-separator" },
{ "caption": "Refresh", "id": "side-bar-refresh", "command": "refresh_folder_list" },
{ "caption": "-", "id": "side-bar-refresh-separator" },
{ "caption": "Project", "id": "side-bar-project",
"children":
[
{ "caption": "Edit Project", "id": "side-bar-project-open-file", "command": "side_bar_project_open_file", "args": {"paths": []} },
{ "caption": "Edit Projects Preview URLs", "id": "side-bar-project-preview-url", "command": "side_bar_project_open_project_preview_urls_file", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-project-open-file-separator" },
{ "caption": "Promote as Project Folder", "id": "side-bar-project-item-add", "command": "side_bar_project_item_add", "args": {"paths": []} },
{ "caption": "Exclude From Project", "id": "side-bar-project-item-exclude", "command": "side_bar_project_item_exclude", "args": {"paths": []} },
{ "caption": "-", "id": "side-bar-project-item-separator" },
{ "caption": "Remove Folder from Project", "id": "side-bar-project-item-remove-folder", "command": "side_bar_project_item_remove_folder", "args": { "paths": []} },
{ "command": "prompt_add_folder", "caption": "Add Folder to Project…", "mnemonic": "d" }
]
},
{ "caption": "-", "id": "side-bar-end-separator" }
]

View File

@@ -0,0 +1,32 @@
{
"statusbar_modified_time" : false,
"statusbar_modified_time_format" : "%A %b %d %H:%M:%S %Y",
"statusbar_file_size" : false,
"close_affected_buffers_when_deleting_even_if_dirty" : false,
"hide_open_with_entries_when_there_are_no_applicable" : false,
"confirm_before_deleting" : true,
"new_files_relative_to_project_root": false,
"disabled_menuitem_edit": false,
"disabled_menuitem_open_run": false,
"disabled_menuitem_open_in_browser": false,
"disabled_menuitem_open_in_new_window": false,
"disabled_menuitem_find_in_project": false,
"disabled_menuitem_copy_name": false,
"disabled_menuitem_copy_path": false,
"disabled_menuitem_copy_dir_path": true,
"disabled_menuitem_paste_in_parent": false,
"disabled_menuitem_empty": true,
//if installed in a default location maybe this works.
"default_browser": "chrome", //one of this list: firefox, chrome, chromium, opera, safari
"portable_browser": "" // for example: C:/Program Files (x86)/Nightly/firefox.exe
}

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>name</key>
<string>SideBar Results</string>
<key>patterns</key>
<array>
<dict>
<key>match</key>
<string>(Type to search\:).*</string>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.function</string>
</dict>
</dict>
</dict>
<dict>
<key>match</key>
<string>([0-9]+) match.*</string>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.function</string>
</dict>
</dict>
</dict>
<dict>
<key>match</key>
<string>^(.*(\:|/).*)$</string>
<key>captures</key>
<dict>
<key>1</key>
<dict>
<key>name</key>
<string>entity.name.filename.find-in-files</string>
</dict>
</dict>
</dict>
</array>
<key>scopeName</key>
<string>text.find-in-files</string>
</dict>
</plist>

View File

@@ -0,0 +1,24 @@
import sublime, sublime_plugin
from hurry.filesize import size
from os.path import getsize
s = sublime.load_settings('Side Bar.sublime-settings')
class StatusBarFileSize(sublime_plugin.EventListener):
def on_load(self, v):
if s.get('statusbar_file_size') and v.file_name():
try:
self.show(v, size(getsize(v.file_name())))
except:
pass
def on_post_save(self, v):
if s.get('statusbar_file_size') and v.file_name():
try:
self.show(v, size(getsize(v.file_name())))
except:
pass
def show(self, v, size):
v.set_status('statusbar_file_size', size);

View File

@@ -0,0 +1,25 @@
import sublime, sublime_plugin, time
from os.path import getmtime
s = sublime.load_settings('Side Bar.sublime-settings')
class StatusBarModifiedTime(sublime_plugin.EventListener):
def on_load(self, v):
if s.get('statusbar_modified_time') and v.file_name():
try:
self.show(v, getmtime(v.file_name()))
except:
pass
def on_post_save(self, v):
if s.get('statusbar_modified_time') and v.file_name():
try:
self.show(v, getmtime(v.file_name()))
except:
pass
def show(self, v, mtime):
v.set_status('statusbar_modified_time', time.strftime(s.get('statusbar_modified_time_format'), time.localtime(mtime)));

View File

@@ -0,0 +1,4 @@
[
{ "caption": "-", "id": "folder_commands" },
{ "caption": "Remove Folder from Project", "command": "remove_folder", "args": { "dirs": []} }
]

View File

@@ -0,0 +1,11 @@
[
{ "caption": "New File", "command": "new_file_at", "args": {"dirs": []} },
{ "caption": "Rename…", "command": "rename_path", "args": {"paths": []} },
{ "caption": "Delete File", "command": "delete_file", "args": {"files": []} },
{ "caption": "Open Containing Folder…", "command": "open_containing_folder", "args": {"files": []} },
{ "caption": "-", "id": "folder_commands" },
{ "caption": "New Folder…", "command": "new_folder", "args": {"dirs": []} },
{ "caption": "Delete Folder", "command": "delete_folder", "args": {"dirs": []} },
{ "caption": "Find in Folder…", "command": "find_in_folder", "args": {"dirs": []} },
{ "caption": "-", "id": "end" }
]

View File

@@ -0,0 +1,71 @@
Metadata-Version: 1.0
Name: hurry.filesize
Version: 0.9
Summary: A simple Python library for human readable file sizes (or anything sized in bytes).
Home-page: UNKNOWN
Author: Martijn Faassen, Startifact
Author-email: faassen@startifact.com
License: ZPL 2.1
Description: hurry.filesize
==============
hurry.filesize a simple Python library that can take a number of bytes and
returns a human-readable string with the size in it, in kilobytes (K),
megabytes (M), etc.
The default system it uses is "traditional", where multipliers of 1024
increase the unit size::
>>> from hurry.filesize import size
>>> size(1024)
'1K'
An alternative, slightly more verbose system::
>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'
A verbose system::
>>> from hurry.filesize import verbose
>>> size(10, system=verbose)
'10 bytes'
>>> size(1024, system=verbose)
'1 kilobyte'
>>> size(2000, system=verbose)
'1 kilobyte'
>>> size(3000, system=verbose)
'2 kilobytes'
>>> size(1024 * 1024, system=verbose)
'1 megabyte'
>>> size(1024 * 1024 * 3, system=verbose)
'3 megabytes'
You can also use the SI system, where multipliers of 1000 increase the unit
size::
>>> from hurry.filesize import si
>>> size(1000, system=si)
'1K'
Changes
=======
0.9 (2009-03-11)
----------------
* Initial public release.
Download
========
Keywords: file size bytes
Platform: UNKNOWN
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: Libraries :: Python Modules

View File

@@ -0,0 +1,47 @@
hurry.filesize
==============
hurry.filesize a simple Python library that can take a number of bytes and
returns a human-readable string with the size in it, in kilobytes (K),
megabytes (M), etc.
The default system it uses is "traditional", where multipliers of 1024
increase the unit size::
>>> from hurry.filesize import size
>>> size(1024)
'1K'
An alternative, slightly more verbose system::
>>> from hurry.filesize import alternative
>>> size(1, system=alternative)
'1 byte'
>>> size(10, system=alternative)
'10 bytes'
>>> size(1024, system=alternative)
'1 KB'
A verbose system::
>>> from hurry.filesize import verbose
>>> size(10, system=verbose)
'10 bytes'
>>> size(1024, system=verbose)
'1 kilobyte'
>>> size(2000, system=verbose)
'1 kilobyte'
>>> size(3000, system=verbose)
'2 kilobytes'
>>> size(1024 * 1024, system=verbose)
'1 megabyte'
>>> size(1024 * 1024 * 3, system=verbose)
'3 megabytes'
You can also use the SI system, where multipliers of 1000 increase the unit
size::
>>> from hurry.filesize import si
>>> size(1000, system=si)
'1K'

View File

@@ -0,0 +1,110 @@
traditional = [
(1024 ** 5, 'P'),
(1024 ** 4, 'T'),
(1024 ** 3, 'G'),
(1024 ** 2, 'M'),
(1024 ** 1, 'K'),
(1024 ** 0, 'B'),
]
alternative = [
(1024 ** 5, ' PB'),
(1024 ** 4, ' TB'),
(1024 ** 3, ' GB'),
(1024 ** 2, ' MB'),
(1024 ** 1, ' KB'),
(1024 ** 0, (' byte', ' bytes')),
]
verbose = [
(1024 ** 5, (' petabyte', ' petabytes')),
(1024 ** 4, (' terabyte', ' terabytes')),
(1024 ** 3, (' gigabyte', ' gigabytes')),
(1024 ** 2, (' megabyte', ' megabytes')),
(1024 ** 1, (' kilobyte', ' kilobytes')),
(1024 ** 0, (' byte', ' bytes')),
]
iec = [
(1024 ** 5, 'Pi'),
(1024 ** 4, 'Ti'),
(1024 ** 3, 'Gi'),
(1024 ** 2, 'Mi'),
(1024 ** 1, 'Ki'),
(1024 ** 0, ''),
]
si = [
(1000 ** 5, 'P'),
(1000 ** 4, 'T'),
(1000 ** 3, 'G'),
(1000 ** 2, 'M'),
(1000 ** 1, 'K'),
(1000 ** 0, 'B'),
]
def size(bytes, system=traditional):
"""Human-readable file size.
Using the traditional system, where a factor of 1024 is used::
>>> size(10)
'10B'
>>> size(100)
'100B'
>>> size(1000)
'1000B'
>>> size(2000)
'1K'
>>> size(10000)
'9K'
>>> size(20000)
'19K'
>>> size(100000)
'97K'
>>> size(200000)
'195K'
>>> size(1000000)
'976K'
>>> size(2000000)
'1M'
Using the SI system, with a factor 1000::
>>> size(10, system=si)
'10B'
>>> size(100, system=si)
'100B'
>>> size(1000, system=si)
'1K'
>>> size(2000, system=si)
'2K'
>>> size(10000, system=si)
'10K'
>>> size(20000, system=si)
'20K'
>>> size(100000, system=si)
'100K'
>>> size(200000, system=si)
'200K'
>>> size(1000000, system=si)
'1M'
>>> size(2000000, system=si)
'2M'
"""
for factor, suffix in system:
if bytes >= factor:
break
amount = int(bytes/factor)
if isinstance(suffix, tuple):
singular, multiple = suffix
if amount == 1:
suffix = singular
else:
suffix = multiple
return str(amount) + suffix

View File

@@ -0,0 +1,19 @@
"None are so hopelessly enslaved as those who falsely believe they are free."
Johann Wolfgang von Goethe
Copyright (C) 2012 Tito Bouzout <tito.bouzout@gmail.com>
This license apply to all the files inside this program unless noted
different for some files or portions of code inside these files.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation. http://www.gnu.org/licenses/gpl.html
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/gpl.html

View File

@@ -0,0 +1,21 @@
SideBarEnhancements Changelog:
## Shorcuts collisioning with Sublime2's built-in shortcuts have been removed.
If you miss these
- Go to "Preferences" -> "Browse Packages" -> "User"
- Open or create file "Default.sublime-keymap"
- Add this content:
, { "keys": ["ctrl+t"], "command": "side_bar_new_file2" },
{ "keys": ["f2"], "command": "side_bar_rename" },
{ "keys": ["ctrl+alt+f"], "command": "side_bar_find_files_path_containing" }
## If you have problems configuring F12 key
- https://github.com/titoBouzout/SideBarEnhancements#f12-key

View File

@@ -0,0 +1 @@
{"url": "https://github.com/titoBouzout/SideBarEnhancements", "version": "2013.03.24.15.45.28", "description": "Enhancements to Sublime Text sidebar. Files and folders."}

View File

@@ -0,0 +1,193 @@
# Sublime Text 3 Compatibilty
Please see: https://github.com/titoBouzout/SideBarEnhancements/tree/st3atyourownrisk
# Description
Provides enhancements to the operations on Side Bar of Files and Folders for Sublime Text 2. See: http://www.sublimetext.com/
Notably provides delete as "move to trash", open with.. a and clipboard. Close, move, open and restore buffers affected by a rename/move command.
Provides the basics: new file/folder, edit, open/run, reveal, find in selected/parent/project, cut, copy, paste, paste in parent, rename, move, delete, refresh....
The not so basic: copy paths as URIs, URLs, content as UTF8, content as data:uri base64 ( nice for embedding into CSS! ), copy as tags img/a/script/style, duplicate
Preference to control if a buffer should be closed when affected by a deletion operation.
Allows to display "file modified date" and "file size" on statusbar.
All commands available for files and folders(when applicable) .
[img]http://dl.dropbox.com/u/43596449/tito/sublime/SideBar/screenshot.png[/img]
<img src="http://dl.dropbox.com/u/43596449/tito/sublime/SideBar/screenshot.png" border="0"/>
# F12 key
F12 key allows you to open the current file in browser.
If you want to add a url to that feature:
* Right click any file on sidebar and select: "Project -> Edit Projects Preview URLs"
* Edit this file, and add your paths and URLs with the following structure:
```
{
"S:/www/domain.tld":{
"url_testing":"http://testing",
"url_production":"http://domain.tld"
},
"C:/Users/luna/some/domain2.tld":{
"url_testing":"http://testing1",
"url_production":"http://productiontld2"
}
}
```
```url_testing``` allows you to set the url of your local server, opened via F12
```url_production``` allows you to set the url of your production server, opened via ALT+F12
# Notes on configuring the `Open With` menu:
Definitions file: `User/SidebarEnhancements/Open With/Side Bar.sublime-menu` (note the extra subfolder levels).
To open it, right-click on any file in an open project and select `Open With > Edit Applications...`
- On OSX, the 'application' property simply takes the *name* of an application, to which the file at hand's full path will be passed as if with `open ...`, e.g.: "application": "Google Chrome"
- On OSX, invoking *shell* commands is NOT supported.
# Todo
* Use a real clipboard integrated with the OS
# Installation
Install this repository via "Package Control" http://wbond.net/sublime_packages/package_control
# FAQ
Q: Uninstall?
* Follow the instructions here: https://github.com/titoBouzout/SideBarEnhancements/issues/18
Q: Why the menu is not shown on `Open Files`?
- It should be mentioned that the package's context menu is only available for files and folders **in a project (section `Folders` in the side bar)**, and _not_ on the open files listed at the top of the side bar, due to a limitation of ST2.
# Using the External Libraries
* "getImageInfo" to get width and height for images from "bfg-pages". See: http://code.google.com/p/bfg-pages/
* "desktop" to be able to open files with system handlers. See: http://pypi.python.org/pypi/desktop
* "send2trash" to be able to send to the trash instead of deleting for ever!. See: http://pypi.python.org/pypi/Send2Trash
* "hurry.filesize" to be able to format file sizes. See: http://pypi.python.org/pypi/hurry.filesize/
# Source-code
https://github.com/titoBouzout/SideBarEnhancements
# Forum Thread
http://www.sublimetext.com/forum/viewtopic.php?f=5&t=3331
# Contributors:
- Leif Ringstad
- Sven Axelsson
- Dalibor Simacek
- Stephen Horne
# Update v1.2:
* Improved: Feature "find advanced -> in paths containing" or CTRL+ALT+F now provides instant search, contribution by @ryecroft, thanks a lot!
* Fix: When only 1 tab is open and setting "close_windows_when_empty" is true. If the user renames or delete the current file will cause the application to close by itself (it will be perceived as a crash but is not).
* New: Add to the command palette useful commands as duplicate, reveal, move, open project file, open in browser, refresh, rename
* New: added keybindings F12 to open in local server, ALT+F12 to open in production server.
* New: Allows to copy the URL of the selected items.
* Improved: When renaming/moving remember the tab position and syntax.
* small fixes:
- Correct display of commands that are available only for projects
- Be sure to return None if there is no open project
- only display a message when using the clipboard if something was copied.
# Update v1.1:
* New: Add boolean preference "confirm_before_deleting" which controls if a the package should ask the user to delete files and folders
* New: When using copy, cut or paste the editor will ask for "replace items" when these items exists. Note: When a folder exists the package will merge the two as in the OS.
# Update v1.0:
* New: Add boolean preference "close_affected_buffers_when_deleting_even_if_dirty" which controls if a buffer should be closed when affected by a deletion operation-
# Update v0.9:
* Minor tweaks and fixes.
* Fix: Re-enable move to trash for OSX
* New: Allow to display "file modified time" and "file size" on statusbar via preferences.
* Fix: Disable of built-in function is now automatic.
* On the way: exclude from project, promote as project folder. ( requires restart to apply changes, looks like there is no way to reload project files.)
* Fix: Many appends of same directory to "sys.path"
# Update v0.8:
* Full review for when the user has selection of multiples items.
* New: Added support for bookmarks and marks for when a view is moved.
# Update v0.7:
* New: After a rename of a file or folder, the affected views will update(reload) to reflect the new location keeping intact content, selections, folded regions and scroll position.
* New: File path search
# Update v0.6:
* Fix: Paste was pasting on parent folder (Misinterpretation of boolean)
* Fix: "Open with" works on Linux
* Improved: Allow case change on Windows when renaming a file or folder
* Improved: Update to "find commands" for version 2134
# Update v0.5:
* Change: Removed "files" prefix from commands.
* New: Ability to copy a path relative to the current view
* New: Ability to "paste in parent"
* New: Ctrl+T will ask for a new file on same folder as current view
* Improved: Context menu open faster
# Update v0.4:
* Fix: "Open / Run" fixed on Linux thanks to project [desktop](http://pypi.python.org/pypi/desktop )
* Improved: "Paste" command copy permission bits, last access time, last modification time, and flags
* Improved: "Delete" command send files to trash thanks to [Send2Trash](http://pypi.python.org/pypi/Send2Trash ) . NOTE: If "Delete" fails to send to trash it will ask for "Permanently Delete" On confirmation it delete the item forever.
# Update v0.3:
* Fixed: Open should run correctly with some strange characters on paths
* New: "Open with.." is enabled and allows to set custom applications for different file extensions.
* New: "Copy content as Data URI" ( handy for embedding images on CSS files )
* Improved: Copy img tags now add attributes width and height thanks to project [bfg-pages](http://code.google.com/p/bfg-pages/ ) and suggestion from nobleach.
# Update v0.2:
* Copy paths and names in various formats.
* Removed license to not conflict with sublime
# Update v0.1:
* Tweaks here, tweaks there.
* Renamed repository
* New: "edit" will open the file with sublime text.
* New: "open" will call to the command line with the file path
* New: a disabled "open with" for future use
* Tweaks: ids to all context elements

View File

@@ -0,0 +1,14 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
import sys
if sys.platform == 'darwin':
from .plat_osx import send2trash
elif sys.platform == 'win32':
from .plat_win import send2trash
else:
from .plat_other import send2trash

View File

@@ -0,0 +1,44 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from ctypes import cdll, byref, Structure, c_char, c_char_p
from ctypes.util import find_library
Foundation = cdll.LoadLibrary(find_library('Foundation'))
CoreServices = cdll.LoadLibrary(find_library('CoreServices'))
GetMacOSStatusCommentString = Foundation.GetMacOSStatusCommentString
GetMacOSStatusCommentString.restype = c_char_p
FSPathMakeRefWithOptions = CoreServices.FSPathMakeRefWithOptions
FSMoveObjectToTrashSync = CoreServices.FSMoveObjectToTrashSync
kFSPathMakeRefDefaultOptions = 0
kFSPathMakeRefDoNotFollowLeafSymlink = 0x01
kFSFileOperationDefaultOptions = 0
kFSFileOperationOverwrite = 0x01
kFSFileOperationSkipSourcePermissionErrors = 0x02
kFSFileOperationDoNotMoveAcrossVolumes = 0x04
kFSFileOperationSkipPreflight = 0x08
class FSRef(Structure):
_fields_ = [('hidden', c_char * 80)]
def check_op_result(op_result):
if op_result:
msg = GetMacOSStatusCommentString(op_result).decode('utf-8')
raise OSError(msg)
def send2trash(path):
if not isinstance(path, bytes):
path = path.encode('utf-8')
fp = FSRef()
opts = kFSPathMakeRefDoNotFollowLeafSymlink
op_result = FSPathMakeRefWithOptions(path, opts, byref(fp), None)
check_op_result(op_result)
opts = kFSFileOperationDefaultOptions
op_result = FSMoveObjectToTrashSync(byref(fp), None, opts)
check_op_result(op_result)

View File

@@ -0,0 +1,154 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
# This is a reimplementation of plat_other.py with reference to the
# freedesktop.org trash specification:
# [1] http://www.freedesktop.org/wiki/Specifications/trash-spec
# [2] http://www.ramendik.ru/docs/trashspec.html
# See also:
# [3] http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
#
# For external volumes this implementation will raise an exception if it can't
# find or create the user's trash directory.
#import sys
import os
import os.path as op
from datetime import datetime
import stat
from urllib import quote
FILES_DIR = 'files'
INFO_DIR = 'info'
INFO_SUFFIX = '.trashinfo'
# Default of ~/.local/share [3]
XDG_DATA_HOME = op.expanduser(os.environ.get('XDG_DATA_HOME', '~/.local/share'))
HOMETRASH = op.join(XDG_DATA_HOME, 'Trash')
uid = os.getuid()
TOPDIR_TRASH = '.Trash'
TOPDIR_FALLBACK = '.Trash-' + str(uid)
def is_parent(parent, path):
path = op.realpath(path) # In case it's a symlink
parent = op.realpath(parent)
return path.startswith(parent)
def format_date(date):
return date.strftime("%Y-%m-%dT%H:%M:%S")
def info_for(src, topdir):
# ...it MUST not include a ".."" directory, and for files not "under" that
# directory, absolute pathnames must be used. [2]
if topdir is None or not is_parent(topdir, src):
src = op.abspath(src)
else:
src = op.relpath(src, topdir)
info = "[Trash Info]\n"
info += "Path=" + quote(src) + "\n"
info += "DeletionDate=" + format_date(datetime.now()) + "\n"
return info
def check_create(dir):
# use 0700 for paths [3]
if not op.exists(dir):
os.makedirs(dir, 0o700)
def trash_move(src, dst, topdir=None):
filename = op.basename(src)
filespath = op.join(dst, FILES_DIR)
infopath = op.join(dst, INFO_DIR)
base_name, ext = op.splitext(filename)
counter = 0
destname = filename
while op.exists(op.join(filespath, destname)) or op.exists(op.join(infopath, destname + INFO_SUFFIX)):
counter += 1
destname = '%s %s%s' % (base_name, counter, ext)
check_create(filespath)
check_create(infopath)
os.rename(src, op.join(filespath, destname))
f = open(op.join(infopath, destname + INFO_SUFFIX), 'w')
f.write(info_for(src, topdir))
f.close()
def find_mount_point(path):
# Even if something's wrong, "/" is a mount point, so the loop will exit.
# Use realpath in case it's a symlink
path = op.realpath(path) # Required to avoid infinite loop
while not op.ismount(path):
path = op.split(path)[0]
return path
def find_ext_volume_global_trash(volume_root):
# from [2] Trash directories (1) check for a .Trash dir with the right
# permissions set.
trash_dir = op.join(volume_root, TOPDIR_TRASH)
if not op.exists(trash_dir):
return None
mode = os.lstat(trash_dir).st_mode
# vol/.Trash must be a directory, cannot be a symlink, and must have the
# sticky bit set.
if not op.isdir(trash_dir) or op.islink(trash_dir) or not (mode & stat.S_ISVTX):
return None
trash_dir = op.join(trash_dir, str(uid))
try:
check_create(trash_dir)
except OSError:
return None
return trash_dir
def find_ext_volume_fallback_trash(volume_root):
# from [2] Trash directories (1) create a .Trash-$uid dir.
trash_dir = op.join(volume_root, TOPDIR_FALLBACK)
# Try to make the directory, if we can't the OSError exception will escape
# be thrown out of send2trash.
check_create(trash_dir)
return trash_dir
def find_ext_volume_trash(volume_root):
trash_dir = find_ext_volume_global_trash(volume_root)
if trash_dir is None:
trash_dir = find_ext_volume_fallback_trash(volume_root)
return trash_dir
# Pull this out so it's easy to stub (to avoid stubbing lstat itself)
def get_dev(path):
return os.lstat(path).st_dev
def send2trash(path):
#if not isinstance(path, str):
# path = str(path, sys.getfilesystemencoding())
#if not op.exists(path):
# raise OSError("File not found: %s" % path)
# ...should check whether the user has the necessary permissions to delete
# it, before starting the trashing operation itself. [2]
#if not os.access(path, os.W_OK):
# raise OSError("Permission denied: %s" % path)
# if the file to be trashed is on the same device as HOMETRASH we
# want to move it there.
path_dev = get_dev(path)
# If XDG_DATA_HOME or HOMETRASH do not yet exist we need to stat the
# home directory, and these paths will be created further on if needed.
trash_dev = get_dev(op.expanduser('~'))
if path_dev == trash_dev:
topdir = XDG_DATA_HOME
dest_trash = HOMETRASH
else:
topdir = find_mount_point(path)
trash_dev = get_dev(topdir)
if trash_dev != path_dev:
raise OSError("Couldn't find mount point for %s" % path)
dest_trash = find_ext_volume_trash(topdir)
trash_move(path, dest_trash, topdir)

View File

@@ -0,0 +1,56 @@
# Copyright 2010 Hardcoded Software (http://www.hardcoded.net)
# This software is licensed under the "BSD" License as described in the "LICENSE" file,
# which should be included with this package. The terms are also available at
# http://www.hardcoded.net/licenses/bsd_license
from ctypes import windll, Structure, byref, c_uint
from ctypes.wintypes import HWND, UINT, LPCWSTR, BOOL
#import os.path as op
shell32 = windll.shell32
SHFileOperationW = shell32.SHFileOperationW
class SHFILEOPSTRUCTW(Structure):
_fields_ = [
("hwnd", HWND),
("wFunc", UINT),
("pFrom", LPCWSTR),
("pTo", LPCWSTR),
("fFlags", c_uint),
("fAnyOperationsAborted", BOOL),
("hNameMappings", c_uint),
("lpszProgressTitle", LPCWSTR),
]
FO_MOVE = 1
FO_COPY = 2
FO_DELETE = 3
FO_RENAME = 4
FOF_MULTIDESTFILES = 1
FOF_SILENT = 4
FOF_NOCONFIRMATION = 16
FOF_ALLOWUNDO = 64
FOF_NOERRORUI = 1024
def send2trash(path):
#
#if not isinstance(path, str):
# path = str(path, 'mbcs')
#if not op.isabs(path):
# path = op.abspath(path)
fileop = SHFILEOPSTRUCTW()
fileop.hwnd = 0
fileop.wFunc = FO_DELETE
fileop.pFrom = LPCWSTR(path + '\0')
fileop.pTo = None
fileop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT
fileop.fAnyOperationsAborted = 0
fileop.hNameMappings = 0
fileop.lpszProgressTitle = None
result = SHFileOperationW(byref(fileop))
if result:
msg = "Couldn't perform operation. Error code: %d" % result
raise OSError(msg)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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