Skip to content

Commit be8de94

Browse files
committed
new version
addresses points made by Ben Cohen
1 parent 5c7664b commit be8de94

File tree

6 files changed

+190
-98
lines changed

6 files changed

+190
-98
lines changed

GDL.py

Lines changed: 118 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -9,32 +9,22 @@
99
PACKAGE_SETTINGS = "GDL.sublime-settings"
1010
DEFAULT_AC_PATH = "C:/Program Files/GRAPHISOFT/ARCHICAD 23"
1111

12-
def check_system():
13-
""" Returns the file ending of executables depending on the
14-
operating system of the user.
15-
"""
16-
if sys.platform.startswith('darwin'): # OSX
17-
return "Contents/MacOS/LP_XMLConverter.app/Contents/MacOS/LP_XMLConverter"
18-
elif sys.platform.startswith('win'): # Windows
19-
return "LP_XMLConverter.exe"
20-
else:
21-
sublime.error_message("GDL build error: Your OS is not supported.")
22-
return
2312

2413
def save_all_files():
25-
""" Saves all files open in sublime.
14+
""" Saves all files open in Sublime.
2615
Mimics the 'save on build' behavior of Sublime Text.
2716
"""
2817
for window in sublime.windows():
2918
for view in window.views():
3019
if view.file_name() and view.is_dirty():
3120
view.run_command("save")
3221

33-
def get_project_data(view, invoke): # invoke is either 'to-hsf' or 'to-gsm'
22+
def get_project_data(view, invoke):
3423
""" Gets the data of the .sublime-project file.
3524
Returns additional arguments for the commandline operation,
3625
if the user has set any.
3726
"""
27+
# invoke options: 'to-hsf' / 'to-gsm' / 'proj_gsm_path'
3828
project_data = view.window().project_data()
3929
if not project_data:
4030
sublime.error_message("You must create a project first! (Project > Save Project As...)")
@@ -51,25 +41,25 @@ def get_project_data(view, invoke): # invoke is either 'to-hsf' or 'to-gsm'
5141
else:
5242
sublime.error_message("Something went wrong.")
5343
return
54-
44+
45+
# Future addition. Sadly not working as by now.
5546
# class AutocompleteCaps(sublime_plugin.EventListener):
5647
# def on_query_completions(self, view, prefix, locations):
5748
# return suggestions
58-
59-
# go to
60-
# http://gdl.graphisoft.com/tips-and-tricks/how-to-use-the-lp_xmlconverter-tool
61-
# for detailed information
62-
class HsfBuildCommand(sublime_plugin.WindowCommand):
49+
50+
51+
class Builder(sublime_plugin.WindowCommand):
6352

6453
def run(self, *args, **kwargs):
65-
self.os = check_system()
54+
self.lp_conv_path = self.check_system()
55+
self.pckg_settings = sublime.load_settings(PACKAGE_SETTINGS)
56+
self.AC_path = str(self.pckg_settings.get("AC_path", DEFAULT_AC_PATH))
57+
self.converter = os.path.join(self.AC_path, self.lp_conv_path)
58+
6659
self.view = self.window.active_view()
6760
if self.view.settings().get("auto_save", True):
6861
save_all_files()
6962

70-
settings = sublime.load_settings(PACKAGE_SETTINGS)
71-
self.AC_path = str(settings.get("AC_path", DEFAULT_AC_PATH))
72-
7363
folders = self.window.folders()
7464
if len(folders) <= 0:
7565
sublime.error_message("GDL build command error: You must have a project open.")
@@ -82,16 +72,25 @@ def run(self, *args, **kwargs):
8272
self.multipleFolders = True
8373
self.pick_project_folder(folders)
8474

85-
def on_done_proj(self):
86-
# this needs to be in its own function, because
87-
# the sublime text quick panel works asynchronous
88-
self.find_gsm()
75+
def check_system(self):
76+
""" Returns the path to the LP_XML converter.
77+
"""
78+
if sys.platform.startswith('darwin'): # OSX
79+
self.os_win = "false"
80+
return "Contents/MacOS/LP_XMLConverter.app/Contents/MacOS/LP_XMLConverter"
81+
elif sys.platform.startswith('win'): # Windows
82+
self.os_win = "true"
83+
return "LP_XMLConverter.exe"
84+
else:
85+
sublime.error_message("GDL build error: Your OS is not supported.")
86+
return
8987

90-
def on_done_file(self):
91-
self.cmdargs = get_project_data(self.view, 'to-hsf')
92-
self.run_hsf()
88+
def normpath(self, path):
89+
return '"{}"'.format(os.path.normpath(path))
9390

9491
def pick_project_folder(self, folders):
92+
""" Gets called if there are multiple folders in the project.
93+
"""
9594
folderNames = []
9695
for folder in folders:
9796
index = folder.rfind('/') + 1
@@ -110,6 +109,31 @@ def select_project(self, select):
110109
self.project_folder = folders[select]
111110
self.on_done_proj() # go on here
112111

112+
def show_quick_panel(self, options, done):
113+
""" Shows the Sublime Text quick panel with the invoked options. """
114+
# Sublime Text 3 requires a short timeout between quick panels
115+
sublime.set_timeout(lambda: self.window.show_quick_panel(options, done), 10)
116+
117+
118+
# @classmethod
119+
# def is_enabled(self):
120+
# return "/GDL/" in self.window.active_view().settings().get("syntax")
121+
122+
123+
# go to
124+
# http://gdl.graphisoft.com/tips-and-tricks/how-to-use-the-lp_xmlconverter-tool
125+
# for detailed information
126+
class HsfBuildCommand(Builder):
127+
""" Converts a GSM into human readable GDL scripts. """
128+
129+
def run(self, *args, **kwargs):
130+
""" Sublime Text will call this function. """
131+
super().run(self)
132+
133+
def on_done_proj(self):
134+
# own function because quick panel is async
135+
self.find_gsm()
136+
113137
def find_gsm(self):
114138
self.files = []
115139
# r=root, d=directories, f=files
@@ -127,78 +151,47 @@ def find_gsm(self):
127151
self.file_to_convert = self.files[0]
128152
self.on_done_file() # go on here
129153

154+
def on_done_file(self):
155+
self.cmdargs = get_project_data(self.view, 'to-hsf')
156+
self.run_hsf()
157+
130158
def select_gsm(self, select):
131159
if select < 0:
132160
return
133161
self.file_to_convert = self.files[select]
134162
self.on_done_file() # go on here
135-
136-
# Sublime Text 3 requires a short timeout between quick panels
137-
def show_quick_panel(self, options, done):
138-
sublime.set_timeout(lambda: self.window.show_quick_panel(options, done), 10)
139-
163+
140164
def run_hsf(self, ):
141-
converter = os.path.join(self.AC_path, self.os)
142-
cmd = [converter, "libpart2hsf", self.cmdargs, self.file_to_convert, self.project_folder] # cmd, source, dest
165+
""" Invokes the LP_XML converter.
166+
"""
167+
self.converter = super().normpath(self.converter)
168+
self.file_to_convert = super().normpath(self.file_to_convert)
169+
self.project_folder = super().normpath(self.project_folder)
170+
171+
cmd = [self.converter, "libpart2hsf", self.cmdargs, self.file_to_convert, self.project_folder] # cmd, source, dest
143172
cmd = list(filter(None, cmd)) # filters out the empty cmdargs. otherwise Macs get hiccups. sigh.
144-
log.debug("GDL Command run: " + " ".join(cmd))
145-
execCMD = {"cmd": cmd}
173+
cmd = " ".join(cmd)
174+
175+
#log.debug("GDL Command run: " + " ".join(cmd))
176+
execCMD = {"shell_cmd": cmd}
146177

147178
self.window.run_command("exec", execCMD)
148179

149180
############################################################################
150-
class LibpartBuildCommand(sublime_plugin.WindowCommand):
181+
class LibpartBuildCommand(Builder):
182+
""" Builds a GSM from the GDL scripts in the project. """
151183

152184
def run(self, *args, **kwargs):
153-
self.os = check_system()
154-
self.view = self.window.active_view()
155-
if self.view.settings().get("auto_save", True):
156-
save_all_files()
157-
158-
settings = sublime.load_settings(PACKAGE_SETTINGS)
159-
self.AC_path = str(settings.get("AC_path", DEFAULT_AC_PATH))
160-
161-
folders = self.window.folders()
162-
if len(folders) <= 0:
163-
sublime.error_message("GDL build command error: You must have a project open.")
164-
else:
165-
if len(folders) == 1:
166-
self.multipleFolders = False
167-
self.project_folder = folders[0]
168-
self.on_done_proj() # go on here
169-
else:
170-
self.multipleFolders = True
171-
self.pick_project_folder(folders)
185+
""" Sublime Text will call this function. """
186+
super().run(self)
172187

173188
def on_done_proj(self):
174189
# own function because quick panel is async
175190
self.find_hsf()
176191

177-
def on_done_file(self):
178-
self.gsm_name = self.folder_to_convert + ".gsm"
179-
self.cmdargs = get_project_data(self.view, 'to-gsm')
180-
self.run_libpart()
181-
182-
def pick_project_folder(self, folders):
183-
folderNames = []
184-
for folder in folders:
185-
index = folder.rfind('/') + 1
186-
if index > 0:
187-
folderNames.append(folder[index:])
188-
else:
189-
folderNames.append(folder)
190-
191-
# self.sel_proj will be called once, with the index of the selected item
192-
self.show_quick_panel(folderNames, self.select_project)
193-
194-
def select_project(self, select):
195-
folders = self.window.folders()
196-
if select < 0: # will be -1 if panel was cancelled
197-
return
198-
self.project_folder = folders[select]
199-
self.on_done_proj() # go on here
200-
201192
def find_hsf(self):
193+
""" Finds all possible folders for converting to GSM.
194+
"""
202195
self.folders = [fldr for fldr in os.listdir(self.project_folder) if os.path.isdir(os.path.join(self.project_folder, fldr))]
203196

204197
if len(self.folders) <= 0:
@@ -211,21 +204,56 @@ def find_hsf(self):
211204
self.on_done_file() # go on here
212205

213206
def select_hsf(self, select):
207+
""" Selects on of the possible of folders of the find_hsf() def.
208+
"""
214209
folders = self.folders
215210
if select < 0: # will be -1 if panel was cancelled
216211
return
217212
self.folder_to_convert = os.path.join(self.project_folder, folders[select])
218213
self.on_done_file() # go on here
219214

220-
# Sublime Text 3 requires a short timeout between quick panels
221-
def show_quick_panel(self, options, done):
222-
sublime.set_timeout(lambda: self.window.show_quick_panel(options, done), 10)
215+
def on_done_file(self):
216+
""" Path handling for GSM output.
217+
"""
218+
self.global_gsm_path = str(self.pckg_settings.get("global_gsm_path", ""))
219+
self.proj_gsm_path = get_project_data(self.view, 'proj_gsm_path')
220+
221+
output_path = ""
222+
if self.proj_gsm_path: # project based path takes precedence
223+
if self.proj_gsm_path != "default":
224+
# only set the path if its not 'default' which mimics the standard behavior
225+
# TODO implement path check here
226+
output_path = self.proj_gsm_path
227+
elif self.global_gsm_path: # otherwise take global path, if set
228+
output_path = self.global_gsm_path
229+
230+
if not output_path:
231+
# default path => not global or proj path is set, or proj path says 'default'
232+
output_path = os.path.dirname(self.folder_to_convert)
233+
234+
gsm_name = os.path.basename(os.path.normpath(self.folder_to_convert)) + ".gsm"
235+
self.gsm_path = os.path.join(output_path, gsm_name)
236+
237+
self.cmdargs = get_project_data(self.view, 'to-gsm')
238+
239+
self.run_libpart()
223240

224241
def run_libpart(self):
225-
converter = os.path.join(self.AC_path, self.os)
226-
cmd = [converter, "hsf2libpart", self.cmdargs, self.folder_to_convert, self.gsm_name] # cmd, source, dest
242+
""" Invokes the LP_XML converter.
243+
"""
244+
self.converter = super().normpath(self.converter)
245+
self.folder_to_convert = super().normpath(self.folder_to_convert)
246+
self.gsm_path = super().normpath(self.gsm_path)
247+
248+
cmd = [self.converter, "hsf2libpart", self.cmdargs, self.folder_to_convert, self.gsm_path] # cmd, source, dest
227249
cmd = list(filter(None, cmd)) # filters out the empty cmdargs. otherwise Macs get hiccups. sigh.
228-
log.debug("GDL Command run: " + " ".join(cmd))
229-
execCMD = {"cmd": cmd}
250+
cmd = " ".join(cmd)
251+
# log.debug("GDL Command run: " + cmd)
252+
253+
# if you use `cmd` instead of `shell_cmd` you will get the infamous [Winerror 5]
254+
# see: https://forum.sublimetext.com/t/winerror-5-access-is-denied/
255+
# however, for `shell_cmd` to work we need to pass a string, not a list (!)
256+
execCMD = {"shell_cmd": cmd}
230257

231258
self.window.run_command("exec", execCMD)
259+

GDL.sublime-settings

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@
99

1010
"rulers": [80,120],
1111

12-
// Path to the current Archicad, where the LP_XMLConverter resides
12+
// Path to the current Archicad, where the LP_XMLConverter resides.
1313
// example Win:
14-
"AC_path": "C:/Program Files/GRAPHISOFT/ARCHICAD 23"
14+
"AC_path": "C:/Program Files/GRAPHISOFT/ARCHICAD 23",
1515
// example Mac:
1616
// "/Applications/GRAPHISOFT/AC23B3/ARCHICAD 23.app"
17+
18+
// If you specify a path here all conversions from HSF to GSM
19+
// will go into this place. This is helpful if you want to deploy
20+
// into a central library already linked in Archicad.
21+
"global_gsm_path": "",
1722
}

Messages/install.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ Put your settings into the right pane and you're good to go!
2424

2525
Whenever possible, use the "project" feature of Sublime Text, so you can benefit from automatic libpart conversion (via the "LP_XMLConverter") – which means avoiding the pain of copy & pasting every piece of code back into ARCHICAD.
2626

27+
Don't forget to set up your Package Settings!
28+
2729
=========================
2830
Happy coding!

Messages/v3.1.0.txt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
██████╗ ██████╗ ██╗
3+
██╔════╝ ██╔══██╗██║
4+
██║ ███╗██║ ██║██║
5+
██║ ██║██║ ██║██║
6+
╚██████╔╝██████╔╝███████╗
7+
╚═════╝ ╚═════╝ ╚══════╝
8+
9+
GDL Sublime Text package
10+
has successfully updated
11+
12+
*************************
13+
14+
VERSION 3.1.0
15+
16+
*************************
17+
18+
• New Package Setting:
19+
`global_gsm_path`
20+
You can now define a global default path where any
21+
GSMs should be deployed to.
22+
This is useful if you have a central library
23+
already linked in Archicad.
24+
(Thanks to Ben Cohen, http://www.4dlibrary.com.au/,
25+
for the suggestion.)
26+
27+
• New Project Setting:
28+
`proj_gsm_path`
29+
You can also set a conversion path on project basis.
30+
This overrides the default (which is project root)
31+
and *also* the new `global_gsm_path` from the
32+
package setting!
33+
Instead of putting a path we can also write `default`
34+
which mimics the standard behavior (building the GSM
35+
right next to the HSF).
36+
37+
Note: There's no path checking implemented at the moment!
38+
You have to take care by yourself that you're allowed to
39+
write at the paths accordingly.

0 commit comments

Comments
 (0)