feat(SublimeText2.UtilPackages): cache packages
This commit is contained in:
3
EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/.gitignore
vendored
Normal file
3
EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.pyc
|
||||
*.cache
|
||||
*.sublime-project
|
@@ -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"}
|
||||
}
|
||||
]
|
@@ -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"}
|
||||
}
|
||||
]
|
@@ -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": "-" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
@@ -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" }
|
||||
]
|
@@ -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
|
||||
|
||||
|
||||
}
|
@@ -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>
|
File diff suppressed because it is too large
Load Diff
@@ -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);
|
@@ -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)));
|
||||
|
||||
|
@@ -0,0 +1,4 @@
|
||||
[
|
||||
{ "caption": "-", "id": "folder_commands" },
|
||||
{ "caption": "Remove Folder from Project", "command": "remove_folder", "args": { "dirs": []} }
|
||||
]
|
@@ -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" }
|
||||
]
|
@@ -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
|
@@ -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'
|
||||
|
@@ -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
|
||||
|
@@ -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
|
@@ -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
|
@@ -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."}
|
@@ -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
|
@@ -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
|
@@ -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)
|
@@ -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)
|
@@ -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)
|
||||
|
@@ -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