├── .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 | 
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 | 
17 |
18 | 
19 |
20 | 
21 |
22 | 
23 |
24 | 
25 |
26 | 
27 |
28 | 
29 |
30 | 
31 |
32 | 
33 |
34 | 
35 |
36 | 
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 |
--------------------------------------------------------------------------------