├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature-requests.md ├── .gitignore ├── Addon Scripts └── Blender Manager │ ├── __init__.py │ └── blender_manager_operator.py ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── docs └── WELCOME.md └── source ├── Assets └── Images │ ├── bmicon.ico │ └── bmng.ico ├── Blender_Manager_Addon.zip ├── blender_manager.py ├── blendermanager.ico ├── requirements.txt ├── themes ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-310.pyc │ ├── standard.cpython-310.pyc │ └── user.cpython-310.pyc ├── standard.py └── user.py ├── tkdnd2.8 ├── pkgIndex.tcl ├── tkdnd.tcl ├── tkdnd28.dll ├── tkdnd_compat.tcl ├── tkdnd_generic.tcl ├── tkdnd_macosx.tcl ├── tkdnd_unix.tcl └── tkdnd_windows.tcl └── updater.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | If you encounter a bug while using Blender Manager, we’d love to hear about it so we can improve your experience. Please include the following information in your report: 11 | 12 | - **Description**: A clear and concise description of the issue. 13 | - **Steps to Reproduce**: Detailed steps to replicate the bug. 14 | - **Expected Behavior**: What you expected to happen. 15 | - **Actual Behavior**: What actually happened. 16 | - **Environment**: Include your operating system, Blender Manager version, and Blender version (if applicable). 17 | - **Screenshots/Logs**: Any relevant screenshots or log files that can help us understand the problem better. 18 | 19 | Send your bug reports to [Developer](mailto:kaansoyler@proton.me) 20 | 21 | Thank you for helping us make Blender Manager better! 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-requests.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Requests 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Have an idea to improve Blender Manager? We’d love to hear it! Please include the following details in your feature request: 11 | 12 | - **Feature Description**: A clear and concise description of the feature you’re suggesting. 13 | - **Use Case**: Explain how this feature would improve your workflow or benefit the community. 14 | - **Additional Details**: Include any relevant examples, mockups, or other information to help us understand your idea. 15 | - **Priority**: Let us know how important this feature is to your work (e.g., nice-to-have, must-have). 16 | 17 | Send your feature requests to [Developer](mailto:kaansoyler@proton.me) 18 | 19 | Thank you for contributing to the future of Blender Manager! 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # pdm 105 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 106 | #pdm.lock 107 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 108 | # in version control. 109 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 110 | .pdm.toml 111 | .pdm-python 112 | .pdm-build/ 113 | 114 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 115 | __pypackages__/ 116 | 117 | # Celery stuff 118 | celerybeat-schedule 119 | celerybeat.pid 120 | 121 | # SageMath parsed files 122 | *.sage.py 123 | 124 | # Environments 125 | .env 126 | .venv 127 | env/ 128 | venv/ 129 | ENV/ 130 | env.bak/ 131 | venv.bak/ 132 | 133 | # Spyder project settings 134 | .spyderproject 135 | .spyproject 136 | 137 | # Rope project settings 138 | .ropeproject 139 | 140 | # mkdocs documentation 141 | /site 142 | 143 | # mypy 144 | .mypy_cache/ 145 | .dmypy.json 146 | dmypy.json 147 | 148 | # Pyre type checker 149 | .pyre/ 150 | 151 | # pytype static type analyzer 152 | .pytype/ 153 | 154 | # Cython debug symbols 155 | cython_debug/ 156 | 157 | # PyCharm 158 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 159 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 160 | # and can be added to the global gitignore or merged into this file. For a more nuclear 161 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 162 | #.idea/ 163 | -------------------------------------------------------------------------------- /Addon Scripts/Blender Manager/__init__.py: -------------------------------------------------------------------------------- 1 | bl_info = { 2 | "name": "Blender Manager", 3 | "description": "Essential Blender addon for Blender Manager.", 4 | "author": "verlorengest", 5 | "version": (1, 0, 3), 6 | "blender": (4, 2, 0), # 4.0.0 + 7 | "location": "File > External Tools > Blender Manager", 8 | "category": "System", 9 | } 10 | 11 | import bpy 12 | import json 13 | import os 14 | import time 15 | from bpy.app.handlers import persistent 16 | from . import blender_manager_operator 17 | from .blender_manager_operator import load_autosave_settings 18 | 19 | 20 | 21 | 22 | 23 | COMM_DIR = os.path.join(os.path.expanduser("~"), '.BlenderManager', 'mngaddon') 24 | PROJECT_TIME_FILE = os.path.join(COMM_DIR, 'project_time.json') 25 | SETTINGS_FILE = os.path.join(COMM_DIR, 'settings.json') 26 | 27 | project_open_time = None 28 | project_path = None 29 | 30 | def load_project_time_data(): 31 | """Load project time data from JSON file.""" 32 | if os.path.exists(PROJECT_TIME_FILE): 33 | try: 34 | with open(PROJECT_TIME_FILE, 'r') as file: 35 | data = json.load(file) 36 | print("[Blender Manager] Loaded project time data:", data) 37 | return data 38 | except json.JSONDecodeError: 39 | print("[Blender Manager] Invalid JSON format in project_time.json.") 40 | return {} 41 | 42 | def save_project_time_data(data): 43 | """Save project time data to JSON file with error checking.""" 44 | os.makedirs(COMM_DIR, exist_ok=True) 45 | try: 46 | with open(PROJECT_TIME_FILE, 'w') as file: 47 | json.dump(data, file, indent=4) 48 | print("[Blender Manager] Successfully saved project time data:", data) 49 | except Exception as e: 50 | print(f"[Blender Manager] Error saving project time data: {e}") 51 | 52 | @persistent 53 | def on_save_post_handler(dummy): 54 | """Handler called after saving the blend file.""" 55 | global project_open_time, project_path 56 | print("[Blender Manager] Save event triggered.") 57 | try: 58 | filepath = getattr(bpy.data, 'filepath', None) 59 | if filepath: 60 | current_time = time.time() 61 | if project_path == filepath and project_open_time is not None: 62 | elapsed_time = current_time - project_open_time 63 | print(f"[Blender Manager] Elapsed time for {project_path}: {elapsed_time:.2f} seconds.") 64 | 65 | project_time_data = load_project_time_data() 66 | 67 | if project_path in project_time_data: 68 | project_time_data[project_path] += elapsed_time 69 | else: 70 | project_time_data[project_path] = elapsed_time 71 | 72 | save_project_time_data(project_time_data) 73 | 74 | project_open_time = current_time 75 | elif project_path is None: 76 | elapsed_time = current_time - project_open_time 77 | print(f"[Blender Manager] First save. Elapsed time for {filepath}: {elapsed_time:.2f} seconds.") 78 | 79 | project_time_data = load_project_time_data() 80 | 81 | project_time_data[filepath] = elapsed_time 82 | 83 | save_project_time_data(project_time_data) 84 | 85 | project_path = filepath 86 | project_open_time = current_time 87 | else: 88 | elapsed_time = current_time - project_open_time 89 | print(f"[Blender Manager] Elapsed time for {project_path}: {elapsed_time:.2f} seconds.") 90 | 91 | project_time_data = load_project_time_data() 92 | 93 | if project_path in project_time_data: 94 | project_time_data[project_path] += elapsed_time 95 | else: 96 | project_time_data[project_path] = elapsed_time 97 | 98 | save_project_time_data(project_time_data) 99 | 100 | project_path = filepath 101 | project_open_time = current_time 102 | print(f"[Blender Manager] Project switched to: {project_path} at {time.ctime(project_open_time)}") 103 | 104 | if project_path == filepath: 105 | project_open_time = current_time 106 | else: 107 | print("[Blender Manager] No project path; not saving time.") 108 | except AttributeError: 109 | print("[Blender Manager] bpy.data.filepath not accessible in on_save_post_handler") 110 | 111 | @persistent 112 | def on_load_post_handler(dummy): 113 | """Handler called after loading a blend file or creating a new one.""" 114 | global project_open_time, project_path 115 | print("[Blender Manager] Load event triggered.") 116 | try: 117 | filepath = getattr(bpy.data, 'filepath', None) 118 | current_time = time.time() 119 | 120 | if filepath: 121 | 122 | filepath = os.path.abspath(os.path.normpath(filepath)) 123 | print(f"[Blender Manager] Normalized filepath: {filepath}") 124 | # Existing project loaded 125 | if project_path and project_open_time is not None: 126 | elapsed_time = current_time - project_open_time 127 | print(f"[Blender Manager] Elapsed time for {project_path}: {elapsed_time:.2f} seconds.") 128 | 129 | project_time_data = load_project_time_data() 130 | 131 | if project_path in project_time_data: 132 | project_time_data[project_path] += elapsed_time 133 | else: 134 | project_time_data[project_path] = elapsed_time 135 | 136 | save_project_time_data(project_time_data) 137 | 138 | project_path = filepath 139 | project_open_time = current_time 140 | print(f"[Blender Manager] Project loaded: {project_path} at {time.ctime(project_open_time)}") 141 | load_autosave_settings() 142 | project_path = filepath 143 | project_open_time = current_time 144 | print(f"[Blender Manager] Project loaded: {project_path} at {time.ctime(project_open_time)}") 145 | except Exception as e: 146 | print(f"[Blender Manager] Error in on_load_post_handler: {e}") 147 | 148 | @persistent 149 | def on_quit_pre_handler(dummy): 150 | """Handler called before Blender quits.""" 151 | global project_open_time, project_path 152 | print("[Blender Manager] Blender is quitting.") 153 | try: 154 | filepath = getattr(bpy.data, 'filepath', None) 155 | if filepath and project_open_time is not None: 156 | current_time = time.time() 157 | elapsed_time = current_time - project_open_time 158 | print(f"[Blender Manager] Elapsed time for {project_path}: {elapsed_time:.2f} seconds.") 159 | 160 | project_time_data = load_project_time_data() 161 | 162 | if project_path in project_time_data: 163 | project_time_data[project_path] += elapsed_time 164 | else: 165 | project_time_data[project_path] = elapsed_time 166 | 167 | save_project_time_data(project_time_data) 168 | else: 169 | print("[Blender Manager] No project path or open time; not saving time.") 170 | except AttributeError: 171 | print("[Blender Manager] bpy.data.filepath not accessible in on_quit_pre_handler") 172 | 173 | def register(): 174 | """Register the addon and its handlers.""" 175 | os.makedirs(COMM_DIR, exist_ok=True) 176 | print(f"[Blender Manager] Directory ensured at: {COMM_DIR}") 177 | 178 | global project_open_time, project_path 179 | filepath = getattr(bpy.data, 'filepath', None) 180 | if filepath: 181 | project_path = os.path.abspath(os.path.normpath(filepath)) 182 | project_open_time = time.time() 183 | print(f"[Blender Manager] Existing project detected: {project_path} at {time.ctime(project_open_time)}") 184 | 185 | else: 186 | project_path = None 187 | project_open_time = time.time() 188 | print(f"[Blender Manager] No project open at start. Timer will start when a project is created or loaded.") 189 | 190 | bpy.app.handlers.save_post.append(on_save_post_handler) 191 | bpy.app.handlers.load_post.append(on_load_post_handler) 192 | print("[Blender Manager] on_load_post_handler registered.") 193 | if hasattr(bpy.app.handlers, 'quit_pre'): 194 | bpy.app.handlers.quit_pre.append(on_quit_pre_handler) 195 | print("[Blender Manager] Quit pre handler appended.") 196 | 197 | blender_manager_operator.register() 198 | 199 | print("[Blender Manager] Blender Manager Plugin registered.") 200 | 201 | 202 | 203 | def unregister(): 204 | """Unregister the addon and remove its handlers.""" 205 | if on_save_post_handler in bpy.app.handlers.save_post: 206 | bpy.app.handlers.save_post.remove(on_save_post_handler) 207 | print("[Blender Manager] Removed save_post handler.") 208 | if on_load_post_handler in bpy.app.handlers.load_post: 209 | bpy.app.handlers.load_post.remove(on_load_post_handler) 210 | print("[Blender Manager] Removed load_post handler.") 211 | if hasattr(bpy.app.handlers, 'quit_pre') and on_quit_pre_handler in bpy.app.handlers.quit_pre: 212 | bpy.app.handlers.quit_pre.remove(on_quit_pre_handler) 213 | print("[Blender Manager] Removed quit_pre handler.") 214 | 215 | blender_manager_operator.unregister() 216 | 217 | print("[Blender Manager] Blender Manager Plugin unregistered.") 218 | 219 | 220 | 221 | if __name__ == "__main__": 222 | register() 223 | -------------------------------------------------------------------------------- /Addon Scripts/Blender Manager/blender_manager_operator.py: -------------------------------------------------------------------------------- 1 | import bpy 2 | import os 3 | import json 4 | import math 5 | import re 6 | 7 | COMM_DIR = os.path.join(os.path.expanduser("~"), '.BlenderManager', 'mngaddon') 8 | SETTINGS_FILE = os.path.join(COMM_DIR, 'settings.json') 9 | AUTOSAVED_PROJECTS_FILE = os.path.join(COMM_DIR, 'autosaved_projects.json') 10 | 11 | autosave_settings = { 12 | "auto_save_project": False, 13 | "auto_save_interval": None, 14 | "auto_save_style": None, 15 | "project_name": None, 16 | "project_dir": None, 17 | "separate_counter": 1 18 | } 19 | 20 | 21 | def check_for_settings_file(): 22 | """Checks for settings.json and processes it if found, then tries to load autosave settings.""" 23 | if os.path.exists(SETTINGS_FILE): 24 | print(f"[Blender Manager] settings.json found at {SETTINGS_FILE}") 25 | try: 26 | with open(SETTINGS_FILE, 'r', encoding='utf-8') as f: 27 | data = json.load(f) 28 | print("[Blender Manager] Settings loaded:", data) 29 | apply_settings(data) 30 | print("[Blender Manager] settings.json processed.") 31 | os.remove(SETTINGS_FILE) # Delete settings.json after processing 32 | except Exception as e: 33 | print(f"[Blender Manager] Error reading settings.json: {e}") 34 | else: 35 | print("[Blender Manager] No settings.json found.") 36 | 37 | # After attempting to process settings.json, load autosave if available 38 | bpy.app.timers.register(load_autosave_settings, first_interval=2.0) 39 | return None 40 | 41 | 42 | def apply_settings(data): 43 | """Applies the settings in Blender""" 44 | print("[Blender Manager] Applying settings...") 45 | 46 | if bpy.ops.object.mode_set.poll(): 47 | bpy.ops.object.mode_set(mode='OBJECT') 48 | 49 | def clear_scene(): 50 | bpy.ops.object.select_all(action='DESELECT') 51 | for obj in bpy.context.scene.objects: 52 | obj.select_set(True) 53 | bpy.ops.object.delete() 54 | bpy.ops.outliner.orphans_purge(do_recursive=True) 55 | 56 | clear_scene() 57 | 58 | project_name = data.get('project_name', None) 59 | project_dir = data.get('project_dir', None) 60 | 61 | # Attempt initial save if project_name and project_dir provided 62 | if project_name and project_dir: 63 | save_project(project_name, project_dir) 64 | else: 65 | print("[Blender Manager] Project name or directory missing, skipping initial save.") 66 | 67 | # Handle base mesh 68 | base_mesh = data.get('base_mesh', {}) 69 | base_mesh_path = base_mesh.get('path', '') 70 | if base_mesh_path and os.path.exists(base_mesh_path): 71 | print(f"[Blender Manager] Importing base mesh from {base_mesh_path}") 72 | import_mesh(base_mesh_path) 73 | else: 74 | print(f"[Blender Manager] Base mesh not found or path is empty: {base_mesh_path}") 75 | 76 | # Handle reference images 77 | reference_images = data.get('reference_images', {}) 78 | add_reference_images(reference_images) 79 | 80 | # Handle light and camera 81 | if data.get('add_light', False): 82 | add_light() 83 | 84 | if data.get('add_camera', False): 85 | add_camera() 86 | 87 | # Handle autosave 88 | auto_save_project_flag = data.get('auto_save_project', False) 89 | auto_save_interval = data.get('auto_save_interval', None) 90 | auto_save_style = data.get('auto_save_style', None) 91 | 92 | if auto_save_project_flag and project_name and project_dir and auto_save_interval and auto_save_style: 93 | setup_autosave(project_name, project_dir, auto_save_interval, auto_save_style) 94 | else: 95 | print("[Blender Manager] Autosave not started. Conditions not met.") 96 | 97 | 98 | def save_project(name, directory): 99 | """Saves the project with the given name and directory after a delay""" 100 | save_path = os.path.join(directory, f"{name}.blend") 101 | os.makedirs(directory, exist_ok=True) # Ensure the directory exists 102 | 103 | def delayed_save(): 104 | print(f"[Blender Manager] Saving project as {save_path}...") 105 | bpy.ops.wm.save_as_mainfile(filepath=save_path) 106 | print(f"[Blender Manager] Project saved successfully: {save_path}") 107 | 108 | bpy.app.timers.register(lambda: delayed_save() or None, first_interval=1.0) 109 | 110 | 111 | def import_mesh(mesh_path): 112 | """Imports a mesh file into the scene""" 113 | ext = os.path.splitext(mesh_path)[1].lower() 114 | if ext == '.obj': 115 | bpy.ops.import_scene.obj(filepath=mesh_path) 116 | elif ext == '.fbx': 117 | bpy.ops.import_scene.fbx(filepath=mesh_path) 118 | elif ext == '.stl': 119 | bpy.ops.import_mesh.stl(filepath=mesh_path) 120 | else: 121 | print(f"[Blender Manager] Unsupported mesh format: {ext}") 122 | 123 | 124 | def add_reference_images(images): 125 | """Adds reference images to the scene""" 126 | for position, image_path in images.items(): 127 | if os.path.exists(image_path): 128 | print(f"[Blender Manager] Adding reference image for {position} from {image_path}") 129 | create_background_image(image_path, position) 130 | else: 131 | print(f"[Blender Manager] Image not found: {image_path}") 132 | 133 | 134 | def create_background_image(image_path, position): 135 | """Creates a background image aligned to the specified view.""" 136 | bpy.ops.object.empty_add(type='IMAGE', align='WORLD', location=(0, 0, 0)) 137 | empty = bpy.context.active_object 138 | empty.name = f"Ref_{position.capitalize()}" 139 | 140 | try: 141 | img = bpy.data.images.load(filepath=image_path) 142 | empty.data = img 143 | print(f"[Blender Manager] Image loaded successfully: {image_path}") 144 | except Exception as e: 145 | print(f"[Blender Manager] Error loading image {image_path}: {e}") 146 | bpy.data.objects.remove(empty, do_unlink=True) 147 | return 148 | 149 | empty.scale = (5, 5, 5) 150 | empty.empty_display_type = 'IMAGE' 151 | 152 | if position == 'front': 153 | empty.rotation_euler = (math.radians(90), 0, 0) 154 | elif position == 'back': 155 | empty.rotation_euler = (math.radians(90), 0, math.radians(180)) 156 | elif position == 'right': 157 | empty.rotation_euler = (math.radians(90), 0, math.radians(-90)) 158 | elif position == 'left': 159 | empty.rotation_euler = (math.radians(90), 0, math.radians(90)) 160 | elif position == 'top': 161 | empty.rotation_euler = (0, 0, 0) 162 | elif position == 'bottom': 163 | empty.rotation_euler = (math.radians(180), 0, 0) 164 | else: 165 | print(f"[Blender Manager] Unknown position: {position}. Image placed without rotation.") 166 | 167 | for obj in bpy.context.scene.objects: 168 | if obj.type == 'EMPTY' and obj.empty_display_type == 'IMAGE': 169 | obj.empty_image_side = 'FRONT' 170 | print(f"Side settings changed to Front for {obj.name}.") 171 | 172 | 173 | def add_light(): 174 | """Adds a light to the scene""" 175 | bpy.ops.object.light_add(type='POINT', align='WORLD', location=(5, 5, 5)) 176 | light = bpy.context.active_object 177 | light.data.energy = 1000 178 | print("[Blender Manager] Added light to the scene.") 179 | 180 | 181 | def add_camera(): 182 | """Adds a camera to the scene""" 183 | bpy.ops.object.camera_add(align='VIEW', location=(0, -10, 0), rotation=(1.5708, 0, 0)) 184 | camera = bpy.context.active_object 185 | bpy.context.scene.camera = camera 186 | print("[Blender Manager] Added camera to the scene.") 187 | 188 | 189 | def setup_autosave(project_name, project_dir, interval, style): 190 | """Setup autosave for the given project.""" 191 | autosave_settings["auto_save_project"] = True 192 | autosave_settings["auto_save_interval"] = interval 193 | autosave_settings["auto_save_style"] = style 194 | autosave_settings["project_name"] = project_name 195 | autosave_settings["project_dir"] = project_dir 196 | autosave_settings["separate_counter"] = 1 197 | 198 | # Write settings to AUTOSAVED_PROJECTS_FILE 199 | write_autosaved_project(project_name, project_dir, interval, style) 200 | 201 | # Start autosave timer 202 | start_autosave_timer() 203 | 204 | 205 | def write_autosaved_project(project_name, project_dir, interval, style): 206 | """Write current autosave settings to AUTOSAVED_PROJECTS_FILE keyed by project_name.""" 207 | data = {} 208 | if os.path.exists(AUTOSAVED_PROJECTS_FILE): 209 | try: 210 | with open(AUTOSAVED_PROJECTS_FILE, 'r', encoding='utf-8') as f: 211 | data = json.load(f) 212 | except: 213 | data = {} 214 | 215 | if "projects" not in data: 216 | data["projects"] = {} 217 | 218 | data["projects"][project_name] = { 219 | "auto_save_project": True, 220 | "auto_save_interval": interval, 221 | "auto_save_style": style, 222 | "project_name": project_name, 223 | "project_dir": project_dir 224 | } 225 | 226 | with open(AUTOSAVED_PROJECTS_FILE, 'w', encoding='utf-8') as f: 227 | json.dump(data, f, ensure_ascii=False, indent=4) 228 | print("[Blender Manager] Autosave settings written to autosaved_projects.json.") 229 | 230 | 231 | def extract_base_project_name(filename): 232 | """ 233 | Extracts the base project name from a given filename. 234 | If filename is like 'NewProject_4.blend', it should return 'NewProject'. 235 | If filename is 'NewProject.blend', it returns 'NewProject'. 236 | """ 237 | name, ext = os.path.splitext(filename) 238 | # Remove trailing underscores and digits 239 | # We'll match a pattern: (BaseName)_(Number)? 240 | # If there's a trailing underscore + digits, remove them. 241 | match = re.match(r'^(.*?)(_\d+)?$', name) 242 | if match: 243 | return match.group(1) 244 | return name 245 | 246 | 247 | def load_autosave_settings(): 248 | """Load autosave settings from AUTOSAVED_PROJECTS_FILE if current project (or its variants) matches one with autosave enabled.""" 249 | current_path = bpy.data.filepath 250 | if not current_path: 251 | # No project loaded yet 252 | return None 253 | 254 | current_filename = os.path.basename(current_path) 255 | guessed_project_name = extract_base_project_name(current_filename) 256 | 257 | if os.path.exists(AUTOSAVED_PROJECTS_FILE): 258 | try: 259 | with open(AUTOSAVED_PROJECTS_FILE, 'r', encoding='utf-8') as f: 260 | data = json.load(f) 261 | except: 262 | data = {} 263 | 264 | projects = data.get("projects", {}) 265 | if guessed_project_name in projects: 266 | project_data = projects[guessed_project_name] 267 | if (project_data.get("auto_save_project", False) and 268 | project_data.get("project_name") and 269 | project_data.get("project_dir") and 270 | project_data.get("auto_save_interval") and 271 | project_data.get("auto_save_style")): 272 | # Setup autosave again with these settings 273 | autosave_settings["auto_save_project"] = True 274 | autosave_settings["auto_save_interval"] = project_data["auto_save_interval"] 275 | autosave_settings["auto_save_style"] = project_data["auto_save_style"] 276 | autosave_settings["project_name"] = project_data["project_name"] 277 | autosave_settings["project_dir"] = project_data["project_dir"] 278 | autosave_settings["separate_counter"] = 1 # reset counter or could load from file if needed 279 | print("[Blender Manager] Resuming autosave from stored settings for project:", guessed_project_name) 280 | start_autosave_timer() 281 | else: 282 | print("[Blender Manager] Autosave settings found but incomplete or disabled for:", guessed_project_name) 283 | else: 284 | print("[Blender Manager] No autosave settings found for:", guessed_project_name) 285 | return None 286 | 287 | 288 | def start_autosave_timer(): 289 | """Start the autosave timer based on the autosave_settings.""" 290 | interval = autosave_settings["auto_save_interval"] 291 | if interval is None: 292 | return 293 | 294 | def autosave(): 295 | if not autosave_settings["auto_save_project"]: 296 | return None 297 | 298 | project_name = autosave_settings["project_name"] 299 | project_dir = autosave_settings["project_dir"] 300 | style = autosave_settings["auto_save_style"] 301 | base_path = os.path.join(project_dir, f"{project_name}.blend") 302 | 303 | if style == "overwrite": 304 | print("[Blender Manager] Autosaving (overwrite)...") 305 | bpy.ops.wm.save_as_mainfile(filepath=base_path) 306 | elif style == "separate": 307 | counter = autosave_settings["separate_counter"] 308 | save_path = os.path.join(project_dir, f"{project_name}_{counter}.blend") 309 | print(f"[Blender Manager] Autosaving (separate) to {save_path}...") 310 | bpy.ops.wm.save_as_mainfile(filepath=save_path) 311 | autosave_settings["separate_counter"] += 1 312 | 313 | # Schedule the next autosave 314 | return interval 315 | 316 | bpy.app.timers.register(autosave, first_interval=autosave_settings["auto_save_interval"]) 317 | 318 | 319 | def register(): 320 | bpy.app.timers.register(check_for_settings_file) 321 | 322 | 323 | if __name__ == "__main__": 324 | register() 325 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | 116 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 117 | enforcement ladder](https://github.com/mozilla/diversity). 118 | 119 | [homepage]: https://www.contributor-covenant.org 120 | 121 | For answers to common questions about this code of conduct, see the FAQ at 122 | https://www.contributor-covenant.org/faq. Translations are available at 123 | https://www.contributor-covenant.org/translations. 124 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### **Contributing to Blender Manager** 2 | 3 | Thank you for considering contributing to Blender Manager! Contributions are welcome and appreciated. Here’s how you can help: 4 | 5 | --- 6 | 7 | ### **How to Contribute** 8 | 1. **Report Bugs** 9 | Found a bug? Open an issue on the [GitHub Issues page](https://github.com/verlorengest/BlenderManager/issues) with detailed steps to reproduce it. 10 | 11 | 2. **Request Features** 12 | Have a feature idea? Submit a feature request by creating an issue. 13 | 14 | 3. **Submit Code Changes** 15 | - Fork the repository. 16 | - Create a new branch for your changes. 17 | - Make your changes and test them. 18 | - Submit a pull request with a clear description of your changes. 19 | 20 | 4. **Improve Documentation** 21 | Help us keep the documentation clear and up to date by submitting edits or new content. 22 | 23 | --- 24 | 25 | ### **Code of Conduct** 26 | Please follow our [Code of Conduct](./CODE_OF_CONDUCT.md) to ensure a welcoming environment for all contributors. 27 | 28 | --- 29 | 30 | ### **Questions?** 31 | If you have any questions, feel free to open a discussion or issue, and we’ll be happy to help! 32 | 33 | Happy coding! 🎉 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Blender Manager License 2 | This software is licensed under the following terms: 3 | 4 | NonCommercial Use Only: 5 | 6 | This software is licensed for non-commercial use only. You may not use this software for any commercial purpose, including but not limited to selling, licensing, or integrating it into commercial products or services. 7 | 8 | Attribution: 9 | 10 | Any distribution or usage of this software, or any derivative works, must include clear attribution to the verlorengest. You may not claim ownership of this software or its derivatives. 11 | 12 | No Commercial Distribution: 13 | 14 | Any modified versions of this software must be freely available under the same terms as this license. You may not distribute modified versions for commercial purposes or charge any fees for access to the modified software. 15 | 16 | No Warranty: 17 | 18 | This software is provided "as is," without any warranty of any kind, express or implied. The developer is not liable for any damages arising from the use of this software. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blender Manager 2 | 3 | ![bmanager1](https://github.com/user-attachments/assets/8f9f6104-29c1-405b-b0f4-9516470f7231) 4 | 5 | 6 | # Purpose 7 | 8 | Blender Manager is an open-source tool for keep your Blender updated, organized and clean. 9 | 10 | 11 | --- 12 | 13 |
14 | 📋 Screenshots 15 | 16 | ![1](https://github.com/user-attachments/assets/97cfd485-9be2-42ab-ad28-adf1b9282437) 17 | 18 | ![1 5](https://github.com/user-attachments/assets/f8d9634f-9d74-4317-9b28-c7606caca572) 19 | 20 | ![2](https://github.com/user-attachments/assets/f507a55a-55c9-48ed-9df9-3267f454ea41) 21 | 22 | ![3](https://github.com/user-attachments/assets/fd72d7ef-4dd0-4d69-8edf-70231bd736f2) 23 | 24 | ![4](https://github.com/user-attachments/assets/8d856750-9358-4f2b-9ed4-d9d81e0c687e) 25 | 26 | ![5](https://github.com/user-attachments/assets/7325f458-e405-41d5-a225-7df15ff6b1f1) 27 | 28 | ![6](https://github.com/user-attachments/assets/e388930a-c6df-4d6e-977b-c1509e2e734f) 29 | 30 | ![7](https://github.com/user-attachments/assets/8eb8e308-550b-490f-8195-4fc44250c4f0) 31 | 32 | ![8](https://github.com/user-attachments/assets/918379e2-f83b-44d0-bcb8-aa92d44fa13c) 33 | 34 | ![9](https://github.com/user-attachments/assets/d2133532-b858-41e9-8471-1fafbf89131f) 35 | 36 | ![10](https://github.com/user-attachments/assets/959458f4-b859-49f9-aeda-740bf227306a) 37 | 38 | Selected Font: SimHei 39 | 40 | 41 | 42 |
43 | 44 |
45 | 📋 Core Features 46 | 47 | 48 | ## **1) Main Menu** 49 | 50 | - **Launch Blender**: Start your main Blender version, export it, or delete it with a click. 51 | - **Create Project**: Create a new project with reference images, a base mesh, and custom startup settings. 52 | - **Check Updates**: Check for the latest Blender version and update if needed. 53 | - **Preferences**: Customize the Blender Manager interface theme, fonts, sizes, and transparency. 54 | - **General Settings**: Configure general options like addon setup, auto-updates, launch behavior, and data reset. 55 | - **Blender Settings**: Export, import, or transfer settings between Blender versions. 56 | - **Recent Projects**: View, open, or delete recently accessed projects and check time spent. 57 | - **Help Section**: Access documentation, contributor credits, and donation options. 58 | - **Version Info**: View installed versions and update if an outdated version is detected. 59 | 60 | --- 61 | 62 | ## **2) Addon Management** 63 | 64 | - **Add Addon**: Import Blender addons from your computer. 65 | - **Refresh**: Update the addon list after changes. 66 | - **Version Selection**: Choose a Blender version to view its addons. 67 | - **Addon List & Right-Click**: Manage addons by deleting, duplicating, activating, deactivating, or viewing info and documentation. 68 | 69 | --- 70 | 71 | ## **3) Project Management** 72 | 73 | - **Add Project**: Import existing Blender projects into the manager. 74 | - **Refresh**: Refresh the project list to reflect recent changes. 75 | - **Project List**: Organize, drag and drop, and manage your project hierarchy. 76 | - **Project Info & Right-Click**: Rename, open, move, delete, export, or view project details and previews. 77 | 78 | --- 79 | 80 | ## **4) Version Management** 81 | 82 | - **OS & Architecture**: Select your operating system and architecture for compatibility. 83 | - **Get Versions**: List official stable or experimental Blender releases. 84 | - **Install**: Download and install selected Blender versions. 85 | - **Release Notes**: Read about new features and updates in each release. 86 | - **Installed Versions**: View, refresh, create shortcuts, or delete installed Blender versions. 87 | - **Buttons**: Launch, open with factory settings, or set as the main version. 88 | 89 | --- 90 | 91 | ## **5) Render Management** 92 | 93 | - **Render List**: Display renders with file size, resolution, and modification date. 94 | - **Browse**: Import render files from your computer. 95 | - **Open**: Preview selected renders. 96 | - **Refresh**: Update the render list after changes. 97 | - **Delete**: Permanently delete selected render files from your system. 98 | - **Render Notes**: Add personal notes or comments to your renders. 99 | 100 |
101 | 102 | 103 | 104 |
105 | 🛠️ Installation Guide 106 | 107 | Follow these steps to install and set up Blender Manager on your system. 108 | 109 | --- 110 | 111 | ### **Step 1: Download and Extract the ZIP File** 112 | 113 | 1. **Download the Blender Manager ZIP file** 114 | 📥 [**Download Latest Release**](https://github.com/verlorengest/BlenderManager/releases) 115 | 116 | 2. **Extract the ZIP file** to a location of your choice: 117 | - Right-click the ZIP file and select **"Extract All"** or use a tool like **WinRAR** or **7-Zip**. 118 | - After extraction, you’ll find a folder named **"BlenderManager"**. 119 | 120 | --- 121 | 122 | ### **Step 2: Launch Blender Manager** 123 | 124 | 1. Open the **BlenderManager** folder. 125 | 2. Double-click on **`blender_manager.exe`** to start the application. 126 | - If a security prompt appears, click **"Run Anyway"**. 127 | 3. Install Blender by clicking Launch Blender in Main Menu 128 | 4. Select the option which suits you. 129 | 5. Note: If the Blender Manager addon doesn't appear in the Preferences or Addon Management tab, go to Settings -> Setup Addon or try installing it manually. 130 | 131 | --- 132 | 133 | 134 | ### 🎉 **You're All Set!** 135 | 136 | Blender Manager is now installed and ready to enhance your Blender workflow. Enjoy streamlined project management and efficient tool integration! 137 | 138 | --- 139 | 140 | 141 | # How to Run BlenderManager from Source 142 | 143 | Follow the instructions below to clone, set up, and run **BlenderManager** from the source code. Ensure you have Python installed on your system (version 3.10 or higher is recommended). 144 | 145 | --- 146 | 147 | ## Prerequisites 148 | 149 | 1. **Python Installation**: Ensure Python 3.10+ is installed and added to your system's PATH. You can download Python from the [official Python website](https://www.python.org/downloads/). 150 | 151 | 2. **Git Installation**: Ensure Git is installed on your system. You can download Git from [here](https://git-scm.com/downloads). 152 | 153 | --- 154 | 155 | ## Steps to Run the Project 156 | 157 | ### Step 1: Clone the Repository 158 | Use the following command to clone the BlenderManager repository to your local machine: 159 | ```bash 160 | git clone https://github.com/verlorengest/BlenderManager.git 161 | ``` 162 | 163 | Navigate to the project directory: 164 | ```bash 165 | cd BlenderManager 166 | ``` 167 | 168 | ### Step 2: Install Dependencies 169 | Create a virtual environment (optional but recommended): 170 | ```bash 171 | python -m venv venv 172 | ``` 173 | 174 | Activate the virtual environment: 175 | - **Windows**: 176 | ```bash 177 | venv\Scripts\activate 178 | ``` 179 | - **macOS/Linux**: 180 | ```bash 181 | source venv/bin/activate 182 | ``` 183 | 184 | Install the required dependencies: 185 | ```bash 186 | pip install -r requirements.txt 187 | ``` 188 | 189 | ### Step 3: Run BlenderManager 190 | Run the application using the following command: 191 | ```bash 192 | python blender_manager.py 193 | ``` 194 | 195 | --- 196 | 197 | ## Additional Notes 198 | - Ensure you have **Blender** installed or configure the application to detect an existing Blender installation. If Blender is not installed, the app will prompt you to install it. 199 | - The application may require elevated permissions to access certain directories or system settings, depending on your operating system. 200 | - **themes** Folder should be inside of \Lib\site-packages\ttkbootstrap 201 | - For further assistance or issues, please open a ticket on the [GitHub Issues Page](https://github.com/verlorengest/BlenderManager/issues). 202 | 203 | --- 204 | 205 | 206 | 207 |
208 | 209 | 210 | 211 | 212 | **⚠️ Note: This is a pre-release version.** 213 | 214 | This version may contain bugs and issues as it is still in pre-release. Your feedback is crucial in helping us improve the application. If you encounter any problems, please don’t hesitate to open an issue and let us know! 215 | 216 | 217 | ## ❣️ Show Some Love 218 | 219 | Blender Manager is free and open-source. If you find it helpful, consider supporting the project: 220 | 221 | - [**Gumroad**](https://kaansoyler.gumroad.com/l/blendermanager) 222 | - [**Buy Me a Coffee**](https://buymeacoffee.com/verlorengest) ☕ 223 | 224 | 225 | --- 226 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ### **Security Policy for Blender Manager** 2 | 3 | 4 | ### **1. Reporting Security Issues** 5 | If you find a security issue in Blender Manager, please report it privately to **[Developer](mailto:kaansoyler@proton.me)**. 6 | 7 | To help us address the issue quickly, include: 8 | - A description of the issue. 9 | - Steps to reproduce it. 10 | - Any suggestions for fixing it. 11 | 12 | We’ll confirm receipt within 48 hours and aim to resolve critical issues within 14 days. 13 | 14 | --- 15 | 16 | -------------------------------------------------------------------------------- /docs/WELCOME.md: -------------------------------------------------------------------------------- 1 | # Welcome to Blender Manager 🎉 2 | 3 | Hey there, Blender enthusiast! Ready to level up your workflow? Welcome to **Blender Manager**, your new best friend for handling all things Blender. From juggling multiple versions to keeping your projects and addons in check, this tool’s got your back. Let’s get started. 🚀 4 | 5 | --- 6 | 7 | ## What’s Blender Manager? 8 | 9 | It’s like a command center for your Blender setup. Want to: 10 | 11 | - Switch between Blender versions like a pro? ✅ 12 | - Organize your messy projects into a neat library? ✅ 13 | - Manage addons without diving into Blender every time? Oh, you bet. ✅ 14 | 15 | Yeah, Blender Manager does all that and more. 16 | 17 | --- 18 | 19 | ## Why You’ll Love It 💖 20 | 21 | Here’s the deal: Blender Manager takes the boring, repetitive tasks and makes them *easy*. No more hunting for the right Blender version or manually transferring addons. Just click a few buttons, and boom, you’re good to go. 22 | 23 | Here’s a taste of what it does: 24 | 25 | - **Version Management**: Install, update, and switch Blender versions with zero hassle. 26 | - **Project Management**: Keep all your blend files tidy and easy to access. 27 | - **Addon Control**: Activate, deactivate, or move addons between versions. Simple as that. 28 | - **Render Hub**: Preview, organize, and add notes to your renders. 29 | 30 | And yes, it looks slick while doing it. 🕶️ 31 | -------------------------------------------------------------------------------- /source/Assets/Images/bmicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/Assets/Images/bmicon.ico -------------------------------------------------------------------------------- /source/Assets/Images/bmng.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/Assets/Images/bmng.ico -------------------------------------------------------------------------------- /source/Blender_Manager_Addon.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/Blender_Manager_Addon.zip -------------------------------------------------------------------------------- /source/blendermanager.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/blendermanager.ico -------------------------------------------------------------------------------- /source/requirements.txt: -------------------------------------------------------------------------------- 1 | ttkbootstrap 2 | tkinterdnd2 3 | pystray 4 | Pillow 5 | beautifulsoup4 6 | pywebview 7 | psutil 8 | pywin32 9 | requests 10 | -------------------------------------------------------------------------------- /source/themes/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/themes/__init__.py -------------------------------------------------------------------------------- /source/themes/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/themes/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /source/themes/__pycache__/standard.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/themes/__pycache__/standard.cpython-310.pyc -------------------------------------------------------------------------------- /source/themes/__pycache__/user.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/themes/__pycache__/user.cpython-310.pyc -------------------------------------------------------------------------------- /source/themes/standard.py: -------------------------------------------------------------------------------- 1 | STANDARD_THEMES = { 2 | "cosmo": { 3 | "type": "light", 4 | "colors": { 5 | "primary": "#2780e3", 6 | "secondary": "#7E8081", 7 | "success": "#3fb618", 8 | "info": "#9954bb", 9 | "warning": "#ff7518", 10 | "danger": "#ff0039", 11 | "light": "#F8F9FA", 12 | "dark": "#373A3C", 13 | "bg": "#ffffff", 14 | "fg": "#373a3c", 15 | "selectbg": "#7e8081", 16 | "selectfg": "#ffffff", 17 | "border": "#ced4da", 18 | "inputfg": "#373a3c", 19 | "inputbg": "#fdfdfe", 20 | "active": "#efefef", 21 | }, 22 | }, 23 | "flatly": { 24 | "type": "light", 25 | "colors": { 26 | "primary": "#2c3e50", 27 | "secondary": "#95a5a6", 28 | "success": "#18bc9c", 29 | "info": "#3498db", 30 | "warning": "#f39c12", 31 | "danger": "#e74c3c", 32 | "light": "#ECF0F1", 33 | "dark": "#7B8A8B", 34 | "bg": "#ffffff", 35 | "fg": "#212529", 36 | "selectbg": "#95a5a6", 37 | "selectfg": "#ffffff", 38 | "border": "#ced4da", 39 | "inputfg": "#212529", 40 | "inputbg": "#ffffff", 41 | "active": "#e2e2e2", 42 | }, 43 | }, 44 | "litera": { 45 | "type": "light", 46 | "colors": { 47 | "primary": "#4582ec", 48 | "secondary": "#adb5bd", 49 | "success": "#02b875", 50 | "info": "#17a2b8", 51 | "warning": "#f0ad4e", 52 | "danger": "#d9534f", 53 | "light": "#F8F9FA", 54 | "dark": "#343A40", 55 | "bg": "#ffffff", 56 | "fg": "#343a40", 57 | "selectbg": "#adb5bd", 58 | "selectfg": "#ffffff", 59 | "border": "#bfbfbf", 60 | "inputfg": "#343a40", 61 | "inputbg": "#fff", 62 | "active": "#e5e5e5", 63 | }, 64 | }, 65 | "minty": { 66 | "type": "light", 67 | "colors": { 68 | "primary": "#78c2ad", 69 | "secondary": "#f3969a", 70 | "success": "#56cc9d", 71 | "info": "#6cc3d5", 72 | "warning": "#ffce67", 73 | "danger": "#ff7851", 74 | "light": "#F8F9FA", 75 | "dark": "#343A40", 76 | "bg": "#ffffff", 77 | "fg": "#5a5a5a", 78 | "selectbg": "#f3969a", 79 | "selectfg": "#ffffff", 80 | "border": "#ced4da", 81 | "inputfg": "#696969", 82 | "inputbg": "#fff", 83 | "active": "#e5e5e5", 84 | }, 85 | }, 86 | "lumen": { 87 | "type": "light", 88 | "colors": { 89 | "primary": "#158cba", 90 | "secondary": "#919191", 91 | "success": "#28b62c", 92 | "info": "#75caeb", 93 | "warning": "#ff851b", 94 | "danger": "#ff4136", 95 | "light": "#F6F6F6", 96 | "dark": "#555555", 97 | "bg": "#ffffff", 98 | "fg": "#555555", 99 | "selectbg": "#919191", 100 | "selectfg": "#ffffff", 101 | "border": "#ced4da", 102 | "inputfg": "#555555", 103 | "inputbg": "#fff", 104 | "active": "#e5e5e5", 105 | }, 106 | }, 107 | "sandstone": { 108 | "type": "light", 109 | "colors": { 110 | "primary": "#325D88", 111 | "secondary": "#8e8c84", 112 | "success": "#93c54b", 113 | "info": "#29abe0", 114 | "warning": "#f47c3c", 115 | "danger": "#d9534f", 116 | "light": "#F8F5F0", 117 | "dark": "#3E3F3A", 118 | "bg": "#ffffff", 119 | "fg": "#3e3f3a", 120 | "selectbg": "#8e8c84", 121 | "selectfg": "#ffffff", 122 | "border": "#ced4da", 123 | "inputfg": "#6E6D69", 124 | "inputbg": "#fff", 125 | "active": "#e5e5e5", 126 | }, 127 | }, 128 | "yeti": { 129 | "type": "light", 130 | "colors": { 131 | "primary": "#008cba", 132 | "secondary": "#707070", 133 | "success": "#43ac6a", 134 | "info": "#5bc0de", 135 | "warning": "#e99002", 136 | "danger": "#f04124", 137 | "light": "#EEEEEE", 138 | "dark": "#222222", 139 | "bg": "#ffffff", 140 | "fg": "#222222", 141 | "selectbg": "#707070", 142 | "selectfg": "#ffffff", 143 | "border": "#cccccc", 144 | "inputfg": "#222222", 145 | "inputbg": "#fff", 146 | "active": "#e5e5e5", 147 | }, 148 | }, 149 | "pulse": { 150 | "type": "light", 151 | "colors": { 152 | "primary": "#593196", 153 | "secondary": "#69676E", 154 | "success": "#13b955", 155 | "info": "#009cdc", 156 | "warning": "#efa31d", 157 | "danger": "#fc3939", 158 | "light": "#F9F8FC", 159 | "dark": "#17141F", 160 | "bg": "#ffffff", 161 | "fg": "#444444", 162 | "selectbg": "#69676e", 163 | "selectfg": "#ffffff", 164 | "border": "#cbc8d0", 165 | "inputfg": "#444444", 166 | "inputbg": "#fdfdfe", 167 | "active": "#e5e5e5", 168 | }, 169 | }, 170 | "united": { 171 | "type": "light", 172 | "colors": { 173 | "primary": "#e95420", 174 | "secondary": "#aea79f", 175 | "success": "#38b44a", 176 | "info": "#17a2b8", 177 | "warning": "#efb73e", 178 | "danger": "#df382c", 179 | "light": "#E9ECEF", 180 | "dark": "#772953", 181 | "bg": "#ffffff", 182 | "fg": "#333333", 183 | "selectbg": "#aea79f", 184 | "selectfg": "#ffffff", 185 | "border": "#ced4da", 186 | "inputfg": "#333333", 187 | "inputbg": "#fff", 188 | "active": "#e5e5e5", 189 | }, 190 | }, 191 | "morph": { 192 | "type": "light", 193 | "colors": { 194 | "primary": "#378DFC", 195 | "secondary": "#aaaaaa", 196 | "success": "#43cc29", 197 | "info": "#5B62F4", 198 | "warning": "#FFC107", 199 | "danger": "#E52527", 200 | "light": "#F0F5FA", 201 | "dark": "#212529", 202 | "bg": "#D9E3F1", 203 | "fg": "#7B8AB8", 204 | "selectbg": "#aaaaaa", 205 | "selectfg": "#FBFDFF", 206 | "border": "#B9C7DA", 207 | "inputfg": "#7F8EBA", 208 | "inputbg": "#F0F5FA", 209 | "active": "#C3CCD8", 210 | }, 211 | }, 212 | "journal": { 213 | "type": "light", 214 | "colors": { 215 | "primary": "#eb6864", 216 | "secondary": "#aaaaaa", 217 | "success": "#22b24c", 218 | "info": "#336699", 219 | "warning": "#f5e625", 220 | "danger": "#f57a00", 221 | "light": "#F8F9FA", 222 | "dark": "#222222", 223 | "bg": "#ffffff", 224 | "fg": "#222222", 225 | "selectbg": "#aaaaaa", 226 | "selectfg": "#ffffff", 227 | "border": "#ced4da", 228 | "inputfg": "#565656", 229 | "inputbg": "#fff", 230 | "active": "#e5e5e5", 231 | }, 232 | }, 233 | 234 | 235 | 236 | 237 | 238 | "disgusting": { 239 | "type": "light", 240 | "colors": { 241 | "primary": "#ff007f", 242 | "secondary": "#3d3dff", 243 | "success": "#00ff00", 244 | "info": "#ff5733", 245 | "warning": "#ffd700", 246 | "danger": "#ff1493", 247 | "light": "#ffffff", 248 | "dark": "#000000", 249 | "bg": "#0f0f0f", 250 | "fg": "#00ffff", 251 | "selectbg": "#8a2be2", 252 | "selectfg": "#ffcc00", 253 | "border": "#ff69b4", 254 | "inputfg": "#32cd32", 255 | "inputbg": "#4b0082", 256 | "active": "#ff4500" 257 | }, 258 | }, 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | "modern": { 267 | "type": "light", 268 | "colors": { 269 | "primary": "#1a73e8", 270 | "secondary": "#6c757d", 271 | "success": "#34a853", 272 | "info": "#2196f3", 273 | "warning": "#fbbc05", 274 | "danger": "#ea4335", 275 | "light": "#f5f5f5", 276 | "dark": "#202124", 277 | "bg": "#ffffff", 278 | "fg": "#3c4043", 279 | "selectbg": "#e8f0fe", 280 | "selectfg": "#202124", 281 | "border": "#dadce0", 282 | "inputfg": "#3c4043", 283 | "inputbg": "#f1f3f4", 284 | "active": "#e8eaed" 285 | }, 286 | }, 287 | 288 | 289 | 290 | 291 | "neon_glow": { 292 | "type": "dark", 293 | "colors": { 294 | "primary": "#ff006e", 295 | "secondary": "#8338ec", 296 | "success": "#3a86ff", 297 | "info": "#06d6a0", 298 | "warning": "#ffbe0b", 299 | "danger": "#ef233c", 300 | "light": "#8d99ae", 301 | "dark": "#1a1b27", 302 | "bg": "#121212", 303 | "fg": "#f1faee", 304 | "selectbg": "#ff006e", 305 | "selectfg": "#ffffff", 306 | "border": "#2b2d42", 307 | "inputfg": "#ffffff", 308 | "inputbg": "#2a2a35", 309 | "active": "#6a00f4" 310 | }, 311 | }, 312 | "retro_wave": { 313 | "type": "dark", 314 | "colors": { 315 | "primary": "#ff4d00", 316 | "secondary": "#7d00ff", 317 | "success": "#00ff99", 318 | "info": "#00c6ff", 319 | "warning": "#ffd700", 320 | "danger": "#ff007a", 321 | "light": "#ffd3e3", 322 | "dark": "#2d2d2d", 323 | "bg": "#0d0f20", 324 | "fg": "#e3e3e3", 325 | "selectbg": "#7d00ff", 326 | "selectfg": "#ffffff", 327 | "border": "#555555", 328 | "inputfg": "#d1d1d1", 329 | "inputbg": "#262626", 330 | "active": "#ff007a" 331 | }, 332 | }, 333 | "pastel_dream": { 334 | "type": "light", 335 | "colors": { 336 | "primary": "#ffb6c1", 337 | "secondary": "#b19cd9", 338 | "success": "#ace1af", 339 | "info": "#add8e6", 340 | "warning": "#ffe4b5", 341 | "danger": "#f08080", 342 | "light": "#f8f8ff", 343 | "dark": "#202020", 344 | "bg": "#fafafa", 345 | "fg": "#404040", 346 | "selectbg": "#b19cd9", 347 | "selectfg": "#fafafa", 348 | "border": "#dcdcdc", 349 | "inputfg": "#404040", 350 | "inputbg": "#f5f5f5", 351 | "active": "#f08080" 352 | }, 353 | }, 354 | "minimal_modern": { 355 | "type": "light", 356 | "colors": { 357 | "primary": "#0077b6", 358 | "secondary": "#0096c7", 359 | "success": "#02c39a", 360 | "info": "#00b4d8", 361 | "warning": "#ffba08", 362 | "danger": "#ef233c", 363 | "light": "#e9ecef", 364 | "dark": "#343a40", 365 | "bg": "#f8f9fa", 366 | "fg": "#212529", 367 | "selectbg": "#0077b6", 368 | "selectfg": "#ffffff", 369 | "border": "#dee2e6", 370 | "inputfg": "#212529", 371 | "inputbg": "#e9ecef", 372 | "active": "#00b4d8" 373 | }, 374 | }, 375 | 376 | 377 | 378 | "pure_black": { 379 | "type": "dark", 380 | "colors": { 381 | "primary": "#202020", 382 | "secondary": "#333333", 383 | "success": "#00ff00", 384 | "info": "#007bff", 385 | "warning": "#ffcc00", 386 | "danger": "#ff3333", 387 | "light": "#272727", 388 | "dark": "#000000", 389 | "bg": "#000000", 390 | "fg": "#ffffff", 391 | "selectbg": "#444444", 392 | "selectfg": "#ffffff", 393 | "border": "#555555", 394 | "inputfg": "#ffffff", 395 | "inputbg": "#1a1a1a", 396 | "active": "#666666" 397 | }, 398 | }, 399 | 400 | "rainbow": { 401 | "type": "light", 402 | "colors": { 403 | "primary": "#ff0000", 404 | "secondary": "#ff7f00", 405 | "success": "#ffff00", 406 | "info": "#00ff00", 407 | "warning": "#0000ff", 408 | "danger": "#4b0082", 409 | "light": "#9400d3", 410 | "dark": "#000000", 411 | "bg": "#ffffff", 412 | "fg": "#000000", 413 | "selectbg": "#ffd700", 414 | "selectfg": "#000000", 415 | "border": "#ff69b4", 416 | "inputfg": "#000000", 417 | "inputbg": "#ffffff", 418 | "active": "#ff1493" 419 | }, 420 | }, 421 | 422 | 423 | 424 | 425 | "smoothie": { 426 | "type": "dark", 427 | "colors": { 428 | "primary": "#2b2b2b", 429 | "secondary": "#383838", 430 | "success": "#4caf50", 431 | "info": "#2196f3", 432 | "warning": "#ffc107", 433 | "danger": "#f44336", 434 | "light": "#e6e6e6", 435 | "dark": "#1e1e1e", 436 | "bg": "#1e1e1e", 437 | "fg": "#d9d9d9", 438 | "selectbg": "#4a4a4a", 439 | "selectfg": "#ffffff", 440 | "border": "#3a3a3a", 441 | "inputfg": "#ffffff", 442 | "inputbg": "#2b2b2b", 443 | "active": "#5e5e5e" 444 | }, 445 | }, 446 | 447 | "cream_pastel": { 448 | "type": "light", 449 | "colors": { 450 | "primary": "#f5e6cc", 451 | "secondary": "#f8f0e3", 452 | "success": "#d4a373", 453 | "info": "#f7c6a3", 454 | "warning": "#f4b183", 455 | "danger": "#e89a9a", 456 | "light": "#fffaf1", 457 | "dark": "#cbb091", 458 | "bg": "#fff8e8", 459 | "fg": "#5b4636", 460 | "selectbg": "#d4b48c", 461 | "selectfg": "#3e3227", 462 | "border": "#d6bea4", 463 | "inputfg": "#5b4636", 464 | "inputbg": "#f7e6d4", 465 | "active": "#bca085" 466 | }, 467 | }, 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | "midnight_blue": { 477 | "type": "dark", 478 | "colors": { 479 | "primary": "#0a1172", 480 | "secondary": "#1b1f3b", 481 | "success": "#163172", 482 | "info": "#26408b", 483 | "warning": "#3e66a1", 484 | "danger": "#172554", 485 | "light": "#f0f4fa", 486 | "dark": "#0a0e21", 487 | "bg": "#0e1621", 488 | "fg": "#d1d9e6", 489 | "selectbg": "#26408b", 490 | "selectfg": "#ffffff", 491 | "border": "#1b2735", 492 | "inputfg": "#d1d9e6", 493 | "inputbg": "#1a2331", 494 | "active": "#203a63" 495 | }, 496 | }, 497 | 498 | 499 | 500 | 501 | "baby_blue": { 502 | "type": "light", 503 | "colors": { 504 | "primary": "#a2d2ff", 505 | "secondary": "#caf0f8", 506 | "success": "#ade8f4", 507 | "info": "#90e0ef", 508 | "warning": "#48cae4", 509 | "danger": "#00b4d8", 510 | "light": "#f1f9ff", 511 | "dark": "#023e8a", 512 | "bg": "#e0f7ff", 513 | "fg": "#03045e", 514 | "selectbg": "#90e0ef", 515 | "selectfg": "#ffffff", 516 | "border": "#caf0f8", 517 | "inputfg": "#03045e", 518 | "inputbg": "#dbeeff", 519 | "active": "#48cae4" 520 | }, 521 | }, 522 | 523 | 524 | 525 | "blender": { 526 | "type": "dark", 527 | "colors": { 528 | "primary": "#d15b1f", 529 | "secondary": "#242424", 530 | "success": "#97b700", 531 | "info": "#64a2c8", 532 | "warning": "#e0aa29", 533 | "danger": "#b32929", 534 | "light": "#e5e5e5", 535 | "dark": "#191919", 536 | "bg": "#383838", 537 | "fg": "#e1e1e1", 538 | "selectbg": "#d15b1f", 539 | "selectfg": "#ffffff", 540 | "border": "#4d4d4d", 541 | "inputfg": "#f1f1f1", 542 | "inputbg": "#4d4d4d", 543 | "active": "#d97a37" 544 | }, 545 | }, 546 | 547 | 548 | 549 | "clown_fiesta": { 550 | "type": "light", 551 | "colors": { 552 | "primary": "#ff0000", 553 | "secondary": "#00ff00", 554 | "success": "#0000ff", 555 | "info": "#ffff00", 556 | "warning": "#ff00ff", 557 | "danger": "#00ffff", 558 | "light": "#ffffff", 559 | "dark": "#000000", 560 | "bg": "#ff8800", 561 | "fg": "#ff00aa", 562 | "selectbg": "#00ff88", 563 | "selectfg": "#0000ff", 564 | "border": "#ff00ff", 565 | "inputfg": "#ff8800", 566 | "inputbg": "#00ff00", 567 | "active": "#aa00ff" 568 | }, 569 | }, 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | "plasticity": { 579 | "type": "dark", 580 | "colors": { 581 | "primary": "#6c43b8", 582 | "secondary": "#1e1e1e", 583 | "success": "#5c5c5c", 584 | "info": "#2c2c2c", 585 | "warning": "#333333", 586 | "danger": "#b84d4d", 587 | "light": "#d6d6d6", 588 | "dark": "#141414", 589 | "bg": "#1c1c1c", 590 | "fg": "#e0e0e0", 591 | "selectbg": "#6c43b8", 592 | "selectfg": "#ffffff", 593 | "border": "#2a2a2a", 594 | "inputfg": "#f1f1f1", 595 | "inputbg": "#2c2c2c", 596 | "active": "#845ec2" 597 | }, 598 | }, 599 | 600 | 601 | 602 | "vomit_vibes": { 603 | "type": "dark", 604 | "colors": { 605 | "primary": "#6b8e23", 606 | "secondary": "#8b4513", 607 | "success": "#556b2f", 608 | "info": "#7f6000", 609 | "warning": "#999900", 610 | "danger": "#cc9966", 611 | "light": "#d2b48c", 612 | "dark": "#3b2f2f", 613 | "bg": "#2f4f4f", 614 | "fg": "#8b0000", 615 | "selectbg": "#556b2f", 616 | "selectfg": "#ffffe0", 617 | "border": "#bdb76b", 618 | "inputfg": "#7f6000", 619 | "inputbg": "#8b4513", 620 | "active": "#808000" 621 | }, 622 | }, 623 | 624 | 625 | 626 | "radioactive_swamp": { 627 | "type": "dark", 628 | "colors": { 629 | "primary": "#00ff00", 630 | "secondary": "#008000", 631 | "success": "#00cc00", 632 | "info": "#00b300", 633 | "warning": "#99ff33", 634 | "danger": "#33cc33", 635 | "light": "#66ff66", 636 | "dark": "#004d00", 637 | "bg": "#003300", 638 | "fg": "#ccffcc", 639 | "selectbg": "#00ff00", 640 | "selectfg": "#003300", 641 | "border": "#006600", 642 | "inputfg": "#33cc33", 643 | "inputbg": "#004d00", 644 | "active": "#00cc00" 645 | }, 646 | }, 647 | 648 | 649 | "painful_pink": { 650 | "type": "light", 651 | "colors": { 652 | "primary": "#ff69b4", 653 | "secondary": "#ff1493", 654 | "success": "#ff85c2", 655 | "info": "#ffc0cb", 656 | "warning": "#ff00ff", 657 | "danger": "#ff77aa", 658 | "light": "#fff0f5", 659 | "dark": "#ff007f", 660 | "bg": "#ffe4e1", 661 | "fg": "#ff1493", 662 | "selectbg": "#ff69b4", 663 | "selectfg": "#ffe4e1", 664 | "border": "#ffb6c1", 665 | "inputfg": "#ff69b4", 666 | "inputbg": "#ffe4e1", 667 | "active": "#ff85c2" 668 | }, 669 | }, 670 | 671 | "toxic_circus": { 672 | "type": "dark", 673 | "colors": { 674 | "primary": "#ff00ff", 675 | "secondary": "#00ffff", 676 | "success": "#ffcc00", 677 | "info": "#ff0000", 678 | "warning": "#00ff00", 679 | "danger": "#0000ff", 680 | "light": "#ff88ff", 681 | "dark": "#333333", 682 | "bg": "#6600ff", 683 | "fg": "#ffff00", 684 | "selectbg": "#ff00ff", 685 | "selectfg": "#00ffff", 686 | "border": "#ffcc00", 687 | "inputfg": "#ff0000", 688 | "inputbg": "#ff8800", 689 | "active": "#ff0066" 690 | }, 691 | }, 692 | 693 | 694 | 695 | 696 | "darkly": { 697 | "type": "dark", 698 | "colors": { 699 | "primary": "#373737", 700 | "secondary": "#444444", 701 | "success": "#00bc8c", 702 | "info": "#3498db", 703 | "warning": "#f39c12", 704 | "danger": "#e74c3c", 705 | "light": "#ADB5BD", 706 | "dark": "#303030", 707 | "bg": "#222222", 708 | "fg": "#ffffff", 709 | "selectbg": "#555555", 710 | "selectfg": "#ffffff", 711 | "border": "#222222", 712 | "inputfg": "#ffffff", 713 | "inputbg": "#2f2f2f", 714 | "active": "#1F1F1F", 715 | }, 716 | }, 717 | "superhero": { 718 | "type": "dark", 719 | "colors": { 720 | "primary": "#4c9be8", 721 | "secondary": "#4e5d6c", 722 | "success": "#5cb85c", 723 | "info": "#5bc0de", 724 | "warning": "#f0ad4e", 725 | "danger": "#d9534f", 726 | "light": "#ABB6C2", 727 | "dark": "#20374C", 728 | "bg": "#2b3e50", 729 | "fg": "#ffffff", 730 | "selectbg": "#526170", 731 | "selectfg": "#ffffff", 732 | "border": "#222222", 733 | "inputfg": "#ebebeb", 734 | "inputbg": "#32465a", 735 | "active": "#2B4155", 736 | }, 737 | }, 738 | "solar": { 739 | "type": "dark", 740 | "colors": { 741 | "primary": "#bc951a", 742 | "secondary": "#94a2a4", 743 | "success": "#44aca4", 744 | "info": "#3f98d7", 745 | "warning": "#d05e2f", 746 | "danger": "#d95092", 747 | "light": "#A9BDBD", 748 | "dark": "#073642", 749 | "bg": "#002B36", 750 | "fg": "#ffffff", 751 | "selectbg": "#0b5162", 752 | "selectfg": "#ffffff", 753 | "border": "#00252e", 754 | "inputfg": "#A9BDBD", 755 | "inputbg": "#073642", 756 | "active": "#002730", 757 | }, 758 | }, 759 | "cyborg": { 760 | "type": "dark", 761 | "colors": { 762 | "primary": "#2a9fd6", 763 | "secondary": "#555555", 764 | "success": "#77b300", 765 | "info": "#9933cc", 766 | "warning": "#ff8800", 767 | "danger": "#cc0000", 768 | "light": "#ADAFAE", 769 | "dark": "#222222", 770 | "bg": "#060606", 771 | "fg": "#ffffff", 772 | "selectbg": "#454545", 773 | "selectfg": "#ffffff", 774 | "border": "#060606", 775 | "inputfg": "#ffffff", 776 | "inputbg": "#191919", 777 | "active": "#282828", 778 | }, 779 | }, 780 | "vapor": { 781 | "type": "dark", 782 | "colors": { 783 | "primary": "#6e40c0", 784 | "secondary": "#ea38b8", 785 | "success": "#3af180", 786 | "info": "#1da2f2", 787 | "warning": "#ffbd05", 788 | "danger": "#e34b54", 789 | "light": "#44d7e8", 790 | "dark": "#170229", 791 | "bg": "#190831", 792 | "fg": "#32fbe2", 793 | "selectbg": "#461a8a", 794 | "selectfg": "#ffffff", 795 | "border": "#060606", 796 | "inputfg": "#bfb6cd", 797 | "inputbg": "#30115e", 798 | "active": "#17082E", 799 | }, 800 | }, 801 | "simplex": { 802 | "type": "light", 803 | "colors": { 804 | "primary": "#d8220e", 805 | "secondary": "#858e96", 806 | "success": "#469307", 807 | "info": "#0099ce", 808 | "warning": "#d88220", 809 | "danger": "#9a479e", 810 | "light": "#f2f2f2", 811 | "dark": "#3b3d3f", 812 | "bg": "#fcfcfc", 813 | "fg": "#3b3d3f", 814 | "selectbg": "#a9afb6", 815 | "selectfg": "#ffffff", 816 | "border": "#858e96", 817 | "inputfg": "#3b3d3f", 818 | "inputbg": "#fcfcfc", 819 | "active": "#e2e2e2", 820 | }, 821 | }, 822 | "cerculean": { 823 | "type": "light", 824 | "colors": { 825 | "primary": "#4bb1ea", 826 | "secondary": "#a9b4be", 827 | "success": "#84b251", 828 | "info": "#225384", 829 | "warning": "#e16e25", 830 | "danger": "#cf3c40", 831 | "light": "#eceef1", 832 | "dark": "#33383e", 833 | "bg": "#ffffff", 834 | "fg": "#2ea4e7", 835 | "selectbg": "#adb5bd", 836 | "selectfg": "#ffffff", 837 | "border": "#a9b4be", 838 | "inputfg": "#495057", 839 | "inputbg": "#ffffff", 840 | "active": "#e5e5e5", 841 | }, 842 | }, 843 | } 844 | -------------------------------------------------------------------------------- /source/themes/user.py: -------------------------------------------------------------------------------- 1 | USER_THEMES={} -------------------------------------------------------------------------------- /source/tkdnd2.8/pkgIndex.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # Tcl package index file 3 | # 4 | package ifneeded tkdnd 2.8 \ 5 | "source \{$dir/tkdnd.tcl\} ; \ 6 | tkdnd::initialise \{$dir\} tkdnd28.dll tkdnd" 7 | -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tkdnd.tcl -- 3 | # 4 | # This file implements some utility procedures that are used by the TkDND 5 | # package. 6 | # 7 | # This software is copyrighted by: 8 | # George Petasis, National Centre for Scientific Research "Demokritos", 9 | # Aghia Paraskevi, Athens, Greece. 10 | # e-mail: petasis@iit.demokritos.gr 11 | # 12 | # The following terms apply to all files associated 13 | # with the software unless explicitly disclaimed in individual files. 14 | # 15 | # The authors hereby grant permission to use, copy, modify, distribute, 16 | # and license this software and its documentation for any purpose, provided 17 | # that existing copyright notices are retained in all copies and that this 18 | # notice is included verbatim in any distributions. No written agreement, 19 | # license, or royalty fee is required for any of the authorized uses. 20 | # Modifications to this software may be copyrighted by their authors 21 | # and need not follow the licensing terms described here, provided that 22 | # the new terms are clearly indicated on the first page of each file where 23 | # they apply. 24 | # 25 | # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 26 | # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 27 | # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 28 | # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 32 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 33 | # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 34 | # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 35 | # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 36 | # MODIFICATIONS. 37 | # 38 | 39 | package require Tk 40 | 41 | namespace eval ::tkdnd { 42 | variable _topw ".drag" 43 | variable _tabops 44 | variable _state 45 | variable _x0 46 | variable _y0 47 | variable _platform_namespace 48 | variable _drop_file_temp_dir 49 | variable _auto_update 1 50 | 51 | variable _windowingsystem 52 | 53 | bind TkDND_Drag1 {tkdnd::_begin_drag press 1 %W %s %X %Y %x %y} 54 | bind TkDND_Drag1 {tkdnd::_begin_drag motion 1 %W %s %X %Y %x %y} 55 | bind TkDND_Drag2 {tkdnd::_begin_drag press 2 %W %s %X %Y %x %y} 56 | bind TkDND_Drag2 {tkdnd::_begin_drag motion 2 %W %s %X %Y %x %y} 57 | bind TkDND_Drag3 {tkdnd::_begin_drag press 3 %W %s %X %Y %x %y} 58 | bind TkDND_Drag3 {tkdnd::_begin_drag motion 3 %W %s %X %Y %x %y} 59 | 60 | # ---------------------------------------------------------------------------- 61 | # Command tkdnd::initialise: Initialise the TkDND package. 62 | # ---------------------------------------------------------------------------- 63 | proc initialise { dir PKG_LIB_FILE PACKAGE_NAME} { 64 | variable _platform_namespace 65 | variable _drop_file_temp_dir 66 | variable _windowingsystem 67 | global env 68 | 69 | switch [tk windowingsystem] { 70 | x11 { 71 | set _windowingsystem x11 72 | } 73 | win32 - 74 | windows { 75 | set _windowingsystem windows 76 | } 77 | aqua { 78 | set _windowingsystem aqua 79 | } 80 | default { 81 | error "unknown Tk windowing system" 82 | } 83 | } 84 | 85 | ## Get User's home directory: We try to locate the proper path from a set of 86 | ## environmental variables... 87 | foreach var {HOME HOMEPATH USERPROFILE ALLUSERSPROFILE APPDATA} { 88 | if {[info exists env($var)]} { 89 | if {[file isdirectory $env($var)]} { 90 | set UserHomeDir $env($var) 91 | break 92 | } 93 | } 94 | } 95 | 96 | ## Should use [tk windowingsystem] instead of tcl platform array: 97 | ## OS X returns "unix," but that's not useful because it has its own 98 | ## windowing system, aqua 99 | ## Under windows we have to also combine HOMEDRIVE & HOMEPATH... 100 | if {![info exists UserHomeDir] && 101 | [string equal $_windowingsystem windows] && 102 | [info exists env(HOMEDRIVE)] && [info exists env(HOMEPATH)]} { 103 | if {[file isdirectory $env(HOMEDRIVE)$env(HOMEPATH)]} { 104 | set UserHomeDir $env(HOMEDRIVE)$env(HOMEPATH) 105 | } 106 | } 107 | ## Have we located the needed path? 108 | if {![info exists UserHomeDir]} { 109 | set UserHomeDir [pwd] 110 | } 111 | set UserHomeDir [file normalize $UserHomeDir] 112 | 113 | ## Try to locate a temporary directory... 114 | foreach var {TKDND_TEMP_DIR TEMP TMP} { 115 | if {[info exists env($var)]} { 116 | if {[file isdirectory $env($var)] && [file writable $env($var)]} { 117 | set _drop_file_temp_dir $env($var) 118 | break 119 | } 120 | } 121 | } 122 | if {![info exists _drop_file_temp_dir]} { 123 | foreach _dir [list "$UserHomeDir/Local Settings/Temp" \ 124 | "$UserHomeDir/AppData/Local/Temp" \ 125 | /tmp \ 126 | C:/WINDOWS/Temp C:/Temp C:/tmp \ 127 | D:/WINDOWS/Temp D:/Temp D:/tmp] { 128 | if {[file isdirectory $_dir] && [file writable $_dir]} { 129 | set _drop_file_temp_dir $_dir 130 | break 131 | } 132 | } 133 | } 134 | if {![info exists _drop_file_temp_dir]} { 135 | set _drop_file_temp_dir $UserHomeDir 136 | } 137 | set _drop_file_temp_dir [file native $_drop_file_temp_dir] 138 | 139 | source $dir/tkdnd_generic.tcl 140 | switch $_windowingsystem { 141 | x11 { 142 | source $dir/tkdnd_unix.tcl 143 | set _platform_namespace xdnd 144 | } 145 | win32 - 146 | windows { 147 | source $dir/tkdnd_windows.tcl 148 | set _platform_namespace olednd 149 | } 150 | aqua { 151 | source $dir/tkdnd_macosx.tcl 152 | set _platform_namespace macdnd 153 | } 154 | default { 155 | error "unknown Tk windowing system" 156 | } 157 | } 158 | load $dir/$PKG_LIB_FILE $PACKAGE_NAME 159 | source $dir/tkdnd_compat.tcl 160 | ${_platform_namespace}::initialise 161 | };# initialise 162 | 163 | proc GetDropFileTempDirectory { } { 164 | variable _drop_file_temp_dir 165 | return $_drop_file_temp_dir 166 | } 167 | proc SetDropFileTempDirectory { dir } { 168 | variable _drop_file_temp_dir 169 | set _drop_file_temp_dir $dir 170 | } 171 | 172 | };# namespace ::tkdnd 173 | 174 | # ---------------------------------------------------------------------------- 175 | # Command tkdnd::drag_source 176 | # ---------------------------------------------------------------------------- 177 | proc ::tkdnd::drag_source { mode path { types {} } { event 1 } } { 178 | set tags [bindtags $path] 179 | set idx [lsearch $tags "TkDND_Drag*"] 180 | switch -- $mode { 181 | register { 182 | if { $idx != -1 } { 183 | bindtags $path [lreplace $tags $idx $idx TkDND_Drag$event] 184 | } else { 185 | bindtags $path [concat $tags TkDND_Drag$event] 186 | } 187 | set types [platform_specific_types $types] 188 | set old_types [bind $path <>] 189 | foreach type $types { 190 | if {[lsearch $old_types $type] < 0} {lappend old_types $type} 191 | } 192 | bind $path <> $old_types 193 | } 194 | unregister { 195 | if { $idx != -1 } { 196 | bindtags $path [lreplace $tags $idx $idx] 197 | } 198 | } 199 | } 200 | };# tkdnd::drag_source 201 | 202 | # ---------------------------------------------------------------------------- 203 | # Command tkdnd::drop_target 204 | # ---------------------------------------------------------------------------- 205 | proc ::tkdnd::drop_target { mode path { types {} } } { 206 | variable _windowingsystem 207 | set types [platform_specific_types $types] 208 | switch -- $mode { 209 | register { 210 | switch $_windowingsystem { 211 | x11 { 212 | _register_types $path [winfo toplevel $path] $types 213 | } 214 | win32 - 215 | windows { 216 | _RegisterDragDrop $path 217 | bind $path {+ tkdnd::_RevokeDragDrop %W} 218 | } 219 | aqua { 220 | macdnd::registerdragwidget [winfo toplevel $path] $types 221 | } 222 | default { 223 | error "unknown Tk windowing system" 224 | } 225 | } 226 | set old_types [bind $path <>] 227 | set new_types {} 228 | foreach type $types { 229 | if {[lsearch -exact $old_types $type] < 0} {lappend new_types $type} 230 | } 231 | if {[llength $new_types]} { 232 | bind $path <> [concat $old_types $new_types] 233 | } 234 | } 235 | unregister { 236 | switch $_windowingsystem { 237 | x11 { 238 | } 239 | win32 - 240 | windows { 241 | _RevokeDragDrop $path 242 | } 243 | aqua { 244 | error todo 245 | } 246 | default { 247 | error "unknown Tk windowing system" 248 | } 249 | } 250 | bind $path <> {} 251 | } 252 | } 253 | };# tkdnd::drop_target 254 | 255 | # ---------------------------------------------------------------------------- 256 | # Command tkdnd::_begin_drag 257 | # ---------------------------------------------------------------------------- 258 | proc ::tkdnd::_begin_drag { event button source state X Y x y } { 259 | variable _x0 260 | variable _y0 261 | variable _state 262 | 263 | switch -- $event { 264 | press { 265 | set _x0 $X 266 | set _y0 $Y 267 | set _state "press" 268 | } 269 | motion { 270 | if { ![info exists _state] } { 271 | # This is just extra protection. There seem to be 272 | # rare cases where the motion comes before the press. 273 | return 274 | } 275 | if { [string equal $_state "press"] } { 276 | if { abs($_x0-$X) > 3 || abs($_y0-$Y) > 3 } { 277 | set _state "done" 278 | _init_drag $button $source $state $X $Y $x $y 279 | } 280 | } 281 | } 282 | } 283 | };# tkdnd::_begin_drag 284 | 285 | # ---------------------------------------------------------------------------- 286 | # Command tkdnd::_init_drag 287 | # ---------------------------------------------------------------------------- 288 | proc ::tkdnd::_init_drag { button source state rootX rootY X Y } { 289 | # Call the <> binding. 290 | set cmd [bind $source <>] 291 | if {[string length $cmd]} { 292 | set cmd [string map [list %W $source %X $rootX %Y $rootY %x $X %y $Y \ 293 | %S $state %e <> %A \{\} %% % \ 294 | %t [bind $source <>]] $cmd] 295 | set code [catch {uplevel \#0 $cmd} info options] 296 | switch -exact -- $code { 297 | 0 {} 298 | 3 - 4 { 299 | # FRINK: nocheck 300 | return 301 | } 302 | default { 303 | return -options $options $info 304 | } 305 | } 306 | 307 | set len [llength $info] 308 | if {$len == 3} { 309 | foreach { actions types _data } $info { break } 310 | set types [platform_specific_types $types] 311 | set data [list] 312 | foreach type $types { 313 | lappend data $_data 314 | } 315 | unset _data 316 | } elseif {$len == 2} { 317 | foreach { actions _data } $info { break } 318 | set data [list]; set types [list] 319 | foreach {t d} $_data { 320 | foreach t [platform_specific_types $t] { 321 | lappend types $t; lappend data $d 322 | } 323 | } 324 | unset _data t d 325 | } else { 326 | if {$len == 1 && [string equal [lindex $actions 0] "refuse_drop"]} { 327 | return 328 | } 329 | error "not enough items in the result of the <>\ 330 | event binding. Either 2 or 3 items are expected." 331 | } 332 | set action refuse_drop 333 | variable _windowingsystem 334 | switch $_windowingsystem { 335 | x11 { 336 | set action [xdnd::_dodragdrop $source $actions $types $data $button] 337 | } 338 | win32 - 339 | windows { 340 | set action [_DoDragDrop $source $actions $types $data $button] 341 | } 342 | aqua { 343 | set action [macdnd::dodragdrop $source $actions $types $data $button] 344 | } 345 | default { 346 | error "unknown Tk windowing system" 347 | } 348 | } 349 | ## Call _end_drag to notify the widget of the result of the drag 350 | ## operation... 351 | _end_drag $button $source {} $action {} $data {} $state $rootX $rootY $X $Y 352 | } 353 | };# tkdnd::_init_drag 354 | 355 | # ---------------------------------------------------------------------------- 356 | # Command tkdnd::_end_drag 357 | # ---------------------------------------------------------------------------- 358 | proc ::tkdnd::_end_drag { button source target action type data result 359 | state rootX rootY X Y } { 360 | set rootX 0 361 | set rootY 0 362 | # Call the <> binding. 363 | set cmd [bind $source <>] 364 | if {[string length $cmd]} { 365 | set cmd [string map [list %W $source %X $rootX %Y $rootY %x $X %y $Y %% % \ 366 | %S $state %e <> %A \{$action\}] $cmd] 367 | set info [uplevel \#0 $cmd] 368 | # if { $info != "" } { 369 | # variable _windowingsystem 370 | # foreach { actions types data } $info { break } 371 | # set types [platform_specific_types $types] 372 | # switch $_windowingsystem { 373 | # x11 { 374 | # error "dragging from Tk widgets not yet supported" 375 | # } 376 | # win32 - 377 | # windows { 378 | # set action [_DoDragDrop $source $actions $types $data $button] 379 | # } 380 | # aqua { 381 | # macdnd::dodragdrop $source $actions $types $data 382 | # } 383 | # default { 384 | # error "unknown Tk windowing system" 385 | # } 386 | # } 387 | # ## Call _end_drag to notify the widget of the result of the drag 388 | # ## operation... 389 | # _end_drag $button $source {} $action {} $data {} $state $rootX $rootY 390 | # } 391 | } 392 | };# tkdnd::_end_drag 393 | 394 | # ---------------------------------------------------------------------------- 395 | # Command tkdnd::platform_specific_types 396 | # ---------------------------------------------------------------------------- 397 | proc ::tkdnd::platform_specific_types { types } { 398 | variable _platform_namespace 399 | ${_platform_namespace}::platform_specific_types $types 400 | }; # tkdnd::platform_specific_types 401 | 402 | # ---------------------------------------------------------------------------- 403 | # Command tkdnd::platform_independent_types 404 | # ---------------------------------------------------------------------------- 405 | proc ::tkdnd::platform_independent_types { types } { 406 | variable _platform_namespace 407 | ${_platform_namespace}::platform_independent_types $types 408 | }; # tkdnd::platform_independent_types 409 | 410 | # ---------------------------------------------------------------------------- 411 | # Command tkdnd::platform_specific_type 412 | # ---------------------------------------------------------------------------- 413 | proc ::tkdnd::platform_specific_type { type } { 414 | variable _platform_namespace 415 | ${_platform_namespace}::platform_specific_type $type 416 | }; # tkdnd::platform_specific_type 417 | 418 | # ---------------------------------------------------------------------------- 419 | # Command tkdnd::platform_independent_type 420 | # ---------------------------------------------------------------------------- 421 | proc ::tkdnd::platform_independent_type { type } { 422 | variable _platform_namespace 423 | ${_platform_namespace}::platform_independent_type $type 424 | }; # tkdnd::platform_independent_type 425 | 426 | # ---------------------------------------------------------------------------- 427 | # Command tkdnd::bytes_to_string 428 | # ---------------------------------------------------------------------------- 429 | proc ::tkdnd::bytes_to_string { bytes } { 430 | set string {} 431 | foreach byte $bytes { 432 | append string [binary format c $byte] 433 | } 434 | return $string 435 | };# tkdnd::bytes_to_string 436 | 437 | # ---------------------------------------------------------------------------- 438 | # Command tkdnd::urn_unquote 439 | # ---------------------------------------------------------------------------- 440 | proc ::tkdnd::urn_unquote {url} { 441 | set result "" 442 | set start 0 443 | while {[regexp -start $start -indices {%[0-9a-fA-F]{2}} $url match]} { 444 | foreach {first last} $match break 445 | append result [string range $url $start [expr {$first - 1}]] 446 | append result [format %c 0x[string range $url [incr first] $last]] 447 | set start [incr last] 448 | } 449 | append result [string range $url $start end] 450 | return $result 451 | };# tkdnd::urn_unquote 452 | -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd28.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/verlorengest/BlenderManager/44f4abc66a6710ae0f3d2982892955894bcd5c36/source/tkdnd2.8/tkdnd28.dll -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd_compat.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tkdnd_compat.tcl -- 3 | # 4 | # This file implements some utility procedures, to support older versions 5 | # of the TkDND package. 6 | # 7 | # This software is copyrighted by: 8 | # George Petasis, National Centre for Scientific Research "Demokritos", 9 | # Aghia Paraskevi, Athens, Greece. 10 | # e-mail: petasis@iit.demokritos.gr 11 | # 12 | # The following terms apply to all files associated 13 | # with the software unless explicitly disclaimed in individual files. 14 | # 15 | # The authors hereby grant permission to use, copy, modify, distribute, 16 | # and license this software and its documentation for any purpose, provided 17 | # that existing copyright notices are retained in all copies and that this 18 | # notice is included verbatim in any distributions. No written agreement, 19 | # license, or royalty fee is required for any of the authorized uses. 20 | # Modifications to this software may be copyrighted by their authors 21 | # and need not follow the licensing terms described here, provided that 22 | # the new terms are clearly indicated on the first page of each file where 23 | # they apply. 24 | # 25 | # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 26 | # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 27 | # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 28 | # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 32 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 33 | # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 34 | # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 35 | # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 36 | # MODIFICATIONS. 37 | # 38 | 39 | namespace eval compat { 40 | 41 | };# namespace compat 42 | 43 | # ---------------------------------------------------------------------------- 44 | # Command ::dnd 45 | # ---------------------------------------------------------------------------- 46 | proc ::dnd {method window args} { 47 | switch $method { 48 | bindtarget { 49 | switch [llength $args] { 50 | 0 {return [tkdnd::compat::bindtarget0 $window]} 51 | 1 {return [tkdnd::compat::bindtarget1 $window [lindex $args 0]]} 52 | 2 {return [tkdnd::compat::bindtarget2 $window [lindex $args 0] \ 53 | [lindex $args 1]]} 54 | 3 {return [tkdnd::compat::bindtarget3 $window [lindex $args 0] \ 55 | [lindex $args 1] [lindex $args 2]]} 56 | 4 {return [tkdnd::compat::bindtarget4 $window [lindex $args 0] \ 57 | [lindex $args 1] [lindex $args 2] [lindex $args 3]]} 58 | } 59 | } 60 | cleartarget { 61 | return [tkdnd::compat::cleartarget $window] 62 | } 63 | bindsource { 64 | switch [llength $args] { 65 | 0 {return [tkdnd::compat::bindsource0 $window]} 66 | 1 {return [tkdnd::compat::bindsource1 $window [lindex $args 0]]} 67 | 2 {return [tkdnd::compat::bindsource2 $window [lindex $args 0] \ 68 | [lindex $args 1]]} 69 | 3 {return [tkdnd::compat::bindsource3 $window [lindex $args 0] \ 70 | [lindex $args 1] [lindex $args 2]]} 71 | } 72 | } 73 | clearsource { 74 | return [tkdnd::compat::clearsource $window] 75 | } 76 | drag { 77 | return [tkdnd::_init_drag $window "press" 0 0] 78 | } 79 | } 80 | error "invalid number of arguments!" 81 | };# ::dnd 82 | 83 | # ---------------------------------------------------------------------------- 84 | # Command compat::bindtarget 85 | # ---------------------------------------------------------------------------- 86 | proc compat::bindtarget0 {window} { 87 | return [bind $window <>] 88 | };# compat::bindtarget0 89 | 90 | proc compat::bindtarget1 {window type} { 91 | return [bindtarget2 $window $type ] 92 | };# compat::bindtarget1 93 | 94 | proc compat::bindtarget2 {window type event} { 95 | switch $event { 96 | {return [bind $window <>]} 97 | {return [bind $window <>]} 98 | {return [bind $window <>]} 99 | {return [bind $window <>]} 100 | } 101 | };# compat::bindtarget2 102 | 103 | proc compat::bindtarget3 {window type event script} { 104 | set type [normalise_type $type] 105 | ::tkdnd::drop_target register $window [list $type] 106 | switch $event { 107 | {return [bind $window <> $script]} 108 | {return [bind $window <> $script]} 109 | {return [bind $window <> $script]} 110 | {return [bind $window <> $script]} 111 | } 112 | };# compat::bindtarget3 113 | 114 | proc compat::bindtarget4 {window type event script priority} { 115 | return [bindtarget3 $window $type $event $script] 116 | };# compat::bindtarget4 117 | 118 | proc compat::normalise_type { type } { 119 | switch $type { 120 | text/plain - 121 | {text/plain;charset=UTF-8} - 122 | Text {return DND_Text} 123 | text/uri-list - 124 | Files {return DND_Files} 125 | default {return $type} 126 | } 127 | };# compat::normalise_type 128 | 129 | # ---------------------------------------------------------------------------- 130 | # Command compat::bindsource 131 | # ---------------------------------------------------------------------------- 132 | proc compat::bindsource0 {window} { 133 | return [bind $window <>] 134 | };# compat::bindsource0 135 | 136 | proc compat::bindsource1 {window type} { 137 | return [bindsource2 $window $type ] 138 | };# compat::bindsource1 139 | 140 | proc compat::bindsource2 {window type script} { 141 | ::tkdnd::drag_source register $window $type 2 142 | bind $window <> "list {copy} %t \[$script\]" 143 | };# compat::bindsource2 144 | 145 | proc compat::bindsource3 {window type script priority} { 146 | return [bindsource2 $window $type $script] 147 | };# compat::bindsource3 148 | 149 | # ---------------------------------------------------------------------------- 150 | # Command compat::cleartarget 151 | # ---------------------------------------------------------------------------- 152 | proc compat::cleartarget {window} { 153 | };# compat::cleartarget 154 | 155 | # ---------------------------------------------------------------------------- 156 | # Command compat::clearsource 157 | # ---------------------------------------------------------------------------- 158 | proc compat::clearsource {window} { 159 | };# compat::clearsource 160 | -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd_generic.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tkdnd_generic.tcl -- 3 | # 4 | # This file implements some utility procedures that are used by the TkDND 5 | # package. 6 | # 7 | # This software is copyrighted by: 8 | # George Petasis, National Centre for Scientific Research "Demokritos", 9 | # Aghia Paraskevi, Athens, Greece. 10 | # e-mail: petasis@iit.demokritos.gr 11 | # 12 | # The following terms apply to all files associated 13 | # with the software unless explicitly disclaimed in individual files. 14 | # 15 | # The authors hereby grant permission to use, copy, modify, distribute, 16 | # and license this software and its documentation for any purpose, provided 17 | # that existing copyright notices are retained in all copies and that this 18 | # notice is included verbatim in any distributions. No written agreement, 19 | # license, or royalty fee is required for any of the authorized uses. 20 | # Modifications to this software may be copyrighted by their authors 21 | # and need not follow the licensing terms described here, provided that 22 | # the new terms are clearly indicated on the first page of each file where 23 | # they apply. 24 | # 25 | # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 26 | # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 27 | # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 28 | # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 32 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 33 | # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 34 | # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 35 | # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 36 | # MODIFICATIONS. 37 | # 38 | 39 | namespace eval generic { 40 | variable _types {} 41 | variable _typelist {} 42 | variable _codelist {} 43 | variable _actionlist {} 44 | variable _pressedkeys {} 45 | variable _action {} 46 | variable _common_drag_source_types {} 47 | variable _common_drop_target_types {} 48 | variable _drag_source {} 49 | variable _drop_target {} 50 | 51 | variable _last_mouse_root_x 0 52 | variable _last_mouse_root_y 0 53 | 54 | variable _tkdnd2platform 55 | variable _platform2tkdnd 56 | 57 | proc debug {msg} { 58 | puts $msg 59 | };# debug 60 | 61 | proc initialise { } { 62 | };# initialise 63 | 64 | proc initialise_platform_to_tkdnd_types { types } { 65 | variable _platform2tkdnd 66 | variable _tkdnd2platform 67 | set _platform2tkdnd [dict create {*}$types] 68 | set _tkdnd2platform [dict create] 69 | foreach type [dict keys $_platform2tkdnd] { 70 | dict lappend _tkdnd2platform [dict get $_platform2tkdnd $type] $type 71 | } 72 | };# initialise_platform_to_tkdnd_types 73 | 74 | proc initialise_tkdnd_to_platform_types { types } { 75 | variable _tkdnd2platform 76 | set _tkdnd2platform [dict create {*}$types] 77 | };# initialise_tkdnd_to_platform_types 78 | 79 | };# namespace generic 80 | 81 | # ---------------------------------------------------------------------------- 82 | # Command generic::HandleEnter 83 | # ---------------------------------------------------------------------------- 84 | proc generic::HandleEnter { drop_target drag_source typelist codelist 85 | actionlist pressedkeys } { 86 | variable _typelist; set _typelist $typelist 87 | variable _pressedkeys; set _pressedkeys $pressedkeys 88 | variable _action; set _action refuse_drop 89 | variable _common_drag_source_types; set _common_drag_source_types {} 90 | variable _common_drop_target_types; set _common_drop_target_types {} 91 | variable _actionlist 92 | variable _drag_source; set _drag_source $drag_source 93 | variable _drop_target; set _drop_target {} 94 | variable _actionlist; set _actionlist $actionlist 95 | variable _codelist set _codelist $codelist 96 | 97 | variable _last_mouse_root_x; set _last_mouse_root_x 0 98 | variable _last_mouse_root_y; set _last_mouse_root_y 0 99 | # debug "\n===============================================================" 100 | # debug "generic::HandleEnter: drop_target=$drop_target,\ 101 | # drag_source=$drag_source,\ 102 | # typelist=$typelist" 103 | # debug "generic::HandleEnter: ACTION: default" 104 | return default 105 | };# generic::HandleEnter 106 | 107 | # ---------------------------------------------------------------------------- 108 | # Command generic::HandlePosition 109 | # ---------------------------------------------------------------------------- 110 | proc generic::HandlePosition { drop_target drag_source pressedkeys 111 | rootX rootY } { 112 | variable _types 113 | variable _typelist 114 | variable _codelist 115 | variable _actionlist 116 | variable _pressedkeys 117 | variable _action 118 | variable _common_drag_source_types 119 | variable _common_drop_target_types 120 | variable _drag_source 121 | variable _drop_target 122 | 123 | variable _last_mouse_root_x; set _last_mouse_root_x $rootX 124 | variable _last_mouse_root_y; set _last_mouse_root_y $rootY 125 | 126 | # debug "generic::HandlePosition: drop_target=$drop_target,\ 127 | # _drop_target=$_drop_target, rootX=$rootX, rootY=$rootY" 128 | 129 | if {![info exists _drag_source] && ![string length $_drag_source]} { 130 | # debug "generic::HandlePosition: no or empty _drag_source:\ 131 | # return refuse_drop" 132 | return refuse_drop 133 | } 134 | 135 | if {$drag_source ne "" && $drag_source ne $_drag_source} { 136 | debug "generic position event from unexpected source: $_drag_source\ 137 | != $drag_source" 138 | return refuse_drop 139 | } 140 | 141 | set _pressedkeys $pressedkeys 142 | 143 | ## Does the new drop target support any of our new types? 144 | # foreach {common_drag_source_types common_drop_target_types} \ 145 | # [GetWindowCommonTypes $drop_target $_typelist] {break} 146 | foreach {drop_target common_drag_source_types common_drop_target_types} \ 147 | [FindWindowWithCommonTypes $drop_target $_typelist] {break} 148 | 149 | # debug "\t($_drop_target) -> ($drop_target)" 150 | if {$drop_target != $_drop_target} { 151 | if {[string length $_drop_target]} { 152 | ## Call the <> event. 153 | # debug "\t<> on $_drop_target" 154 | set cmd [bind $_drop_target <>] 155 | if {[string length $cmd]} { 156 | set cmd [string map [list %W $_drop_target %X $rootX %Y $rootY \ 157 | %CST \{$_common_drag_source_types\} \ 158 | %CTT \{$_common_drop_target_types\} \ 159 | %CPT \{[lindex [platform_independent_type [lindex $_common_drag_source_types 0]] 0]\} \ 160 | %ST \{$_typelist\} %TT \{$_types\} \ 161 | %A \{$_action\} %a \{$_actionlist\} \ 162 | %b \{$_pressedkeys\} %m \{$_pressedkeys\} \ 163 | %D \{\} %e <> \ 164 | %L \{$_typelist\} %% % \ 165 | %t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \ 166 | %c \{$_codelist\} %C \{[lindex $_codelist 0]\} \ 167 | ] $cmd] 168 | uplevel \#0 $cmd 169 | } 170 | } 171 | set _drop_target $drop_target 172 | set _action refuse_drop 173 | 174 | if {[llength $common_drag_source_types]} { 175 | set _action [lindex $_actionlist 0] 176 | set _common_drag_source_types $common_drag_source_types 177 | set _common_drop_target_types $common_drop_target_types 178 | ## Drop target supports at least one type. Send a <>. 179 | # puts "<> -> $drop_target" 180 | set cmd [bind $drop_target <>] 181 | if {[string length $cmd]} { 182 | focus $drop_target 183 | set cmd [string map [list %W $drop_target %X $rootX %Y $rootY \ 184 | %CST \{$_common_drag_source_types\} \ 185 | %CTT \{$_common_drop_target_types\} \ 186 | %CPT \{[lindex [platform_independent_type [lindex $_common_drag_source_types 0]] 0]\} \ 187 | %ST \{$_typelist\} %TT \{$_types\} \ 188 | %A $_action %a \{$_actionlist\} \ 189 | %b \{$_pressedkeys\} %m \{$_pressedkeys\} \ 190 | %D \{\} %e <> \ 191 | %L \{$_typelist\} %% % \ 192 | %t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \ 193 | %c \{$_codelist\} %C \{[lindex $_codelist 0]\} \ 194 | ] $cmd] 195 | set _action [uplevel \#0 $cmd] 196 | switch -exact -- $_action { 197 | copy - move - link - ask - private - refuse_drop - default {} 198 | default {set _action copy} 199 | } 200 | } 201 | } 202 | } 203 | 204 | set _drop_target {} 205 | if {[llength $common_drag_source_types]} { 206 | set _common_drag_source_types $common_drag_source_types 207 | set _common_drop_target_types $common_drop_target_types 208 | set _drop_target $drop_target 209 | ## Drop target supports at least one type. Send a <>. 210 | set cmd [bind $drop_target <>] 211 | if {[string length $cmd]} { 212 | set cmd [string map [list %W $drop_target %X $rootX %Y $rootY \ 213 | %CST \{$_common_drag_source_types\} \ 214 | %CTT \{$_common_drop_target_types\} \ 215 | %CPT \{[lindex [platform_independent_type [lindex $_common_drag_source_types 0]] 0]\} \ 216 | %ST \{$_typelist\} %TT \{$_types\} \ 217 | %A $_action %a \{$_actionlist\} \ 218 | %b \{$_pressedkeys\} %m \{$_pressedkeys\} \ 219 | %D \{\} %e <> \ 220 | %L \{$_typelist\} %% % \ 221 | %t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \ 222 | %c \{$_codelist\} %C \{[lindex $_codelist 0]\} \ 223 | ] $cmd] 224 | set _action [uplevel \#0 $cmd] 225 | } 226 | } 227 | # Return values: copy, move, link, ask, private, refuse_drop, default 228 | # debug "generic::HandlePosition: ACTION: $_action" 229 | switch -exact -- $_action { 230 | copy - move - link - ask - private - refuse_drop - default {} 231 | default {set _action copy} 232 | } 233 | return $_action 234 | };# generic::HandlePosition 235 | 236 | # ---------------------------------------------------------------------------- 237 | # Command generic::HandleLeave 238 | # ---------------------------------------------------------------------------- 239 | proc generic::HandleLeave { } { 240 | variable _types 241 | variable _typelist 242 | variable _codelist 243 | variable _actionlist 244 | variable _pressedkeys 245 | variable _action 246 | variable _common_drag_source_types 247 | variable _common_drop_target_types 248 | variable _drag_source 249 | variable _drop_target 250 | variable _last_mouse_root_x 251 | variable _last_mouse_root_y 252 | if {![info exists _drop_target]} {set _drop_target {}} 253 | # debug "generic::HandleLeave: _drop_target=$_drop_target" 254 | if {[info exists _drop_target] && [string length $_drop_target]} { 255 | set cmd [bind $_drop_target <>] 256 | if {[string length $cmd]} { 257 | set cmd [string map [list %W $_drop_target \ 258 | %X $_last_mouse_root_x %Y $_last_mouse_root_y \ 259 | %CST \{$_common_drag_source_types\} \ 260 | %CTT \{$_common_drop_target_types\} \ 261 | %CPT \{[lindex [platform_independent_type [lindex $_common_drag_source_types 0]] 0]\} \ 262 | %ST \{$_typelist\} %TT \{$_types\} \ 263 | %A \{$_action\} %a \{$_actionlist\} \ 264 | %b \{$_pressedkeys\} %m \{$_pressedkeys\} \ 265 | %D \{\} %e <> \ 266 | %L \{$_typelist\} %% % \ 267 | %t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \ 268 | %c \{$_codelist\} %C \{[lindex $_codelist 0]\} \ 269 | ] $cmd] 270 | set _action [uplevel \#0 $cmd] 271 | } 272 | } 273 | foreach var {_types _typelist _actionlist _pressedkeys _action 274 | _common_drag_source_types _common_drop_target_types 275 | _drag_source _drop_target} { 276 | set $var {} 277 | } 278 | };# generic::HandleLeave 279 | 280 | # ---------------------------------------------------------------------------- 281 | # Command generic::HandleDrop 282 | # ---------------------------------------------------------------------------- 283 | proc generic::HandleDrop {drop_target drag_source pressedkeys rootX rootY time } { 284 | variable _types 285 | variable _typelist 286 | variable _codelist 287 | variable _actionlist 288 | variable _pressedkeys 289 | variable _action 290 | variable _common_drag_source_types 291 | variable _common_drop_target_types 292 | variable _drag_source 293 | variable _drop_target 294 | variable _last_mouse_root_x 295 | variable _last_mouse_root_y 296 | variable _last_mouse_root_x; set _last_mouse_root_x $rootX 297 | variable _last_mouse_root_y; set _last_mouse_root_y $rootY 298 | 299 | set _pressedkeys $pressedkeys 300 | 301 | # puts "generic::HandleDrop: $time" 302 | 303 | if {![info exists _drag_source] && ![string length $_drag_source]} { 304 | return refuse_drop 305 | } 306 | if {![info exists _drop_target] && ![string length $_drop_target]} { 307 | return refuse_drop 308 | } 309 | if {![llength $_common_drag_source_types]} {return refuse_drop} 310 | ## Get the dropped data. 311 | set data [GetDroppedData $time] 312 | ## Try to select the most specific <> event. 313 | foreach type [concat $_common_drag_source_types $_common_drop_target_types] { 314 | set type [platform_independent_type $type] 315 | set cmd [bind $_drop_target <>] 316 | if {[string length $cmd]} { 317 | set cmd [string map [list %W $_drop_target %X $rootX %Y $rootY \ 318 | %CST \{$_common_drag_source_types\} \ 319 | %CTT \{$_common_drop_target_types\} \ 320 | %CPT \{[lindex [platform_independent_type [lindex $_common_drag_source_types 0]] 0]\} \ 321 | %ST \{$_typelist\} %TT \{$_types\} \ 322 | %A $_action %a \{$_actionlist\} \ 323 | %b \{$_pressedkeys\} %m \{$_pressedkeys\} \ 324 | %D [list $data] %e <> \ 325 | %L \{$_typelist\} %% % \ 326 | %t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \ 327 | %c \{$_codelist\} %C \{[lindex $_codelist 0]\} \ 328 | ] $cmd] 329 | set _action [uplevel \#0 $cmd] 330 | # Return values: copy, move, link, ask, private, refuse_drop 331 | switch -exact -- $_action { 332 | copy - move - link - ask - private - refuse_drop - default {} 333 | default {set _action copy} 334 | } 335 | return $_action 336 | } 337 | } 338 | set cmd [bind $_drop_target <>] 339 | if {[string length $cmd]} { 340 | set cmd [string map [list %W $_drop_target %X $rootX %Y $rootY \ 341 | %CST \{$_common_drag_source_types\} \ 342 | %CTT \{$_common_drop_target_types\} \ 343 | %CPT \{[lindex [platform_independent_type [lindex $_common_drag_source_types 0]] 0]\} \ 344 | %ST \{$_typelist\} %TT \{$_types\} \ 345 | %A $_action %a \{$_actionlist\} \ 346 | %b \{$_pressedkeys\} %m \{$_pressedkeys\} \ 347 | %D [list $data] %e <> \ 348 | %L \{$_typelist\} %% % \ 349 | %t \{$_typelist\} %T \{[lindex $_common_drag_source_types 0]\} \ 350 | %c \{$_codelist\} %C \{[lindex $_codelist 0]\} \ 351 | ] $cmd] 352 | set _action [uplevel \#0 $cmd] 353 | } 354 | # Return values: copy, move, link, ask, private, refuse_drop 355 | switch -exact -- $_action { 356 | copy - move - link - ask - private - refuse_drop - default {} 357 | default {set _action copy} 358 | } 359 | return $_action 360 | };# generic::HandleDrop 361 | 362 | # ---------------------------------------------------------------------------- 363 | # Command generic::GetWindowCommonTypes 364 | # ---------------------------------------------------------------------------- 365 | proc generic::GetWindowCommonTypes { win typelist } { 366 | set types [bind $win <>] 367 | # debug ">> Accepted types: $win $_types" 368 | set common_drag_source_types {} 369 | set common_drop_target_types {} 370 | if {[llength $types]} { 371 | ## Examine the drop target types, to find at least one match with the drag 372 | ## source types... 373 | set supported_types [supported_types $typelist] 374 | foreach type $types { 375 | foreach matched [lsearch -glob -all -inline $supported_types $type] { 376 | ## Drop target supports this type. 377 | lappend common_drag_source_types $matched 378 | lappend common_drop_target_types $type 379 | } 380 | } 381 | } 382 | list $common_drag_source_types $common_drop_target_types 383 | };# generic::GetWindowCommonTypes 384 | 385 | # ---------------------------------------------------------------------------- 386 | # Command generic::FindWindowWithCommonTypes 387 | # ---------------------------------------------------------------------------- 388 | proc generic::FindWindowWithCommonTypes { win typelist } { 389 | set toplevel [winfo toplevel $win] 390 | while {![string equal $win $toplevel]} { 391 | foreach {common_drag_source_types common_drop_target_types} \ 392 | [GetWindowCommonTypes $win $typelist] {break} 393 | if {[llength $common_drag_source_types]} { 394 | return [list $win $common_drag_source_types $common_drop_target_types] 395 | } 396 | set win [winfo parent $win] 397 | } 398 | ## We have reached the toplevel, which may be also a target (SF Bug #30) 399 | foreach {common_drag_source_types common_drop_target_types} \ 400 | [GetWindowCommonTypes $win $typelist] {break} 401 | if {[llength $common_drag_source_types]} { 402 | return [list $win $common_drag_source_types $common_drop_target_types] 403 | } 404 | return { {} {} {} } 405 | };# generic::FindWindowWithCommonTypes 406 | 407 | # ---------------------------------------------------------------------------- 408 | # Command generic::GetDroppedData 409 | # ---------------------------------------------------------------------------- 410 | proc generic::GetDroppedData { time } { 411 | variable _dropped_data 412 | return $_dropped_data 413 | };# generic::GetDroppedData 414 | 415 | # ---------------------------------------------------------------------------- 416 | # Command generic::SetDroppedData 417 | # ---------------------------------------------------------------------------- 418 | proc generic::SetDroppedData { data } { 419 | variable _dropped_data 420 | set _dropped_data $data 421 | };# generic::SetDroppedData 422 | 423 | # ---------------------------------------------------------------------------- 424 | # Command generic::GetDragSource 425 | # ---------------------------------------------------------------------------- 426 | proc generic::GetDragSource { } { 427 | variable _drag_source 428 | return $_drag_source 429 | };# generic::GetDragSource 430 | 431 | # ---------------------------------------------------------------------------- 432 | # Command generic::GetDropTarget 433 | # ---------------------------------------------------------------------------- 434 | proc generic::GetDropTarget { } { 435 | variable _drop_target 436 | return $_drop_target 437 | };# generic::GetDropTarget 438 | 439 | # ---------------------------------------------------------------------------- 440 | # Command generic::GetDragSourceCommonTypes 441 | # ---------------------------------------------------------------------------- 442 | proc generic::GetDragSourceCommonTypes { } { 443 | variable _common_drag_source_types 444 | return $_common_drag_source_types 445 | };# generic::GetDragSourceCommonTypes 446 | 447 | # ---------------------------------------------------------------------------- 448 | # Command generic::GetDropTargetCommonTypes 449 | # ---------------------------------------------------------------------------- 450 | proc generic::GetDropTargetCommonTypes { } { 451 | variable _common_drag_source_types 452 | return $_common_drag_source_types 453 | };# generic::GetDropTargetCommonTypes 454 | 455 | # ---------------------------------------------------------------------------- 456 | # Command generic::platform_specific_types 457 | # ---------------------------------------------------------------------------- 458 | proc generic::platform_specific_types { types } { 459 | set new_types {} 460 | foreach type $types { 461 | set new_types [concat $new_types [platform_specific_type $type]] 462 | } 463 | return $new_types 464 | }; # generic::platform_specific_types 465 | 466 | # ---------------------------------------------------------------------------- 467 | # Command generic::platform_specific_type 468 | # ---------------------------------------------------------------------------- 469 | proc generic::platform_specific_type { type } { 470 | variable _tkdnd2platform 471 | if {[dict exists $_tkdnd2platform $type]} { 472 | return [dict get $_tkdnd2platform $type] 473 | } 474 | list $type 475 | }; # generic::platform_specific_type 476 | 477 | # ---------------------------------------------------------------------------- 478 | # Command tkdnd::platform_independent_types 479 | # ---------------------------------------------------------------------------- 480 | proc ::tkdnd::platform_independent_types { types } { 481 | set new_types {} 482 | foreach type $types { 483 | set new_types [concat $new_types [platform_independent_type $type]] 484 | } 485 | return $new_types 486 | }; # tkdnd::platform_independent_types 487 | 488 | # ---------------------------------------------------------------------------- 489 | # Command generic::platform_independent_type 490 | # ---------------------------------------------------------------------------- 491 | proc generic::platform_independent_type { type } { 492 | variable _platform2tkdnd 493 | if {[dict exists $_platform2tkdnd $type]} { 494 | return [dict get $_platform2tkdnd $type] 495 | } 496 | return $type 497 | }; # generic::platform_independent_type 498 | 499 | # ---------------------------------------------------------------------------- 500 | # Command generic::supported_types 501 | # ---------------------------------------------------------------------------- 502 | proc generic::supported_types { types } { 503 | set new_types {} 504 | foreach type $types { 505 | if {[supported_type $type]} {lappend new_types $type} 506 | } 507 | return $new_types 508 | }; # generic::supported_types 509 | 510 | # ---------------------------------------------------------------------------- 511 | # Command generic::supported_type 512 | # ---------------------------------------------------------------------------- 513 | proc generic::supported_type { type } { 514 | variable _platform2tkdnd 515 | if {[dict exists $_platform2tkdnd $type]} { 516 | return 1 517 | } 518 | return 0 519 | }; # generic::supported_type 520 | -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd_macosx.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tkdnd_macosx.tcl -- 3 | # 4 | # This file implements some utility procedures that are used by the TkDND 5 | # package. 6 | 7 | # This software is copyrighted by: 8 | # Georgios Petasis, Athens, Greece. 9 | # e-mail: petasisg@yahoo.gr, petasis@iit.demokritos.gr 10 | # 11 | # Mac portions (c) 2009 Kevin Walzer/WordTech Communications LLC, 12 | # kw@codebykevin.com 13 | # 14 | # 15 | # The following terms apply to all files associated 16 | # with the software unless explicitly disclaimed in individual files. 17 | # 18 | # The authors hereby grant permission to use, copy, modify, distribute, 19 | # and license this software and its documentation for any purpose, provided 20 | # that existing copyright notices are retained in all copies and that this 21 | # notice is included verbatim in any distributions. No written agreement, 22 | # license, or royalty fee is required for any of the authorized uses. 23 | # Modifications to this software may be copyrighted by their authors 24 | # and need not follow the licensing terms described here, provided that 25 | # the new terms are clearly indicated on the first page of each file where 26 | # they apply. 27 | # 28 | # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 29 | # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 30 | # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 31 | # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 32 | # POSSIBILITY OF SUCH DAMAGE. 33 | # 34 | # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 35 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 36 | # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 37 | # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 38 | # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 39 | # MODIFICATIONS. 40 | # 41 | 42 | #basic API for Mac Drag and Drop 43 | 44 | #two data types supported: strings and file paths 45 | 46 | #two commands at C level: ::tkdnd::macdnd::registerdragwidget, ::tkdnd::macdnd::unregisterdragwidget 47 | 48 | #data retrieval mechanism: text or file paths are copied from drag clipboard to system clipboard and retrieved via [clipboard get]; array of file paths is converted to single tab-separated string, can be split into Tcl list 49 | 50 | if {[tk windowingsystem] eq "aqua" && "AppKit" ni [winfo server .]} { 51 | error {TkAqua Cocoa required} 52 | } 53 | 54 | namespace eval macdnd { 55 | 56 | proc initialise { } { 57 | ## Mapping from platform types to TkDND types... 58 | ::tkdnd::generic::initialise_platform_to_tkdnd_types [list \ 59 | NSPasteboardTypeString DND_Text \ 60 | NSFilenamesPboardType DND_Files \ 61 | NSPasteboardTypeHTML DND_HTML \ 62 | ] 63 | };# initialise 64 | 65 | };# namespace macdnd 66 | 67 | # ---------------------------------------------------------------------------- 68 | # Command macdnd::HandleEnter 69 | # ---------------------------------------------------------------------------- 70 | proc macdnd::HandleEnter { path drag_source typelist } { 71 | variable _pressedkeys 72 | variable _actionlist 73 | set _pressedkeys 1 74 | set _actionlist { copy move link ask private } 75 | ::tkdnd::generic::HandleEnter $path $drag_source $typelist $typelist \ 76 | $_actionlist $_pressedkeys 77 | };# macdnd::HandleEnter 78 | 79 | # ---------------------------------------------------------------------------- 80 | # Command macdnd::HandlePosition 81 | # ---------------------------------------------------------------------------- 82 | proc macdnd::HandlePosition { drop_target rootX rootY {drag_source {}} } { 83 | variable _pressedkeys 84 | variable _last_mouse_root_x; set _last_mouse_root_x $rootX 85 | variable _last_mouse_root_y; set _last_mouse_root_y $rootY 86 | ::tkdnd::generic::HandlePosition $drop_target $drag_source \ 87 | $_pressedkeys $rootX $rootY 88 | };# macdnd::HandlePosition 89 | 90 | # ---------------------------------------------------------------------------- 91 | # Command macdnd::HandleLeave 92 | # ---------------------------------------------------------------------------- 93 | proc macdnd::HandleLeave { args } { 94 | ::tkdnd::generic::HandleLeave 95 | };# macdnd::HandleLeave 96 | 97 | # ---------------------------------------------------------------------------- 98 | # Command macdnd::HandleDrop 99 | # ---------------------------------------------------------------------------- 100 | proc macdnd::HandleDrop { drop_target data args } { 101 | variable _pressedkeys 102 | variable _last_mouse_root_x 103 | variable _last_mouse_root_y 104 | ## Get the dropped data... 105 | ::tkdnd::generic::SetDroppedData $data 106 | ::tkdnd::generic::HandleDrop {} {} $_pressedkeys \ 107 | $_last_mouse_root_x $_last_mouse_root_y 0 108 | };# macdnd::HandleDrop 109 | 110 | # ---------------------------------------------------------------------------- 111 | # Command macdnd::GetDragSourceCommonTypes 112 | # ---------------------------------------------------------------------------- 113 | proc macdnd::GetDragSourceCommonTypes { } { 114 | ::tkdnd::generic::GetDragSourceCommonTypes 115 | };# macdnd::GetDragSourceCommonTypes 116 | 117 | # ---------------------------------------------------------------------------- 118 | # Command macdnd::platform_specific_types 119 | # ---------------------------------------------------------------------------- 120 | proc macdnd::platform_specific_types { types } { 121 | ::tkdnd::generic::platform_specific_types $types 122 | }; # macdnd::platform_specific_types 123 | 124 | # ---------------------------------------------------------------------------- 125 | # Command macdnd::platform_specific_type 126 | # ---------------------------------------------------------------------------- 127 | proc macdnd::platform_specific_type { type } { 128 | ::tkdnd::generic::platform_specific_type $type 129 | }; # macdnd::platform_specific_type 130 | 131 | # ---------------------------------------------------------------------------- 132 | # Command tkdnd::platform_independent_types 133 | # ---------------------------------------------------------------------------- 134 | proc ::tkdnd::platform_independent_types { types } { 135 | ::tkdnd::generic::platform_independent_types $types 136 | }; # tkdnd::platform_independent_types 137 | 138 | # ---------------------------------------------------------------------------- 139 | # Command macdnd::platform_independent_type 140 | # ---------------------------------------------------------------------------- 141 | proc macdnd::platform_independent_type { type } { 142 | ::tkdnd::generic::platform_independent_type $type 143 | }; # macdnd::platform_independent_type 144 | -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd_unix.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tkdnd_unix.tcl -- 3 | # 4 | # This file implements some utility procedures that are used by the TkDND 5 | # package. 6 | # 7 | # This software is copyrighted by: 8 | # George Petasis, National Centre for Scientific Research "Demokritos", 9 | # Aghia Paraskevi, Athens, Greece. 10 | # e-mail: petasis@iit.demokritos.gr 11 | # 12 | # The following terms apply to all files associated 13 | # with the software unless explicitly disclaimed in individual files. 14 | # 15 | # The authors hereby grant permission to use, copy, modify, distribute, 16 | # and license this software and its documentation for any purpose, provided 17 | # that existing copyright notices are retained in all copies and that this 18 | # notice is included verbatim in any distributions. No written agreement, 19 | # license, or royalty fee is required for any of the authorized uses. 20 | # Modifications to this software may be copyrighted by their authors 21 | # and need not follow the licensing terms described here, provided that 22 | # the new terms are clearly indicated on the first page of each file where 23 | # they apply. 24 | # 25 | # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 26 | # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 27 | # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 28 | # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 32 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 33 | # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 34 | # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 35 | # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 36 | # MODIFICATIONS. 37 | # 38 | 39 | namespace eval xdnd { 40 | variable _dragging 0 41 | 42 | proc initialise { } { 43 | ## Mapping from platform types to TkDND types... 44 | ::tkdnd::generic::initialise_platform_to_tkdnd_types [list \ 45 | text/plain\;charset=utf-8 DND_Text \ 46 | UTF8_STRING DND_Text \ 47 | text/plain DND_Text \ 48 | STRING DND_Text \ 49 | TEXT DND_Text \ 50 | COMPOUND_TEXT DND_Text \ 51 | text/uri-list DND_Files \ 52 | text/html\;charset=utf-8 DND_HTML \ 53 | text/html DND_HTML \ 54 | application/x-color DND_Color \ 55 | ] 56 | };# initialise 57 | 58 | };# namespace xdnd 59 | 60 | # ---------------------------------------------------------------------------- 61 | # Command xdnd::HandleXdndEnter 62 | # ---------------------------------------------------------------------------- 63 | proc xdnd::HandleXdndEnter { path drag_source typelist } { 64 | variable _pressedkeys 65 | variable _actionlist 66 | set _pressedkeys 1 67 | set _actionlist { copy move link ask private } 68 | ::tkdnd::generic::HandleEnter $path $drag_source $typelist $typelist \ 69 | $_actionlist $_pressedkeys 70 | };# xdnd::HandleXdndEnter 71 | 72 | # ---------------------------------------------------------------------------- 73 | # Command xdnd::HandleXdndPosition 74 | # ---------------------------------------------------------------------------- 75 | proc xdnd::HandleXdndPosition { drop_target rootX rootY {drag_source {}} } { 76 | variable _pressedkeys 77 | variable _last_mouse_root_x; set _last_mouse_root_x $rootX 78 | variable _last_mouse_root_y; set _last_mouse_root_y $rootY 79 | ::tkdnd::generic::HandlePosition $drop_target $drag_source \ 80 | $_pressedkeys $rootX $rootY 81 | };# xdnd::HandleXdndPosition 82 | 83 | # ---------------------------------------------------------------------------- 84 | # Command xdnd::HandleXdndLeave 85 | # ---------------------------------------------------------------------------- 86 | proc xdnd::HandleXdndLeave { } { 87 | ::tkdnd::generic::HandleLeave 88 | };# xdnd::HandleXdndLeave 89 | 90 | # ---------------------------------------------------------------------------- 91 | # Command xdnd::_HandleXdndDrop 92 | # ---------------------------------------------------------------------------- 93 | proc xdnd::HandleXdndDrop { time } { 94 | variable _pressedkeys 95 | variable _last_mouse_root_x 96 | variable _last_mouse_root_y 97 | ## Get the dropped data... 98 | ::tkdnd::generic::SetDroppedData [GetDroppedData $time] 99 | ::tkdnd::generic::HandleDrop {} {} $_pressedkeys \ 100 | $_last_mouse_root_x $_last_mouse_root_y $time 101 | };# xdnd::HandleXdndDrop 102 | 103 | # ---------------------------------------------------------------------------- 104 | # Command xdnd::_GetDroppedData 105 | # ---------------------------------------------------------------------------- 106 | proc xdnd::GetDroppedData { time } { 107 | set _drag_source [::tkdnd::generic::GetDragSource] 108 | set _drop_target [::tkdnd::generic::GetDropTarget] 109 | set _common_drag_source_types [::tkdnd::generic::GetDragSourceCommonTypes] 110 | if {![llength $_common_drag_source_types]} { 111 | error "no common data types between the drag source and drop target widgets" 112 | } 113 | ## Is drag source in this application? 114 | if {[catch {winfo pathname -displayof $_drop_target $_drag_source} p]} { 115 | set _use_tk_selection 0 116 | } else { 117 | set _use_tk_selection 1 118 | } 119 | foreach type $_common_drag_source_types { 120 | # puts "TYPE: $type ($_drop_target)" 121 | # _get_selection $_drop_target $time $type 122 | if {$_use_tk_selection} { 123 | if {![catch { 124 | selection get -displayof $_drop_target -selection XdndSelection \ 125 | -type $type 126 | } result options]} { 127 | return [normalise_data $type $result] 128 | } 129 | } else { 130 | # puts "_selection_get -displayof $_drop_target -selection XdndSelection \ 131 | # -type $type -time $time" 132 | #after 100 [list focus -force $_drop_target] 133 | #after 50 [list raise [winfo toplevel $_drop_target]] 134 | if {![catch { 135 | _selection_get -displayof $_drop_target -selection XdndSelection \ 136 | -type $type -time $time 137 | } result options]} { 138 | return [normalise_data $type $result] 139 | } 140 | } 141 | } 142 | return -options $options $result 143 | };# xdnd::GetDroppedData 144 | 145 | # ---------------------------------------------------------------------------- 146 | # Command xdnd::platform_specific_types 147 | # ---------------------------------------------------------------------------- 148 | proc xdnd::platform_specific_types { types } { 149 | ::tkdnd::generic::platform_specific_types $types 150 | }; # xdnd::platform_specific_types 151 | 152 | # ---------------------------------------------------------------------------- 153 | # Command xdnd::platform_specific_type 154 | # ---------------------------------------------------------------------------- 155 | proc xdnd::platform_specific_type { type } { 156 | ::tkdnd::generic::platform_specific_type $type 157 | }; # xdnd::platform_specific_type 158 | 159 | # ---------------------------------------------------------------------------- 160 | # Command tkdnd::platform_independent_types 161 | # ---------------------------------------------------------------------------- 162 | proc ::tkdnd::platform_independent_types { types } { 163 | ::tkdnd::generic::platform_independent_types $types 164 | }; # tkdnd::platform_independent_types 165 | 166 | # ---------------------------------------------------------------------------- 167 | # Command xdnd::platform_independent_type 168 | # ---------------------------------------------------------------------------- 169 | proc xdnd::platform_independent_type { type } { 170 | ::tkdnd::generic::platform_independent_type $type 171 | }; # xdnd::platform_independent_type 172 | 173 | # ---------------------------------------------------------------------------- 174 | # Command xdnd::_normalise_data 175 | # ---------------------------------------------------------------------------- 176 | proc xdnd::normalise_data { type data } { 177 | # Tk knows how to interpret the following types: 178 | # STRING, TEXT, COMPOUND_TEXT 179 | # UTF8_STRING 180 | # Else, it returns a list of 8 or 32 bit numbers... 181 | switch -glob $type { 182 | STRING - UTF8_STRING - TEXT - COMPOUND_TEXT {return $data} 183 | text/html { 184 | if {[catch { 185 | encoding convertfrom unicode $data 186 | } string]} { 187 | set string $data 188 | } 189 | return [string map {\r\n \n} $string] 190 | } 191 | text/html\;charset=utf-8 - 192 | text/plain\;charset=utf-8 - 193 | text/plain { 194 | if {[catch { 195 | encoding convertfrom utf-8 [tkdnd::bytes_to_string $data] 196 | } string]} { 197 | set string $data 198 | } 199 | return [string map {\r\n \n} $string] 200 | } 201 | text/uri-list* { 202 | if {[catch { 203 | encoding convertfrom utf-8 [tkdnd::bytes_to_string $data 204 | } string]} { 205 | set string $data 206 | } 207 | ## Get rid of \r\n 208 | set string [string trim [string map {\r\n \n} $string]] 209 | set files {} 210 | foreach quoted_file [split $string] { 211 | set file [tkdnd::urn_unquote $quoted_file] 212 | switch -glob $file { 213 | file://* {lappend files [string range $file 7 end]} 214 | ftp://* - 215 | https://* - 216 | http://* {lappend files $quoted_file} 217 | default {lappend files $file} 218 | } 219 | } 220 | return $files 221 | } 222 | application/x-color { 223 | return $data 224 | } 225 | text/x-moz-url - 226 | application/q-iconlist - 227 | default {return $data} 228 | } 229 | }; # xdnd::normalise_data 230 | 231 | ############################################################################# 232 | ## 233 | ## XDND drag implementation 234 | ## 235 | ############################################################################# 236 | 237 | # ---------------------------------------------------------------------------- 238 | # Command xdnd::_selection_ownership_lost 239 | # ---------------------------------------------------------------------------- 240 | proc xdnd::_selection_ownership_lost {} { 241 | variable _dragging 242 | set _dragging 0 243 | };# _selection_ownership_lost 244 | 245 | # ---------------------------------------------------------------------------- 246 | # Command xdnd::_dodragdrop 247 | # ---------------------------------------------------------------------------- 248 | proc xdnd::_dodragdrop { source actions types data button } { 249 | variable _dragging 250 | 251 | # puts "xdnd::_dodragdrop: source: $source, actions: $actions, types: $types,\ 252 | # data: \"$data\", button: $button" 253 | if {$_dragging} { 254 | ## We are in the middle of another drag operation... 255 | error "another drag operation in progress" 256 | } 257 | 258 | variable _dodragdrop_drag_source $source 259 | variable _dodragdrop_drop_target 0 260 | variable _dodragdrop_drop_target_proxy 0 261 | variable _dodragdrop_actions $actions 262 | variable _dodragdrop_action_descriptions $actions 263 | variable _dodragdrop_actions_len [llength $actions] 264 | variable _dodragdrop_types $types 265 | variable _dodragdrop_types_len [llength $types] 266 | variable _dodragdrop_data $data 267 | variable _dodragdrop_transfer_data {} 268 | variable _dodragdrop_button $button 269 | variable _dodragdrop_time 0 270 | variable _dodragdrop_default_action refuse_drop 271 | variable _dodragdrop_waiting_status 0 272 | variable _dodragdrop_drop_target_accepts_drop 0 273 | variable _dodragdrop_drop_target_accepts_action refuse_drop 274 | variable _dodragdrop_current_cursor $_dodragdrop_default_action 275 | variable _dodragdrop_drop_occured 0 276 | variable _dodragdrop_selection_requestor 0 277 | 278 | ## 279 | ## If we have more than 3 types, the property XdndTypeList must be set on 280 | ## the drag source widget... 281 | ## 282 | if {$_dodragdrop_types_len > 3} { 283 | _announce_type_list $_dodragdrop_drag_source $_dodragdrop_types 284 | } 285 | 286 | ## 287 | ## Announce the actions & their descriptions on the XdndActionList & 288 | ## XdndActionDescription properties... 289 | ## 290 | _announce_action_list $_dodragdrop_drag_source $_dodragdrop_actions \ 291 | $_dodragdrop_action_descriptions 292 | 293 | ## 294 | ## Arrange selection handlers for our drag source, and all the supported types 295 | ## 296 | registerSelectionHandler $source $types 297 | 298 | ## 299 | ## Step 1: When a drag begins, the source takes ownership of XdndSelection. 300 | ## 301 | selection own -command ::tkdnd::xdnd::_selection_ownership_lost \ 302 | -selection XdndSelection $source 303 | set _dragging 1 304 | 305 | ## Grab the mouse pointer... 306 | _grab_pointer $source $_dodragdrop_default_action 307 | 308 | ## Register our generic event handler... 309 | # The generic event callback will report events by modifying variable 310 | # ::xdnd::_dodragdrop_event: a dict with event information will be set as 311 | # the value of the variable... 312 | _register_generic_event_handler 313 | 314 | ## Set a timeout for debugging purposes... 315 | # after 60000 {set ::tkdnd::xdnd::_dragging 0} 316 | 317 | tkwait variable ::tkdnd::xdnd::_dragging 318 | _SendXdndLeave 319 | 320 | set _dragging 0 321 | _ungrab_pointer $source 322 | _unregister_generic_event_handler 323 | catch {selection clear -selection XdndSelection} 324 | unregisterSelectionHandler $source $types 325 | return $_dodragdrop_drop_target_accepts_action 326 | };# xdnd::_dodragdrop 327 | 328 | # ---------------------------------------------------------------------------- 329 | # Command xdnd::_process_drag_events 330 | # ---------------------------------------------------------------------------- 331 | proc xdnd::_process_drag_events {event} { 332 | # The return value from proc is normally 0. A non-zero return value indicates 333 | # that the event is not to be handled further; that is, proc has done all 334 | # processing that is to be allowed for the event 335 | variable _dragging 336 | if {!$_dragging} {return 0} 337 | # puts $event 338 | 339 | variable _dodragdrop_time 340 | set time [dict get $event time] 341 | set type [dict get $event type] 342 | if {$time < $_dodragdrop_time && ![string equal $type SelectionRequest]} { 343 | return 0 344 | } 345 | set _dodragdrop_time $time 346 | 347 | variable _dodragdrop_drag_source 348 | variable _dodragdrop_drop_target 349 | variable _dodragdrop_drop_target_proxy 350 | variable _dodragdrop_default_action 351 | switch $type { 352 | MotionNotify { 353 | set rootx [dict get $event x_root] 354 | set rooty [dict get $event y_root] 355 | set window [_find_drop_target_window $_dodragdrop_drag_source \ 356 | $rootx $rooty] 357 | if {[string length $window]} { 358 | ## Examine the modifiers to suggest an action... 359 | set _dodragdrop_default_action [_default_action $event] 360 | ## Is it a Tk widget? 361 | # set path [winfo containing $rootx $rooty] 362 | # puts "Window under mouse: $window ($path)" 363 | if {$_dodragdrop_drop_target != $window} { 364 | ## Send XdndLeave to $_dodragdrop_drop_target 365 | _SendXdndLeave 366 | ## Is there a proxy? If not, _find_drop_target_proxy returns the 367 | ## target window, so we always get a valid "proxy". 368 | set proxy [_find_drop_target_proxy $_dodragdrop_drag_source $window] 369 | ## Send XdndEnter to $window 370 | _SendXdndEnter $window $proxy 371 | ## Send XdndPosition to $_dodragdrop_drop_target 372 | _SendXdndPosition $rootx $rooty $_dodragdrop_default_action 373 | } else { 374 | ## Send XdndPosition to $_dodragdrop_drop_target 375 | _SendXdndPosition $rootx $rooty $_dodragdrop_default_action 376 | } 377 | } else { 378 | ## No window under the mouse. Send XdndLeave to $_dodragdrop_drop_target 379 | _SendXdndLeave 380 | } 381 | } 382 | ButtonPress { 383 | } 384 | ButtonRelease { 385 | variable _dodragdrop_button 386 | set button [dict get $event button] 387 | if {$button == $_dodragdrop_button} { 388 | ## The button that initiated the drag was released. Trigger drop... 389 | _SendXdndDrop 390 | } 391 | return 1 392 | } 393 | KeyPress { 394 | } 395 | KeyRelease { 396 | set keysym [dict get $event keysym] 397 | switch $keysym { 398 | Escape { 399 | ## The user has pressed escape. Abort... 400 | if {$_dragging} {set _dragging 0} 401 | } 402 | } 403 | } 404 | SelectionRequest { 405 | variable _dodragdrop_selection_requestor 406 | variable _dodragdrop_selection_property 407 | variable _dodragdrop_selection_selection 408 | variable _dodragdrop_selection_target 409 | variable _dodragdrop_selection_time 410 | set _dodragdrop_selection_requestor [dict get $event requestor] 411 | set _dodragdrop_selection_property [dict get $event property] 412 | set _dodragdrop_selection_selection [dict get $event selection] 413 | set _dodragdrop_selection_target [dict get $event target] 414 | set _dodragdrop_selection_time $time 415 | return 0 416 | } 417 | default { 418 | return 0 419 | } 420 | } 421 | return 0 422 | };# _process_drag_events 423 | 424 | # ---------------------------------------------------------------------------- 425 | # Command xdnd::_SendXdndEnter 426 | # ---------------------------------------------------------------------------- 427 | proc xdnd::_SendXdndEnter {window proxy} { 428 | variable _dodragdrop_drag_source 429 | variable _dodragdrop_drop_target 430 | variable _dodragdrop_drop_target_proxy 431 | variable _dodragdrop_types 432 | variable _dodragdrop_waiting_status 433 | variable _dodragdrop_drop_occured 434 | if {$_dodragdrop_drop_target > 0} _SendXdndLeave 435 | if {$_dodragdrop_drop_occured} return 436 | set _dodragdrop_drop_target $window 437 | set _dodragdrop_drop_target_proxy $proxy 438 | set _dodragdrop_waiting_status 0 439 | if {$_dodragdrop_drop_target < 1} return 440 | # puts "XdndEnter: $_dodragdrop_drop_target $_dodragdrop_drop_target_proxy" 441 | _send_XdndEnter $_dodragdrop_drag_source $_dodragdrop_drop_target \ 442 | $_dodragdrop_drop_target_proxy $_dodragdrop_types 443 | };# xdnd::_SendXdndEnter 444 | 445 | # ---------------------------------------------------------------------------- 446 | # Command xdnd::_SendXdndPosition 447 | # ---------------------------------------------------------------------------- 448 | proc xdnd::_SendXdndPosition {rootx rooty action} { 449 | variable _dodragdrop_drag_source 450 | variable _dodragdrop_drop_target 451 | if {$_dodragdrop_drop_target < 1} return 452 | variable _dodragdrop_drop_occured 453 | if {$_dodragdrop_drop_occured} return 454 | variable _dodragdrop_drop_target_proxy 455 | variable _dodragdrop_waiting_status 456 | ## Arrange a new XdndPosition, to be send periodically... 457 | variable _dodragdrop_xdnd_position_heartbeat 458 | catch {after cancel $_dodragdrop_xdnd_position_heartbeat} 459 | set _dodragdrop_xdnd_position_heartbeat [after 200 \ 460 | [list ::tkdnd::xdnd::_SendXdndPosition $rootx $rooty $action]] 461 | if {$_dodragdrop_waiting_status} {return} 462 | # puts "XdndPosition: $_dodragdrop_drop_target $rootx $rooty $action" 463 | _send_XdndPosition $_dodragdrop_drag_source $_dodragdrop_drop_target \ 464 | $_dodragdrop_drop_target_proxy $rootx $rooty $action 465 | set _dodragdrop_waiting_status 1 466 | };# xdnd::_SendXdndPosition 467 | 468 | # ---------------------------------------------------------------------------- 469 | # Command xdnd::_HandleXdndStatus 470 | # ---------------------------------------------------------------------------- 471 | proc xdnd::_HandleXdndStatus {event} { 472 | variable _dodragdrop_drop_target 473 | variable _dodragdrop_waiting_status 474 | 475 | variable _dodragdrop_drop_target_accepts_drop 476 | variable _dodragdrop_drop_target_accepts_action 477 | set _dodragdrop_waiting_status 0 478 | foreach key {target accept want_position action x y w h} { 479 | set $key [dict get $event $key] 480 | } 481 | set _dodragdrop_drop_target_accepts_drop $accept 482 | set _dodragdrop_drop_target_accepts_action $action 483 | if {$_dodragdrop_drop_target < 1} return 484 | variable _dodragdrop_drop_occured 485 | if {$_dodragdrop_drop_occured} return 486 | _update_cursor 487 | # puts "XdndStatus: $event" 488 | };# xdnd::_HandleXdndStatus 489 | 490 | # ---------------------------------------------------------------------------- 491 | # Command xdnd::_HandleXdndFinished 492 | # ---------------------------------------------------------------------------- 493 | proc xdnd::_HandleXdndFinished {event} { 494 | variable _dodragdrop_xdnd_finished_event_after_id 495 | catch {after cancel $_dodragdrop_xdnd_finished_event_after_id} 496 | set _dodragdrop_xdnd_finished_event_after_id {} 497 | variable _dodragdrop_drop_target 498 | set _dodragdrop_drop_target 0 499 | variable _dragging 500 | if {$_dragging} {set _dragging 0} 501 | 502 | variable _dodragdrop_drop_target_accepts_drop 503 | variable _dodragdrop_drop_target_accepts_action 504 | if {[dict size $event]} { 505 | foreach key {target accept action} { 506 | set $key [dict get $event $key] 507 | } 508 | set _dodragdrop_drop_target_accepts_drop $accept 509 | set _dodragdrop_drop_target_accepts_action $action 510 | } else { 511 | set _dodragdrop_drop_target_accepts_drop 0 512 | } 513 | if {!$_dodragdrop_drop_target_accepts_drop} { 514 | set _dodragdrop_drop_target_accepts_action refuse_drop 515 | } 516 | # puts "XdndFinished: $event" 517 | };# xdnd::_HandleXdndFinished 518 | 519 | # ---------------------------------------------------------------------------- 520 | # Command xdnd::_SendXdndLeave 521 | # ---------------------------------------------------------------------------- 522 | proc xdnd::_SendXdndLeave {} { 523 | variable _dodragdrop_drag_source 524 | variable _dodragdrop_drop_target 525 | if {$_dodragdrop_drop_target < 1} return 526 | variable _dodragdrop_drop_target_proxy 527 | # puts "XdndLeave: $_dodragdrop_drop_target" 528 | _send_XdndLeave $_dodragdrop_drag_source $_dodragdrop_drop_target \ 529 | $_dodragdrop_drop_target_proxy 530 | set _dodragdrop_drop_target 0 531 | variable _dodragdrop_drop_target_accepts_drop 532 | variable _dodragdrop_drop_target_accepts_action 533 | set _dodragdrop_drop_target_accepts_drop 0 534 | set _dodragdrop_drop_target_accepts_action refuse_drop 535 | variable _dodragdrop_drop_occured 536 | if {$_dodragdrop_drop_occured} return 537 | _update_cursor 538 | };# xdnd::_SendXdndLeave 539 | 540 | # ---------------------------------------------------------------------------- 541 | # Command xdnd::_SendXdndDrop 542 | # ---------------------------------------------------------------------------- 543 | proc xdnd::_SendXdndDrop {} { 544 | variable _dodragdrop_drag_source 545 | variable _dodragdrop_drop_target 546 | if {$_dodragdrop_drop_target < 1} { 547 | ## The mouse has been released over a widget that does not accept drops. 548 | _HandleXdndFinished {} 549 | return 550 | } 551 | variable _dodragdrop_drop_occured 552 | if {$_dodragdrop_drop_occured} {return} 553 | variable _dodragdrop_drop_target_proxy 554 | variable _dodragdrop_drop_target_accepts_drop 555 | variable _dodragdrop_drop_target_accepts_action 556 | 557 | set _dodragdrop_drop_occured 1 558 | _update_cursor clock 559 | 560 | if {!$_dodragdrop_drop_target_accepts_drop} { 561 | _SendXdndLeave 562 | _HandleXdndFinished {} 563 | return 564 | } 565 | # puts "XdndDrop: $_dodragdrop_drop_target" 566 | variable _dodragdrop_drop_timestamp 567 | set _dodragdrop_drop_timestamp [_send_XdndDrop \ 568 | $_dodragdrop_drag_source $_dodragdrop_drop_target \ 569 | $_dodragdrop_drop_target_proxy] 570 | set _dodragdrop_drop_target 0 571 | # puts "XdndDrop: $_dodragdrop_drop_target" 572 | ## Arrange a timeout for receiving XdndFinished... 573 | variable _dodragdrop_xdnd_finished_event_after_id 574 | set _dodragdrop_xdnd_finished_event_after_id \ 575 | [after 10000 [list ::tkdnd::xdnd::_HandleXdndFinished {}]] 576 | };# xdnd::_SendXdndDrop 577 | 578 | # ---------------------------------------------------------------------------- 579 | # Command xdnd::_update_cursor 580 | # ---------------------------------------------------------------------------- 581 | proc xdnd::_update_cursor { {cursor {}}} { 582 | # puts "_update_cursor $cursor" 583 | variable _dodragdrop_current_cursor 584 | variable _dodragdrop_drag_source 585 | variable _dodragdrop_drop_target_accepts_drop 586 | variable _dodragdrop_drop_target_accepts_action 587 | 588 | if {![string length $cursor]} { 589 | set cursor refuse_drop 590 | if {$_dodragdrop_drop_target_accepts_drop} { 591 | set cursor $_dodragdrop_drop_target_accepts_action 592 | } 593 | } 594 | if {![string equal $cursor $_dodragdrop_current_cursor]} { 595 | _set_pointer_cursor $_dodragdrop_drag_source $cursor 596 | set _dodragdrop_current_cursor $cursor 597 | } 598 | };# xdnd::_update_cursor 599 | 600 | # ---------------------------------------------------------------------------- 601 | # Command xdnd::_default_action 602 | # ---------------------------------------------------------------------------- 603 | proc xdnd::_default_action {event} { 604 | variable _dodragdrop_actions 605 | variable _dodragdrop_actions_len 606 | if {$_dodragdrop_actions_len == 1} {return [lindex $_dodragdrop_actions 0]} 607 | 608 | set alt [dict get $event Alt] 609 | set shift [dict get $event Shift] 610 | set control [dict get $event Control] 611 | 612 | if {$shift && $control && [lsearch $_dodragdrop_actions link] != -1} { 613 | return link 614 | } elseif {$control && [lsearch $_dodragdrop_actions copy] != -1} { 615 | return copy 616 | } elseif {$shift && [lsearch $_dodragdrop_actions move] != -1} { 617 | return move 618 | } elseif {$alt && [lsearch $_dodragdrop_actions link] != -1} { 619 | return link 620 | } 621 | return default 622 | };# xdnd::_default_action 623 | 624 | # ---------------------------------------------------------------------------- 625 | # Command xdnd::getFormatForType 626 | # ---------------------------------------------------------------------------- 627 | proc xdnd::getFormatForType {type} { 628 | switch -glob [string tolower $type] { 629 | text/plain\;charset=utf-8 - 630 | text/html\;charset=utf-8 - 631 | utf8_string {set format UTF8_STRING} 632 | text/html - 633 | text/plain - 634 | string - 635 | text - 636 | compound_text {set format STRING} 637 | text/uri-list* {set format UTF8_STRING} 638 | application/x-color {set format $type} 639 | default {set format $type} 640 | } 641 | return $format 642 | };# xdnd::getFormatForType 643 | 644 | # ---------------------------------------------------------------------------- 645 | # Command xdnd::registerSelectionHandler 646 | # ---------------------------------------------------------------------------- 647 | proc xdnd::registerSelectionHandler {source types} { 648 | foreach type $types { 649 | selection handle -selection XdndSelection \ 650 | -type $type \ 651 | -format [getFormatForType $type] \ 652 | $source [list ::tkdnd::xdnd::_SendData $type] 653 | } 654 | };# xdnd::registerSelectionHandler 655 | 656 | # ---------------------------------------------------------------------------- 657 | # Command xdnd::unregisterSelectionHandler 658 | # ---------------------------------------------------------------------------- 659 | proc xdnd::unregisterSelectionHandler {source types} { 660 | foreach type $types { 661 | catch { 662 | selection handle -selection XdndSelection \ 663 | -type $type \ 664 | -format [getFormatForType $type] \ 665 | $source {} 666 | } 667 | } 668 | };# xdnd::unregisterSelectionHandler 669 | 670 | # ---------------------------------------------------------------------------- 671 | # Command xdnd::_convert_to_unsigned 672 | # ---------------------------------------------------------------------------- 673 | proc xdnd::_convert_to_unsigned {data format} { 674 | switch $format { 675 | 8 { set mask 0xff } 676 | 16 { set mask 0xffff } 677 | 32 { set mask 0xffffff } 678 | default {error "unsupported format $format"} 679 | } 680 | ## Convert signed integer into unsigned... 681 | set d [list] 682 | foreach num $data { 683 | lappend d [expr { $num & $mask }] 684 | } 685 | return $d 686 | };# xdnd::_convert_to_unsigned 687 | 688 | # ---------------------------------------------------------------------------- 689 | # Command xdnd::_SendData 690 | # ---------------------------------------------------------------------------- 691 | proc xdnd::_SendData {type offset bytes args} { 692 | variable _dodragdrop_drag_source 693 | variable _dodragdrop_types 694 | variable _dodragdrop_data 695 | variable _dodragdrop_transfer_data 696 | 697 | ## The variable _dodragdrop_data contains a list of data, one for each 698 | ## type in the _dodragdrop_types variable. We have to search types, and find 699 | ## the corresponding entry in the _dodragdrop_data list. 700 | set index [lsearch $_dodragdrop_types $type] 701 | if {$index < 0} { 702 | error "unable to locate data suitable for type \"$type\"" 703 | } 704 | set typed_data [lindex $_dodragdrop_data $index] 705 | set format 8 706 | if {$offset == 0} { 707 | ## Prepare the data to be transfered... 708 | switch -glob $type { 709 | text/plain* - UTF8_STRING - STRING - TEXT - COMPOUND_TEXT { 710 | binary scan [encoding convertto utf-8 $typed_data] \ 711 | c* _dodragdrop_transfer_data 712 | set _dodragdrop_transfer_data \ 713 | [_convert_to_unsigned $_dodragdrop_transfer_data $format] 714 | } 715 | text/uri-list* { 716 | set files [list] 717 | foreach file $typed_data { 718 | switch -glob $file { 719 | *://* {lappend files $file} 720 | default {lappend files file://$file} 721 | } 722 | } 723 | binary scan [encoding convertto utf-8 "[join $files \r\n]\r\n"] \ 724 | c* _dodragdrop_transfer_data 725 | set _dodragdrop_transfer_data \ 726 | [_convert_to_unsigned $_dodragdrop_transfer_data $format] 727 | } 728 | application/x-color { 729 | set format 16 730 | ## Try to understand the provided data: we accept a standard Tk colour, 731 | ## or a list of 3 values (red green blue) or a list of 4 values 732 | ## (red green blue opacity). 733 | switch [llength $typed_data] { 734 | 1 { set color [winfo rgb $_dodragdrop_drag_source $typed_data] 735 | lappend color 65535 } 736 | 3 { set color $typed_data; lappend color 65535 } 737 | 4 { set color $typed_data } 738 | default {error "unknown color data: \"$typed_data\""} 739 | } 740 | ## Convert the 4 elements into 16 bit values... 741 | set _dodragdrop_transfer_data [list] 742 | foreach c $color { 743 | lappend _dodragdrop_transfer_data [format 0x%04X $c] 744 | } 745 | } 746 | default { 747 | set format 32 748 | binary scan $typed_data c* _dodragdrop_transfer_data 749 | } 750 | } 751 | } 752 | 753 | ## 754 | ## Data has been split into bytes. Count the bytes requested, and return them 755 | ## 756 | set data [lrange $_dodragdrop_transfer_data $offset [expr {$offset+$bytes-1}]] 757 | switch $format { 758 | 8 { 759 | set data [encoding convertfrom utf-8 [binary format c* $data]] 760 | } 761 | 16 { 762 | variable _dodragdrop_selection_requestor 763 | if {$_dodragdrop_selection_requestor} { 764 | ## Tk selection cannot process this format (only 8 & 32 supported). 765 | ## Call our XChangeProperty... 766 | set numItems [llength $data] 767 | variable _dodragdrop_selection_property 768 | variable _dodragdrop_selection_selection 769 | variable _dodragdrop_selection_target 770 | variable _dodragdrop_selection_time 771 | XChangeProperty $_dodragdrop_drag_source \ 772 | $_dodragdrop_selection_requestor \ 773 | $_dodragdrop_selection_property \ 774 | $_dodragdrop_selection_target \ 775 | $format \ 776 | $_dodragdrop_selection_time \ 777 | $data $numItems 778 | return -code break 779 | } 780 | } 781 | 32 { 782 | } 783 | default { 784 | error "unsupported format $format" 785 | } 786 | } 787 | # puts "SendData: $type $offset $bytes $args ($typed_data)" 788 | # puts " $data" 789 | return $data 790 | };# xdnd::_SendData 791 | -------------------------------------------------------------------------------- /source/tkdnd2.8/tkdnd_windows.tcl: -------------------------------------------------------------------------------- 1 | # 2 | # tkdnd_windows.tcl -- 3 | # 4 | # This file implements some utility procedures that are used by the TkDND 5 | # package. 6 | # 7 | # This software is copyrighted by: 8 | # George Petasis, National Centre for Scientific Research "Demokritos", 9 | # Aghia Paraskevi, Athens, Greece. 10 | # e-mail: petasis@iit.demokritos.gr 11 | # 12 | # The following terms apply to all files associated 13 | # with the software unless explicitly disclaimed in individual files. 14 | # 15 | # The authors hereby grant permission to use, copy, modify, distribute, 16 | # and license this software and its documentation for any purpose, provided 17 | # that existing copyright notices are retained in all copies and that this 18 | # notice is included verbatim in any distributions. No written agreement, 19 | # license, or royalty fee is required for any of the authorized uses. 20 | # Modifications to this software may be copyrighted by their authors 21 | # and need not follow the licensing terms described here, provided that 22 | # the new terms are clearly indicated on the first page of each file where 23 | # they apply. 24 | # 25 | # IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY 26 | # FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 27 | # ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY 28 | # DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE 29 | # POSSIBILITY OF SUCH DAMAGE. 30 | # 31 | # THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, 32 | # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, 33 | # FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE 34 | # IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE 35 | # NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR 36 | # MODIFICATIONS. 37 | # 38 | 39 | namespace eval olednd { 40 | 41 | proc initialise { } { 42 | ## Mapping from platform types to TkDND types... 43 | ::tkdnd::generic::initialise_platform_to_tkdnd_types [list \ 44 | CF_UNICODETEXT DND_Text \ 45 | CF_TEXT DND_Text \ 46 | CF_HDROP DND_Files \ 47 | FileGroupDescriptor DND_Files \ 48 | FileGroupDescriptorW DND_Files \ 49 | CF_HTML DND_HTML \ 50 | {HTML Format} DND_HTML \ 51 | CF_RTF DND_RTF \ 52 | CF_RTFTEXT DND_RTF \ 53 | {Rich Text Format} DND_RTF \ 54 | ] 55 | 56 | ## Mapping from TkDND types to platform types... 57 | ::tkdnd::generic::initialise_tkdnd_to_platform_types [list \ 58 | DND_Text {CF_UNICODETEXT CF_TEXT} \ 59 | DND_Files {CF_HDROP} \ 60 | DND_HTML {CF_HTML {HTML Format}} \ 61 | DND_RTF {CF_RTF CF_RTFTEXT {Rich Text Format}} \ 62 | ] 63 | };# initialise 64 | 65 | };# namespace olednd 66 | 67 | # ---------------------------------------------------------------------------- 68 | # Command olednd::HandleDragEnter 69 | # ---------------------------------------------------------------------------- 70 | proc olednd::HandleDragEnter { drop_target typelist actionlist pressedkeys 71 | rootX rootY codelist } { 72 | focus $drop_target 73 | ::tkdnd::generic::HandleEnter $drop_target 0 $typelist \ 74 | $codelist $actionlist $pressedkeys 75 | set action [::tkdnd::generic::HandlePosition $drop_target {} \ 76 | $pressedkeys $rootX $rootY] 77 | if {$::tkdnd::_auto_update} {update} 78 | return $action 79 | };# olednd::HandleDragEnter 80 | 81 | # ---------------------------------------------------------------------------- 82 | # Command olednd::HandleDragOver 83 | # ---------------------------------------------------------------------------- 84 | proc olednd::HandleDragOver { drop_target pressedkeys rootX rootY } { 85 | set action [::tkdnd::generic::HandlePosition $drop_target {} \ 86 | $pressedkeys $rootX $rootY] 87 | if {$::tkdnd::_auto_update} {update} 88 | return $action 89 | };# olednd::HandleDragOver 90 | 91 | # ---------------------------------------------------------------------------- 92 | # Command olednd::HandleDragLeave 93 | # ---------------------------------------------------------------------------- 94 | proc olednd::HandleDragLeave { drop_target } { 95 | ::tkdnd::generic::HandleLeave 96 | if {$::tkdnd::_auto_update} {update} 97 | };# olednd::HandleDragLeave 98 | 99 | # ---------------------------------------------------------------------------- 100 | # Command olednd::HandleXdndDrop 101 | # ---------------------------------------------------------------------------- 102 | proc olednd::HandleDrop { drop_target pressedkeys rootX rootY type data } { 103 | ::tkdnd::generic::SetDroppedData [normalise_data $type $data] 104 | set action [::tkdnd::generic::HandleDrop $drop_target {} \ 105 | $pressedkeys $rootX $rootY 0] 106 | if {$::tkdnd::_auto_update} {update} 107 | return $action 108 | };# olednd::HandleXdndDrop 109 | 110 | # ---------------------------------------------------------------------------- 111 | # Command olednd::GetDragSourceCommonTypes 112 | # ---------------------------------------------------------------------------- 113 | proc olednd::GetDragSourceCommonTypes { drop_target } { 114 | ::tkdnd::generic::GetDragSourceCommonTypes 115 | };# olednd::GetDragSourceCommonTypes 116 | 117 | # ---------------------------------------------------------------------------- 118 | # Command olednd::platform_specific_types 119 | # ---------------------------------------------------------------------------- 120 | proc olednd::platform_specific_types { types } { 121 | ::tkdnd::generic::platform_specific_types $types 122 | }; # olednd::platform_specific_types 123 | 124 | # ---------------------------------------------------------------------------- 125 | # Command olednd::platform_specific_type 126 | # ---------------------------------------------------------------------------- 127 | proc olednd::platform_specific_type { type } { 128 | ::tkdnd::generic::platform_specific_type $type 129 | }; # olednd::platform_specific_type 130 | 131 | # ---------------------------------------------------------------------------- 132 | # Command tkdnd::platform_independent_types 133 | # ---------------------------------------------------------------------------- 134 | proc ::tkdnd::platform_independent_types { types } { 135 | ::tkdnd::generic::platform_independent_types $types 136 | }; # tkdnd::platform_independent_types 137 | 138 | # ---------------------------------------------------------------------------- 139 | # Command olednd::platform_independent_type 140 | # ---------------------------------------------------------------------------- 141 | proc olednd::platform_independent_type { type } { 142 | ::tkdnd::generic::platform_independent_type $type 143 | }; # olednd::platform_independent_type 144 | 145 | # ---------------------------------------------------------------------------- 146 | # Command olednd::normalise_data 147 | # ---------------------------------------------------------------------------- 148 | proc olednd::normalise_data { type data } { 149 | switch [lindex [::tkdnd::generic::platform_independent_type $type] 0] { 150 | DND_Text {return $data} 151 | DND_Files {return $data} 152 | DND_HTML {return [encoding convertfrom utf-8 $data]} 153 | default {return $data} 154 | } 155 | }; # olednd::normalise_data 156 | -------------------------------------------------------------------------------- /source/updater.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import argparse 3 | import os 4 | import zipfile 5 | import shutil 6 | import platform 7 | import subprocess 8 | import sys 9 | 10 | def main(): 11 | parser = argparse.ArgumentParser(description='Updater script for BlenderManager.') 12 | parser.add_argument('--zip-path', required=True, help='Path to the downloaded zip file.') 13 | 14 | args = parser.parse_args() 15 | zip_path = args.zip_path 16 | app_dir = os.getcwd() 17 | extract_dir = os.path.join(app_dir, "blender_manager_update") 18 | 19 | 20 | try: 21 | with zipfile.ZipFile(zip_path, 'r') as zip_ref: 22 | zip_ref.extractall(extract_dir) 23 | except zipfile.BadZipFile as e: 24 | print(f"Error: Failed to extract zip file {zip_path}. Error: {e}") 25 | sys.exit(1) 26 | 27 | try: 28 | extracted_folders = [ 29 | name for name in os.listdir(extract_dir) if os.path.isdir(os.path.join(extract_dir, name)) 30 | ] 31 | 32 | if len(extracted_folders) != 1: 33 | print(f"Error: Expected one top-level folder in zip, found {len(extracted_folders)}.") 34 | sys.exit(1) 35 | 36 | top_level_folder = os.path.join(extract_dir, extracted_folders[0]) 37 | 38 | for root, dirs, files in os.walk(top_level_folder): 39 | rel_path = os.path.relpath(root, top_level_folder) 40 | dest_dir = os.path.join(app_dir, rel_path) 41 | if not os.path.exists(dest_dir): 42 | os.makedirs(dest_dir) 43 | for file in files: 44 | src_file = os.path.join(root, file) 45 | dest_file = os.path.join(dest_dir, file) 46 | shutil.move(src_file, dest_file) 47 | except Exception as e: 48 | print(f"Error: Failed to move files from {top_level_folder} to {app_dir}. Error: {e}") 49 | sys.exit(1) 50 | 51 | try: 52 | shutil.rmtree(extract_dir) 53 | except Exception as e: 54 | print(f"Error: Failed to delete temporary directory {extract_dir}. Error: {e}") 55 | 56 | try: 57 | if os.path.exists(zip_path): 58 | os.remove(zip_path) 59 | except Exception as e: 60 | print(f"Error: Failed to delete zip file {zip_path}. Error: {e}") 61 | 62 | current_os = platform.system().lower() 63 | try: 64 | if current_os == 'windows': 65 | app_executable = os.path.join(app_dir, 'blender_manager.exe') 66 | if os.path.exists(app_executable): 67 | subprocess.Popen([app_executable]) 68 | else: 69 | app_script = os.path.join(app_dir, 'blender_manager.py') 70 | subprocess.Popen(['python', app_script]) 71 | elif current_os in ['darwin', 'linux']: 72 | app_script = os.path.join(app_dir, 'blender_manager.py') 73 | subprocess.Popen(['python3', app_script]) 74 | else: 75 | print(f"Error: Unsupported operating system {current_os}.") 76 | except Exception as e: 77 | print(f"Error: Failed to restart application. Error: {e}") 78 | 79 | if __name__ == '__main__': 80 | main() 81 | 82 | --------------------------------------------------------------------------------