blob: 389a7744867ab682bd02738eccbb0ba066718d40 [file] [log] [blame]
Jack Jansen7571f301995-07-29 13:48:41 +00001"""Edit the Python Preferences file."""
Jack Jansen822a30b1996-04-10 14:52:18 +00002#
3# This program is getting more and more clunky. It should really
4# be rewritten in a modeless way some time soon.
Jack Jansen7571f301995-07-29 13:48:41 +00005
6from Dlg import *
7from Events import *
8from Res import *
9import string
10import struct
11import macfs
12import MacOS
13import os
14import sys
15import Res # For Res.Error
16
Jack Jansen84872291996-10-22 15:33:02 +000017# Resource in the Python resource chain
18PREFNAME_NAME="PythonPreferenceFileName"
19
Jack Jansen7571f301995-07-29 13:48:41 +000020# resource IDs in our own resources (dialogs, etc)
21MESSAGE_ID = 256
22
Jack Jansen145c92d1996-10-11 11:30:26 +000023DIALOG_ID = 511
Jack Jansen7571f301995-07-29 13:48:41 +000024TEXT_ITEM = 1
25OK_ITEM = 2
26CANCEL_ITEM = 3
Jack Jansendb0bace1996-04-04 15:38:44 +000027DIR_ITEM = 4
28TITLE_ITEM = 5
Jack Jansen822a30b1996-04-10 14:52:18 +000029OPTIONS_ITEM = 7
30
31# The options dialog. There is a correspondence between
32# the dialog item numbers and the option.
Jack Jansen145c92d1996-10-11 11:30:26 +000033OPT_DIALOG_ID = 510
Jack Jansen6c4e9871996-09-06 22:25:34 +000034# 1 thru 9 are the options
35# The GUSI creator/type and delay-console
36OD_CREATOR_ITEM = 10
37OD_TYPE_ITEM = 11
38OD_DELAYCONSOLE_ITEM = 12
39OD_OK_ITEM = 13
40OD_CANCEL_ITEM = 14
Jack Jansen7571f301995-07-29 13:48:41 +000041
42# Resource IDs in the preferences file
43PATH_STRINGS_ID = 128
44DIRECTORY_ID = 128
Jack Jansendb0bace1996-04-04 15:38:44 +000045OPTIONS_ID = 128
Jack Jansen6c4e9871996-09-06 22:25:34 +000046GUSI_ID = 10240
Jack Jansen7571f301995-07-29 13:48:41 +000047
Jack Jansendb0bace1996-04-04 15:38:44 +000048# Override IDs (in the applet)
49OVERRIDE_PATH_STRINGS_ID = 129
50OVERRIDE_DIRECTORY_ID = 129
51OVERRIDE_OPTIONS_ID = 129
Jack Jansen10496eb1996-09-07 17:01:47 +000052OVERRIDE_GUSI_ID = 10241
Jack Jansen6c4e9871996-09-06 22:25:34 +000053
54# Things we know about the GUSI resource. Note the code knows these too.
55GUSIPOS_TYPE=0
56GUSIPOS_CREATOR=4
57GUSIPOS_SKIP=8
58GUSIPOS_FLAGS=9
Jack Jansen84872291996-10-22 15:33:02 +000059GUSIPOS_VERSION=10
60GUSIVERSION='0181'
61GUSIFLAGS_DELAY=0x20 # Mask
Jack Jansendb0bace1996-04-04 15:38:44 +000062
63READ = 1
Jack Jansen7571f301995-07-29 13:48:41 +000064WRITE = 2
65smAllScripts = -3
66kOnSystemDisk = 0x8000
67
68def restolist(data):
69 """Convert STR# resource data to a list of strings"""
70 if not data:
71 return []
72 num, = struct.unpack('h', data[:2])
73 data = data[2:]
74 rv = []
75 for i in range(num):
76 strlen = ord(data[0])
77 if strlen < 0: strlen = strlen + 256
78 str = data[1:strlen+1]
79 data = data[strlen+1:]
80 rv.append(str)
81 return rv
82
83def listtores(list):
84 """Convert a list of strings to STR# resource data"""
85 rv = struct.pack('h', len(list))
86 for str in list:
87 rv = rv + chr(len(str)) + str
88 return rv
89
90def message(str = "Hello, world!", id = MESSAGE_ID):
91 """Show a simple alert with a text message"""
92 d = GetNewDialog(id, -1)
Jack Jansen822a30b1996-04-10 14:52:18 +000093 d.SetDialogDefaultItem(1)
Jack Jansen7571f301995-07-29 13:48:41 +000094 tp, h, rect = d.GetDialogItem(2)
95 SetDialogItemText(h, str)
96 while 1:
97 n = ModalDialog(None)
98 if n == 1: break
99
Jack Jansen6c4e9871996-09-06 22:25:34 +0000100def optinteract((options, creator, type, delaycons)):
Jack Jansen822a30b1996-04-10 14:52:18 +0000101 """Let the user interact with the options dialog"""
Jack Jansen6c4e9871996-09-06 22:25:34 +0000102 old_options = (options[:], creator, type, delaycons)
Jack Jansen822a30b1996-04-10 14:52:18 +0000103 d = GetNewDialog(OPT_DIALOG_ID, -1)
Jack Jansen6c4e9871996-09-06 22:25:34 +0000104 tp, h, rect = d.GetDialogItem(OD_CREATOR_ITEM)
105 SetDialogItemText(h, creator)
106 tp, h, rect = d.GetDialogItem(OD_TYPE_ITEM)
107 SetDialogItemText(h, type)
Jack Jansen822a30b1996-04-10 14:52:18 +0000108 d.SetDialogDefaultItem(OD_OK_ITEM)
109 d.SetDialogCancelItem(OD_CANCEL_ITEM)
110 while 1:
111 for i in range(len(options)):
112 tp, h, rect = d.GetDialogItem(i+1)
113 h.as_Control().SetControlValue(options[i])
Jack Jansen6c4e9871996-09-06 22:25:34 +0000114 tp, h, rect = d.GetDialogItem(OD_DELAYCONSOLE_ITEM)
115 h.as_Control().SetControlValue(delaycons)
Jack Jansen822a30b1996-04-10 14:52:18 +0000116 n = ModalDialog(None)
117 if n == OD_OK_ITEM:
Jack Jansen6c4e9871996-09-06 22:25:34 +0000118 tp, h, rect = d.GetDialogItem(OD_CREATOR_ITEM)
119 ncreator = GetDialogItemText(h)
120 tp, h, rect = d.GetDialogItem(OD_TYPE_ITEM)
121 ntype = GetDialogItemText(h)
122 if len(ncreator) == 4 and len(ntype) == 4:
123 return options, ncreator, ntype, delaycons
124 else:
125 sys.stderr.write('\007')
Jack Jansen822a30b1996-04-10 14:52:18 +0000126 elif n == OD_CANCEL_ITEM:
127 return old_options
Jack Jansen6c4e9871996-09-06 22:25:34 +0000128 elif n in (OD_CREATOR_ITEM, OD_TYPE_ITEM):
129 pass
130 elif n == OD_DELAYCONSOLE_ITEM:
131 delaycons = (not delaycons)
Jack Jansen822a30b1996-04-10 14:52:18 +0000132 elif 1 <= n <= len(options):
133 options[n-1] = (not options[n-1])
134
135
136def interact(list, pythondir, options, title):
Jack Jansen7571f301995-07-29 13:48:41 +0000137 """Let the user interact with the dialog"""
138 opythondir = pythondir
139 try:
140 # Try to go to the "correct" dir for GetDirectory
141 os.chdir(pythondir.as_pathname())
142 except os.error:
143 pass
144 d = GetNewDialog(DIALOG_ID, -1)
Jack Jansendb0bace1996-04-04 15:38:44 +0000145 tp, h, rect = d.GetDialogItem(TITLE_ITEM)
146 SetDialogItemText(h, title)
147 tp, h, rect = d.GetDialogItem(TEXT_ITEM)
Jack Jansena918b8c1996-11-20 14:55:26 +0000148## SetDialogItemText(h, string.joinfields(list, '\r'))
149 h.data = string.joinfields(list, '\r')
150 d.SelectDialogItemText(TEXT_ITEM, 0, 32767)
151 d.SelectDialogItemText(TEXT_ITEM, 0, 0)
Jack Jansendb0bace1996-04-04 15:38:44 +0000152## d.SetDialogDefaultItem(OK_ITEM)
153 d.SetDialogCancelItem(CANCEL_ITEM)
Jack Jansena918b8c1996-11-20 14:55:26 +0000154 d.GetDialogWindow().ShowWindow()
155 d.DrawDialog()
Jack Jansen7571f301995-07-29 13:48:41 +0000156 while 1:
157 n = ModalDialog(None)
158 if n == OK_ITEM:
159 break
160 if n == CANCEL_ITEM:
161 return None
Jack Jansendb0bace1996-04-04 15:38:44 +0000162## if n == REVERT_ITEM:
163## return [], pythondir
Jack Jansen7571f301995-07-29 13:48:41 +0000164 if n == DIR_ITEM:
Jack Jansen9062fa21995-08-14 12:21:12 +0000165 fss, ok = macfs.GetDirectory('Select python home folder:')
Jack Jansen7571f301995-07-29 13:48:41 +0000166 if ok:
167 pythondir = fss
Jack Jansen822a30b1996-04-10 14:52:18 +0000168 if n == OPTIONS_ITEM:
169 options = optinteract(options)
Jack Jansena918b8c1996-11-20 14:55:26 +0000170 tmp = string.splitfields(h.data, '\r')
Jack Jansen7571f301995-07-29 13:48:41 +0000171 rv = []
172 for i in tmp:
173 if i:
174 rv.append(i)
Jack Jansen822a30b1996-04-10 14:52:18 +0000175 return rv, pythondir, options
Jack Jansen7571f301995-07-29 13:48:41 +0000176
Jack Jansendb0bace1996-04-04 15:38:44 +0000177def getprefpath(id):
178 # Load the path and directory resources
Jack Jansen7571f301995-07-29 13:48:41 +0000179 try:
Jack Jansendb0bace1996-04-04 15:38:44 +0000180 sr = GetResource('STR#', id)
181 except (MacOS.Error, Res.Error):
182 return None, None
183 d = sr.data
184 l = restolist(d)
185 return l, sr
186
187def getprefdir(id):
188 try:
189 dr = GetResource('alis', id)
190 fss, fss_changed = macfs.RawAlias(dr.data).Resolve()
191 except (MacOS.Error, Res.Error):
192 return None, None, 1
193 return fss, dr, fss_changed
Jack Jansen822a30b1996-04-10 14:52:18 +0000194
195def getoptions(id):
196 try:
197 opr = GetResource('Popt', id)
198 except (MacOS.Error, Res.Error):
Jack Jansen10496eb1996-09-07 17:01:47 +0000199 return [0]*9, None
200 options = map(lambda x: ord(x), opr.data)
201 while len(options) < 9:
202 options = options + [0]
203 return options, opr
Jack Jansen7571f301995-07-29 13:48:41 +0000204
Jack Jansen6c4e9871996-09-06 22:25:34 +0000205def getgusioptions(id):
206 try:
207 opr = GetResource('GU\267I', id)
208 except (MacOS.Error, Res.Error):
209 return '????', '????', 0, None
210 data = opr.data
211 type = data[GUSIPOS_TYPE:GUSIPOS_TYPE+4]
212 creator = data[GUSIPOS_CREATOR:GUSIPOS_CREATOR+4]
213 flags = ord(data[GUSIPOS_FLAGS])
Jack Jansen84872291996-10-22 15:33:02 +0000214 version = data[GUSIPOS_VERSION:GUSIPOS_VERSION+4]
215 if version <> GUSIVERSION:
216 message('GU\267I resource version "%s", fixing to "%s"'%(version, GUSIVERSION))
217 flags = 0
Jack Jansen6c4e9871996-09-06 22:25:34 +0000218 delay = (not not (flags & GUSIFLAGS_DELAY))
219 return creator, type, delay, opr
220
221def setgusioptions(opr, creator, type, delay):
222 data = opr.data
223 flags = ord(data[GUSIPOS_FLAGS])
Jack Jansen84872291996-10-22 15:33:02 +0000224 version = data[GUSIPOS_VERSION:GUSIPOS_VERSION+4]
225 if version <> GUSIVERSION:
226 flags = 0x88
227 version = GUSIVERSION
Jack Jansen6c4e9871996-09-06 22:25:34 +0000228 if delay:
229 flags = flags | GUSIFLAGS_DELAY
230 else:
231 flags = flags & ~GUSIFLAGS_DELAY
Jack Jansen84872291996-10-22 15:33:02 +0000232 data = type + creator + data[GUSIPOS_SKIP] + chr(flags) + GUSIVERSION + data[GUSIPOS_VERSION+4:]
Jack Jansen6c4e9871996-09-06 22:25:34 +0000233 return data
234
Jack Jansendb0bace1996-04-04 15:38:44 +0000235def openpreffile(rw):
Jack Jansen7571f301995-07-29 13:48:41 +0000236 # Find the preferences folder and our prefs file, create if needed.
237 vrefnum, dirid = macfs.FindFolder(kOnSystemDisk, 'pref', 0)
Jack Jansen84872291996-10-22 15:33:02 +0000238 try:
239 pnhandle = GetNamedResource('STR ', PREFNAME_NAME)
240 except Res.Error:
241 message("No %s resource (old Python?)"%PREFNAME_NAME)
242 sys.exit(1)
243 prefname = pnhandle.data[1:]
244 preff_fss = macfs.FSSpec((vrefnum, dirid, prefname))
Jack Jansen7571f301995-07-29 13:48:41 +0000245 try:
Jack Jansendb0bace1996-04-04 15:38:44 +0000246 preff_handle = FSpOpenResFile(preff_fss, rw)
Jack Jansen7571f301995-07-29 13:48:41 +0000247 except Res.Error:
248 # Create it
249 message('No preferences file, creating one...')
Jack Jansen532e3c21996-02-21 15:36:26 +0000250 FSpCreateResFile(preff_fss, 'Pyth', 'pref', smAllScripts)
Jack Jansendb0bace1996-04-04 15:38:44 +0000251 preff_handle = FSpOpenResFile(preff_fss, rw)
252 return preff_handle
253
254def openapplet(name):
255 fss = macfs.FSSpec(name)
Jack Jansen7571f301995-07-29 13:48:41 +0000256 try:
Jack Jansendb0bace1996-04-04 15:38:44 +0000257 app_handle = FSpOpenResFile(fss, WRITE)
258 except Res.Error:
259 message('File does not have a resource fork.')
260 sys.exit(0)
261 return app_handle
262
263
264def edit_preferences():
265 preff_handle = openpreffile(WRITE)
266
267 l, sr = getprefpath(PATH_STRINGS_ID)
268 if l == None:
Jack Jansen7571f301995-07-29 13:48:41 +0000269 message('Cannot find any sys.path resource! (Old python?)')
270 sys.exit(0)
Jack Jansendb0bace1996-04-04 15:38:44 +0000271
272 fss, dr, fss_changed = getprefdir(DIRECTORY_ID)
273 if fss == None:
Jack Jansen7571f301995-07-29 13:48:41 +0000274 fss = macfs.FSSpec(os.getcwd())
275 fss_changed = 1
Jack Jansen822a30b1996-04-10 14:52:18 +0000276
277 options, opr = getoptions(OPTIONS_ID)
278 saved_options = options[:]
Jack Jansen7571f301995-07-29 13:48:41 +0000279
Jack Jansen6c4e9871996-09-06 22:25:34 +0000280 creator, type, delaycons, gusi_opr = getgusioptions(GUSI_ID)
281 saved_gusi_options = creator, type, delaycons
282
Jack Jansen7571f301995-07-29 13:48:41 +0000283 # Let the user play away
Jack Jansen6c4e9871996-09-06 22:25:34 +0000284 result = interact(l, fss, (options, creator, type, delaycons),
285 'System-wide preferences')
Jack Jansen7571f301995-07-29 13:48:41 +0000286
287 # See what we have to update, and how
288 if result == None:
289 sys.exit(0)
290
Jack Jansen6c4e9871996-09-06 22:25:34 +0000291 pathlist, nfss, (options, creator, type, delaycons) = result
Jack Jansen7571f301995-07-29 13:48:41 +0000292 if nfss != fss:
293 fss_changed = 1
294
Jack Jansen822a30b1996-04-10 14:52:18 +0000295 if fss_changed:
296 alias = nfss.NewAlias()
297 if dr:
298 dr.data = alias.data
299 dr.ChangedResource()
300 else:
301 dr = Resource(alias.data)
302 dr.AddResource('alis', DIRECTORY_ID, '')
303
304 if pathlist != l:
305 if pathlist == []:
306 if sr.HomeResFile() == preff_handle:
307 sr.RemoveResource()
308 elif sr.HomeResFile() == preff_handle:
309 sr.data = listtores(pathlist)
310 sr.ChangedResource()
311 else:
312 sr = Resource(listtores(pathlist))
313 sr.AddResource('STR#', PATH_STRINGS_ID, '')
314
315 if options != saved_options:
316 newdata = reduce(lambda x, y: x+chr(y), options, '')
317 if opr and opr.HomeResFile() == preff_handle:
318 opr.data = newdata
319 opr.ChangedResource()
320 else:
321 opr = Resource(newdata)
322 opr.AddResource('Popt', OPTIONS_ID, '')
Jack Jansen6c4e9871996-09-06 22:25:34 +0000323
324 if (creator, type, delaycons) != saved_gusi_options:
325 newdata = setgusioptions(gusi_opr, creator, type, delaycons)
326 if gusi_opr.HomeResFile() == preff_handle:
327 gusi_opr.data = newdata
328 gusi_opr.ChangedResource()
329 else:
Jack Jansen10496eb1996-09-07 17:01:47 +0000330 ngusi_opr = Resource(newdata)
Jack Jansen6c4e9871996-09-06 22:25:34 +0000331 ngusi_opr.AddResource('GU\267I', GUSI_ID, '')
Jack Jansen7571f301995-07-29 13:48:41 +0000332
333 CloseResFile(preff_handle)
Jack Jansendb0bace1996-04-04 15:38:44 +0000334
335def edit_applet(name):
336 pref_handle = openpreffile(READ)
337 app_handle = openapplet(name)
338
339 notfound = ''
340 l, sr = getprefpath(OVERRIDE_PATH_STRINGS_ID)
341 if l == None:
342 notfound = 'path'
343
344 l, dummy = getprefpath(PATH_STRINGS_ID)
345 if l == None:
346 message('Cannot find any sys.path resource! (Old python?)')
347 sys.exit(0)
348
349 fss, dr, fss_changed = getprefdir(OVERRIDE_DIRECTORY_ID)
350 if fss == None:
351 if notfound:
Jack Jansen822a30b1996-04-10 14:52:18 +0000352 notfound = notfound + ', directory'
Jack Jansendb0bace1996-04-04 15:38:44 +0000353 else:
354 notfound = 'directory'
355 fss, dummy, dummy2 = getprefdir(DIRECTORY_ID)
356 if fss == None:
357 fss = macfs.FSSpec(os.getcwd())
358 fss_changed = 1
Jack Jansen822a30b1996-04-10 14:52:18 +0000359
360 options, opr = getoptions(OVERRIDE_OPTIONS_ID)
361 if not opr:
362 if notfound:
363 notfound = notfound + ', options'
364 else:
365 notfound = 'options'
366 options, dummy = getoptions(OPTIONS_ID)
367 saved_options = options[:]
Jack Jansendb0bace1996-04-04 15:38:44 +0000368
Jack Jansen6c4e9871996-09-06 22:25:34 +0000369 creator, type, delaycons, gusi_opr = getgusioptions(OVERRIDE_GUSI_ID)
Jack Jansen10496eb1996-09-07 17:01:47 +0000370 if not gusi_opr:
Jack Jansen6c4e9871996-09-06 22:25:34 +0000371 if notfound:
372 notfound = notfound + ', GUSI options'
373 else:
374 notfound = 'GUSI options'
Jack Jansen10496eb1996-09-07 17:01:47 +0000375 creator, type, delaycons, gusi_opr = getgusioptions(GUSI_ID)
Jack Jansen6c4e9871996-09-06 22:25:34 +0000376 saved_gusi_options = creator, type, delaycons
377
Jack Jansendb0bace1996-04-04 15:38:44 +0000378 dummy = dummy2 = None # Discard them.
379
380 if notfound:
381 message('Warning: initial %s taken from system-wide defaults'%notfound)
382 # Let the user play away
Jack Jansen145c92d1996-10-11 11:30:26 +0000383 print 'DBG interaction'
Jack Jansen6c4e9871996-09-06 22:25:34 +0000384 result = interact(l, fss, (options, creator, type, delaycons), name)
Jack Jansendb0bace1996-04-04 15:38:44 +0000385
386 # See what we have to update, and how
387 if result == None:
388 sys.exit(0)
389
Jack Jansen10496eb1996-09-07 17:01:47 +0000390 pathlist, nfss, (options, creator, type, delaycons) = result
Jack Jansendb0bace1996-04-04 15:38:44 +0000391 if nfss != fss:
392 fss_changed = 1
393
Jack Jansen822a30b1996-04-10 14:52:18 +0000394 if fss_changed:
395 alias = nfss.NewAlias()
396 if dr:
397 dr.data = alias.data
398 dr.ChangedResource()
399 else:
400 dr = Resource(alias.data)
401 dr.AddResource('alis', OVERRIDE_DIRECTORY_ID, '')
402
403 if pathlist != l:
404 if pathlist == []:
405 if sr.HomeResFile() == app_handle:
406 sr.RemoveResource()
407 elif sr and sr.HomeResFile() == app_handle:
408 sr.data = listtores(pathlist)
409 sr.ChangedResource()
410 else:
411 sr = Resource(listtores(pathlist))
412 sr.AddResource('STR#', OVERRIDE_PATH_STRINGS_ID, '')
413
414 if options != saved_options:
415 newdata = reduce(lambda x, y: x+chr(y), options, '')
416 if opr and opr.HomeResFile() == app_handle:
417 opr.data = newdata
418 opr.ChangedResource()
419 else:
420 opr = Resource(newdata)
421 opr.AddResource('Popt', OVERRIDE_OPTIONS_ID, '')
Jack Jansen6c4e9871996-09-06 22:25:34 +0000422
423 if (creator, type, delaycons) != saved_gusi_options:
424 newdata = setgusioptions(gusi_opr, creator, type, delaycons)
Jack Jansen10496eb1996-09-07 17:01:47 +0000425 id, type, name = gusi_opr.GetResInfo()
426 if gusi_opr.HomeResFile() == app_handle and id == OVERRIDE_GUSI_ID:
Jack Jansen6c4e9871996-09-06 22:25:34 +0000427 gusi_opr.data = newdata
428 gusi_opr.ChangedResource()
429 else:
Jack Jansen10496eb1996-09-07 17:01:47 +0000430 ngusi_opr = Resource(newdata)
431 ngusi_opr.AddResource('GU\267I', OVERRIDE_GUSI_ID, '')
Jack Jansen6c4e9871996-09-06 22:25:34 +0000432
Jack Jansendb0bace1996-04-04 15:38:44 +0000433 CloseResFile(app_handle)
434
435def main():
436 try:
437 h = OpenResFile('EditPythonPrefs.rsrc')
438 except Res.Error:
439 pass # Assume we already have acces to our own resource
440
441 if len(sys.argv) <= 1:
442 edit_preferences()
443 else:
444 for appl in sys.argv[1:]:
445 edit_applet(appl)
446
Jack Jansen7571f301995-07-29 13:48:41 +0000447
448if __name__ == '__main__':
Jack Jansen7571f301995-07-29 13:48:41 +0000449 main()