blob: 53f9f5dae0819e519f117967b99566cdc3978c92 [file] [log] [blame]
Jack Jansen17720092001-07-14 14:02:21 +00001# A minimal text editor using MLTE. Based on wed.py.
2#
3# To be done:
4# - Functionality: find, etc.
5
6from Menu import DrawMenuBar
7from FrameWork import *
Jack Jansen5a6fdcd2001-08-25 12:15:04 +00008from Carbon import Win
9from Carbon import Ctl
10from Carbon import Qd
11from Carbon import Res
12from Carbon import Scrap
Jack Jansen17720092001-07-14 14:02:21 +000013import os
Jack Jansen5a6fdcd2001-08-25 12:15:04 +000014from Carbon import MacTextEditor
15from Carbon import Mlte
Jack Jansen17720092001-07-14 14:02:21 +000016
17UNDOLABELS = [ # Indexed by MLTECanUndo() value
18 "Typing", "Cut", "Paste", "Clear", "Font Change", "Color Change", "Size Change",
19 "Style Change", "Align Left", "Align Center", "Align Right", "Drop", "Move"]
20
Jack Jansene160fe12001-07-16 21:57:50 +000021class MlteWindow(Window):
Jack Jansen17720092001-07-14 14:02:21 +000022 def open(self, path, name, data):
23 self.path = path
24 self.name = name
25 r = windowbounds(400, 400)
Jack Jansencbed91b2001-08-03 13:31:36 +000026 w = Win.NewWindow(r, name, 1, 0, -1, 1, 0)
Jack Jansen17720092001-07-14 14:02:21 +000027 self.wid = w
Jack Jansene160fe12001-07-16 21:57:50 +000028 flags = MacTextEditor.kTXNDrawGrowIconMask|MacTextEditor.kTXNWantHScrollBarMask| \
Jack Jansen17720092001-07-14 14:02:21 +000029 MacTextEditor.kTXNWantVScrollBarMask
30 self.ted, self.frameid = Mlte.TXNNewObject(None, w, None, flags, MacTextEditor.kTXNTextEditStyleFrameType,
31 MacTextEditor.kTXNTextFile, MacTextEditor.kTXNMacOSEncoding)
Jack Jansen17720092001-07-14 14:02:21 +000032 self.ted.TXNSetData(MacTextEditor.kTXNTextData, data, 0, 0x7fffffff)
Jack Jansen17720092001-07-14 14:02:21 +000033 self.changed = 0
34 self.do_postopen()
35 self.do_activate(1, None)
36
37 def do_idle(self, event):
Jack Jansen17720092001-07-14 14:02:21 +000038 self.ted.TXNIdle()
39 self.ted.TXNAdjustCursor(None)
40
Jack Jansen17720092001-07-14 14:02:21 +000041
42
43 def do_activate(self, onoff, evt):
Jack Jansen17720092001-07-14 14:02:21 +000044 if onoff:
Jack Jansene160fe12001-07-16 21:57:50 +000045## self.ted.TXNActivate(self.frameid, 0)
Jack Jansen17720092001-07-14 14:02:21 +000046 self.ted.TXNFocus(1)
47 self.parent.active = self
Jack Jansen17720092001-07-14 14:02:21 +000048 else:
49 self.ted.TXNFocus(0)
Jack Jansene160fe12001-07-16 21:57:50 +000050 self.parent.active = None
Jack Jansen17720092001-07-14 14:02:21 +000051 self.parent.updatemenubar()
52
Jack Jansene160fe12001-07-16 21:57:50 +000053 def do_update(self, wid, event):
54 self.ted.TXNDraw(None)
55
56 def do_postresize(self, width, height, window):
57 self.ted.TXNResizeFrame(width, height, self.frameid)
58
59 def do_contentclick(self, local, modifiers, evt):
60 self.ted.TXNClick(evt)
61 self.parent.updatemenubar()
62
Jack Jansen17720092001-07-14 14:02:21 +000063 def do_char(self, ch, event):
64 self.ted.TXNKeyDown(event)
Jack Jansene160fe12001-07-16 21:57:50 +000065 self.parent.updatemenubar()
Jack Jansen17720092001-07-14 14:02:21 +000066
67 def close(self):
68 if self.changed:
69 save = EasyDialogs.AskYesNoCancel('Save window "%s" before closing?'%self.name, 1)
70 if save > 0:
71 self.menu_save()
72 elif save < 0:
73 return
74 if self.parent.active == self:
75 self.parent.active = None
Jack Jansene160fe12001-07-16 21:57:50 +000076 self.ted.TXNDeleteObject()
Jack Jansen17720092001-07-14 14:02:21 +000077 del self.ted
78## del self.tedtexthandle
79 self.do_postclose()
80
81 def menu_save(self):
82 if not self.path:
83 self.menu_save_as()
84 return # Will call us recursively
Jack Jansen17720092001-07-14 14:02:21 +000085 dhandle = self.ted.TXNGetData(0, 0x7fffffff)
86 data = dhandle.data
87 fp = open(self.path, 'wb') # NOTE: wb, because data has CR for end-of-line
88 fp.write(data)
89 if data[-1] <> '\r': fp.write('\r')
90 fp.close()
91 self.changed = 0
92
93 def menu_save_as(self):
Jack Jansenb340acf2003-01-26 21:40:00 +000094 path = EasyDialogs.AskFileForSave(message='Save as:')
95 if not path: return
96 self.path = path
Jack Jansen17720092001-07-14 14:02:21 +000097 self.name = os.path.split(self.path)[-1]
98 self.wid.SetWTitle(self.name)
99 self.menu_save()
100
101 def menu_cut(self):
102## self.ted.WESelView()
103 self.ted.TXNCut()
104### Mlte.ConvertToPublicScrap()
105## Scrap.ZeroScrap()
106## self.ted.WECut()
107## self.updatescrollbars()
108 self.parent.updatemenubar()
109 self.changed = 1
110
111 def menu_copy(self):
112## Scrap.ZeroScrap()
113 self.ted.TXNCopy()
114### Mlte.ConvertToPublicScrap()
115## self.updatescrollbars()
116 self.parent.updatemenubar()
117
118 def menu_paste(self):
119### Mlte.ConvertFromPublicScrap()
120 self.ted.TXNPaste()
121## self.updatescrollbars()
122 self.parent.updatemenubar()
123 self.changed = 1
124
125 def menu_clear(self):
126## self.ted.WESelView()
127 self.ted.TXNClear()
128## self.updatescrollbars()
129 self.parent.updatemenubar()
130 self.changed = 1
131
132 def menu_undo(self):
133 self.ted.TXNUndo()
134## self.updatescrollbars()
135 self.parent.updatemenubar()
136
137 def menu_redo(self):
138 self.ted.TXNRedo()
139## self.updatescrollbars()
140 self.parent.updatemenubar()
141
142 def have_selection(self):
143 start, stop = self.ted.TXNGetSelection()
144 return start < stop
145
146 def can_paste(self):
147 return Mlte.TXNIsScrapPastable()
148
149 def can_undo(self):
150 can, which = self.ted.TXNCanUndo()
151 if not can:
152 return None
153 if which >= len(UNDOLABELS):
154 # Unspecified undo
155 return "Undo"
156 which = UNDOLABELS[which]
157
158 return "Undo "+which
159
160 def can_redo(self):
161 can, which = self.ted.TXNCanRedo()
162 if not can:
163 return None
164 if which >= len(UNDOLABELS):
165 # Unspecified undo
166 return "Redo"
167 which = UNDOLABELS[which]
168
169 return "Redo "+which
170
171class Mlted(Application):
172 def __init__(self):
173 Application.__init__(self)
174 self.num = 0
175 self.active = None
176 self.updatemenubar()
177
178 def makeusermenus(self):
179 self.filemenu = m = Menu(self.menubar, "File")
180 self.newitem = MenuItem(m, "New window", "N", self.open)
181 self.openitem = MenuItem(m, "Open...", "O", self.openfile)
182 self.closeitem = MenuItem(m, "Close", "W", self.closewin)
183 m.addseparator()
184 self.saveitem = MenuItem(m, "Save", "S", self.save)
185 self.saveasitem = MenuItem(m, "Save as...", "", self.saveas)
186 m.addseparator()
187 self.quititem = MenuItem(m, "Quit", "Q", self.quit)
188
189 self.editmenu = m = Menu(self.menubar, "Edit")
190 self.undoitem = MenuItem(m, "Undo", "Z", self.undo)
Jack Jansene160fe12001-07-16 21:57:50 +0000191 self.redoitem = MenuItem(m, "Redo", None, self.redo)
192 m.addseparator()
Jack Jansen17720092001-07-14 14:02:21 +0000193 self.cutitem = MenuItem(m, "Cut", "X", self.cut)
194 self.copyitem = MenuItem(m, "Copy", "C", self.copy)
195 self.pasteitem = MenuItem(m, "Paste", "V", self.paste)
196 self.clearitem = MenuItem(m, "Clear", "", self.clear)
197
198 # Groups of items enabled together:
199 self.windowgroup = [self.closeitem, self.saveitem, self.saveasitem, self.editmenu]
200 self.focusgroup = [self.cutitem, self.copyitem, self.clearitem]
201 self.windowgroup_on = -1
202 self.focusgroup_on = -1
203 self.pastegroup_on = -1
204 self.undo_label = "never"
205 self.redo_label = "never"
206
207 def updatemenubar(self):
208 changed = 0
209 on = (self.active <> None)
210 if on <> self.windowgroup_on:
211 for m in self.windowgroup:
212 m.enable(on)
213 self.windowgroup_on = on
214 changed = 1
215 if on:
216 # only if we have an edit menu
217 on = self.active.have_selection()
218 if on <> self.focusgroup_on:
219 for m in self.focusgroup:
220 m.enable(on)
221 self.focusgroup_on = on
222 changed = 1
223 on = self.active.can_paste()
224 if on <> self.pastegroup_on:
225 self.pasteitem.enable(on)
226 self.pastegroup_on = on
227 changed = 1
228 on = self.active.can_undo()
229 if on <> self.undo_label:
230 if on:
231 self.undoitem.enable(1)
232 self.undoitem.settext(on)
233 self.undo_label = on
234 else:
235 self.undoitem.settext("Nothing to undo")
236 self.undoitem.enable(0)
237 changed = 1
238 on = self.active.can_redo()
239 if on <> self.redo_label:
240 if on:
241 self.redoitem.enable(1)
242 self.redoitem.settext(on)
243 self.redo_label = on
244 else:
245 self.redoitem.settext("Nothing to redo")
246 self.redoitem.enable(0)
247 changed = 1
248 if changed:
249 DrawMenuBar()
250
251 #
252 # Apple menu
253 #
254
255 def do_about(self, id, item, window, event):
256 EasyDialogs.Message("A simple single-font text editor based on MacTextEditor")
257
258 #
259 # File menu
260 #
261
262 def open(self, *args):
263 self._open(0)
264
265 def openfile(self, *args):
266 self._open(1)
267
268 def _open(self, askfile):
269 if askfile:
Jack Jansenb340acf2003-01-26 21:40:00 +0000270 path = EasyDialogs.AskFileForOpen(typeList=('TEXT',))
271 if not path:
Jack Jansen17720092001-07-14 14:02:21 +0000272 return
Jack Jansen17720092001-07-14 14:02:21 +0000273 name = os.path.split(path)[-1]
274 try:
275 fp = open(path, 'rb') # NOTE binary, we need cr as end-of-line
276 data = fp.read()
277 fp.close()
278 except IOError, arg:
279 EasyDialogs.Message("IOERROR: "+`arg`)
280 return
281 else:
282 path = None
283 name = "Untitled %d"%self.num
284 data = ''
Jack Jansene160fe12001-07-16 21:57:50 +0000285 w = MlteWindow(self)
Jack Jansen17720092001-07-14 14:02:21 +0000286 w.open(path, name, data)
287 self.num = self.num + 1
288
289 def closewin(self, *args):
290 if self.active:
291 self.active.close()
292 else:
293 EasyDialogs.Message("No active window?")
294
295 def save(self, *args):
296 if self.active:
297 self.active.menu_save()
298 else:
299 EasyDialogs.Message("No active window?")
300
301 def saveas(self, *args):
302 if self.active:
303 self.active.menu_save_as()
304 else:
305 EasyDialogs.Message("No active window?")
306
307
308 def quit(self, *args):
309 for w in self._windows.values():
310 w.close()
311 if self._windows:
312 return
313 self._quit()
314
315 #
316 # Edit menu
317 #
318
319 def undo(self, *args):
320 if self.active:
321 self.active.menu_undo()
322 else:
323 EasyDialogs.Message("No active window?")
324
325 def redo(self, *args):
326 if self.active:
327 self.active.menu_redo()
328 else:
329 EasyDialogs.Message("No active window?")
330
331 def cut(self, *args):
332 if self.active:
333 self.active.menu_cut()
334 else:
335 EasyDialogs.Message("No active window?")
336
337 def copy(self, *args):
338 if self.active:
339 self.active.menu_copy()
340 else:
341 EasyDialogs.Message("No active window?")
342
343 def paste(self, *args):
344 if self.active:
345 self.active.menu_paste()
346 else:
347 EasyDialogs.Message("No active window?")
348
349 def clear(self, *args):
350 if self.active:
351 self.active.menu_clear()
352 else:
353 EasyDialogs.Message("No active window?")
354
355 #
356 # Other stuff
357 #
358
359 def idle(self, event):
360 if self.active:
361 self.active.do_idle(event)
Jack Jansencbed91b2001-08-03 13:31:36 +0000362 else:
Jack Jansen362c7cd02002-11-30 00:01:29 +0000363 Qd.SetCursor(Qd.GetQDGlobalsArrow())
Jack Jansen17720092001-07-14 14:02:21 +0000364
365def main():
366 Mlte.TXNInitTextension(0)
367 try:
368 App = Mlted()
369 App.mainloop()
370 finally:
371 Mlte.TXNTerminateTextension()
372
373if __name__ == '__main__':
374 main()
375