-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall.py
More file actions
165 lines (138 loc) · 6.15 KB
/
Copy pathinstall.py
File metadata and controls
165 lines (138 loc) · 6.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#!/usr/bin/env python3
#
# Copyright (C) 2026 Junaid Qadir Shekhanzai
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <https://www.gnu.org/licenses/>.
#
"""Install "True Center Align" into Inkscape on macOS, Linux or Windows.
Four pieces go into the Inkscape user profile:
extensions/true_center_align/ the extension itself
icons/ the toolbar icon, as a hicolor theme
ui/align-and-distribute.ui a patched dialog carrying the new button
keys/default.xml the keyboard shortcut
Only the first is what Inkscape's Extension Manager can distribute; the rest is
why this script exists. Run it again after upgrading Inkscape to rebuild the
patched dialog against the new version, then restart Inkscape.
"""
import argparse
import shutil
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
sys.path.insert(0, str(HERE / "tools"))
import inkscape_paths # noqa: E402
import merge_shortcut # noqa: E402
import patch_align_ui # noqa: E402
EXTENSION_FILES = ("true_center_align.py", "true_center_align.inx")
EXTENSION_DIRS = ("icons",)
PACKAGE_NAME = "true_center_align"
class Installer:
def __init__(self, profile: Path, share: Path, dry_run: bool):
self.profile = profile
self.share = share
self.dry_run = dry_run
self.steps = []
def note(self, message):
prefix = "would " if self.dry_run else ""
print(f" {prefix}{message}")
def copy_tree(self, src: Path, dst: Path):
self.note(f"copy {src.name}/ -> {dst}")
if not self.dry_run:
shutil.copytree(src, dst, dirs_exist_ok=True)
def copy_file(self, src: Path, dst: Path):
self.note(f"copy {src.name} -> {dst}")
if not self.dry_run:
dst.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src, dst)
def install_extension(self):
print("==> extension")
target = self.profile / "extensions" / PACKAGE_NAME
if target.resolve() == HERE:
print(f" already installed in place at {target}")
return
for name in EXTENSION_FILES:
self.copy_file(HERE / name, target / name)
for name in EXTENSION_DIRS:
source = HERE / name
if source.is_dir():
self.copy_tree(source, target / name)
def install_icons(self):
print("==> icons")
self.copy_tree(HERE / "profile" / "icons", self.profile / "icons")
def install_dialog(self):
print("==> Align & Distribute button")
out = self.profile / "ui" / "align-and-distribute.ui"
if out.is_file():
backup = out.with_suffix(".ui.bak")
self.note(f"back up existing dialog -> {backup}")
if not self.dry_run:
shutil.move(str(out), str(backup))
self.note(f"generate {out} from {self.share}")
if not self.dry_run:
patch_align_ui.write_patched(self.share, out)
def install_shortcut(self, keys: str):
print("==> keyboard shortcut")
clashes = merge_shortcut.conflicts(self.share, keys)
for action in clashes:
print(f" warning: {keys} is already bound to {action}", file=sys.stderr)
keys_file = self.profile / "keys" / "default.xml"
if self.dry_run:
self.note(f"bind {keys} in {keys_file}")
else:
print(f" {merge_shortcut.merge(keys_file, keys)}")
def main() -> int:
ap = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
ap.add_argument("--profile", type=Path, help="Inkscape user profile directory")
ap.add_argument("--share", type=Path, help="Inkscape share/inkscape directory")
ap.add_argument("--inkscape", type=Path, help="Inkscape executable to interrogate")
ap.add_argument("--shortcut", default=merge_shortcut.DEFAULT_KEYS,
help="accelerator to bind (default: %(default)s)")
ap.add_argument("--no-shortcut", action="store_true", help="skip the shortcut")
ap.add_argument("--no-dialog", action="store_true",
help="skip the Align & Distribute button")
ap.add_argument("--dry-run", action="store_true",
help="report what would happen, change nothing")
args = ap.parse_args()
binary = args.inkscape or inkscape_paths.find_binary()
profile = args.profile or inkscape_paths.profile_dir(binary)
share = args.share or inkscape_paths.share_dir(binary)
print(f"platform: {sys.platform}")
print(f"inkscape: {binary or '(not found)'}")
print(f"profile: {profile or '(unknown)'}")
print(f"share: {share or '(unknown)'}")
print()
if profile is None:
print("error: could not locate Inkscape's user profile directory.\n"
" pass --profile /path/to/inkscape (the folder holding "
"preferences.xml).", file=sys.stderr)
return 1
installer = Installer(profile, share, args.dry_run)
installer.install_extension()
installer.install_icons()
if share is None:
print("\nwarning: Inkscape's share directory was not found, so the Align "
"& Distribute\n button and the shortcut conflict check were "
"skipped. Pass --share\n /path/to/share/inkscape to enable "
"them.", file=sys.stderr)
else:
if not args.no_dialog:
installer.install_dialog()
if not args.no_shortcut:
installer.install_shortcut(args.shortcut)
print("\nDone. Restart Inkscape." if not args.dry_run else "\nDry run: nothing changed.")
return 0
if __name__ == "__main__":
sys.exit(main())